blob: df31f4d4fb7466f2db6d08cc31e2709485039271 [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*/
44#include "magick/studio.h"
glennrp5c7cf4e2010-12-24 00:30:00 +000045#include "magick/artifact.h"
cristy5a2ca482009-10-14 18:24:56 +000046#include "magick/attribute.h"
cristy3ed852e2009-09-05 21:47:34 +000047#include "magick/blob.h"
48#include "magick/blob-private.h"
49#include "magick/cache.h"
50#include "magick/color.h"
51#include "magick/color-private.h"
cristy4ccd4c02010-04-25 00:43:15 +000052#include "magick/colormap.h"
cristy3ed852e2009-09-05 21:47:34 +000053#include "magick/colorspace.h"
54#include "magick/constitute.h"
55#include "magick/enhance.h"
56#include "magick/exception.h"
57#include "magick/exception-private.h"
58#include "magick/geometry.h"
cristyf2e11662009-10-14 01:24:43 +000059#include "magick/histogram.h"
cristy3ed852e2009-09-05 21:47:34 +000060#include "magick/image.h"
61#include "magick/image-private.h"
62#include "magick/layer.h"
63#include "magick/list.h"
64#include "magick/log.h"
65#include "magick/magick.h"
66#include "magick/memory_.h"
67#include "magick/module.h"
68#include "magick/monitor.h"
69#include "magick/monitor-private.h"
70#include "magick/option.h"
71#include "magick/quantum-private.h"
72#include "magick/profile.h"
73#include "magick/property.h"
cristy3ed852e2009-09-05 21:47:34 +000074#include "magick/resource_.h"
75#include "magick/semaphore.h"
76#include "magick/quantum-private.h"
77#include "magick/static.h"
78#include "magick/statistic.h"
79#include "magick/string_.h"
cristyf2f27272009-12-17 14:48:46 +000080#include "magick/string-private.h"
cristy3ed852e2009-09-05 21:47:34 +000081#include "magick/transform.h"
82#include "magick/utility.h"
83#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000084
glennrp7ef138c2009-11-10 13:50:20 +000085/* Suppress libpng pedantic warnings that were added in
86 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000087 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000088 * fix any code that generates warnings.
89 */
glennrp991e92a2010-01-28 03:09:00 +000090/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000091/* #define PNG_USE_RESULT The result of this function must be checked */
92/* #define PNG_NORETURN This function does not return */
93/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000094/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000095
96/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +000097#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +000098
cristy3ed852e2009-09-05 21:47:34 +000099#include "png.h"
100#include "zlib.h"
101
102/* ImageMagick differences */
103#define first_scene scene
104
glennrpd5045b42010-03-24 12:40:35 +0000105#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000106/*
107 Optional declarations. Define or undefine them as you like.
108*/
109/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
110
111/*
112 Features under construction. Define these to work on them.
113*/
114#undef MNG_OBJECT_BUFFERS
115#undef MNG_BASI_SUPPORTED
116#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
117#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000118#if defined(MAGICKCORE_JPEG_DELEGATE)
119# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
120#endif
121#if !defined(RGBColorMatchExact)
122#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000123 (((color).red == (target).red) && \
124 ((color).green == (target).green) && \
125 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000126#endif
127
glennrp8e58efd2011-05-20 12:16:29 +0000128/* Macros for left-bit-replication to ensure that pixels
129 * and PixelPackets all have the image->depth, and for use
130 * in PNG8 quantization.
131 */
132
133
134/* LBR01: Replicate top bit */
135
glennrp91d99252011-06-25 14:30:13 +0000136#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000137 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
138 0 : QuantumRange);
139
glennrp91d99252011-06-25 14:30:13 +0000140#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000141 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
142 0 : QuantumRange);
143
glennrp91d99252011-06-25 14:30:13 +0000144#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000145 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
146 0 : QuantumRange);
147
glennrp91d99252011-06-25 14:30:13 +0000148#define LBR01PacketOpacity(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000149 (pixelpacket).opacity=(ScaleQuantumToChar((pixelpacket).opacity) < 0x10 ? \
150 0 : QuantumRange);
151
glennrp91d99252011-06-25 14:30:13 +0000152#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000153 { \
glennrp91d99252011-06-25 14:30:13 +0000154 LBR01PacketRed((pixelpacket)); \
155 LBR01PacketGreen((pixelpacket)); \
156 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000157 }
glennrp8e58efd2011-05-20 12:16:29 +0000158
glennrp91d99252011-06-25 14:30:13 +0000159#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 { \
glennrp91d99252011-06-25 14:30:13 +0000161 LBR01PacketRGB((pixelpacket)); \
162 LBR01PacketOpacity((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000163 }
glennrp8e58efd2011-05-20 12:16:29 +0000164
cristyef618312011-06-25 12:26:44 +0000165#define LBR01PixelRed(pixel) \
166 (ScaleQuantumToChar(GetPixelRed((pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000167 0 : QuantumRange);
168
cristyef618312011-06-25 12:26:44 +0000169#define LBR01PixelGreen(pixel) \
170 (ScaleQuantumToChar(GetPixelGreen((pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000171 0 : QuantumRange);
172
cristyef618312011-06-25 12:26:44 +0000173#define LBR01PixelBlue(pixel) \
174 (ScaleQuantumToChar(GetPixelBlue((pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000175 0 : QuantumRange);
176
cristyef618312011-06-25 12:26:44 +0000177#define LBR01PixelOpacity(pixel) \
178 (ScaleQuantumToChar(GetPixelOpacity((pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000179 0 : QuantumRange);
180
cristyef618312011-06-25 12:26:44 +0000181#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000182 { \
cristyef618312011-06-25 12:26:44 +0000183 LBR01PixelRed((pixel)); \
184 LBR01PixelGreen((pixel)); \
185 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000186 }
glennrp8e58efd2011-05-20 12:16:29 +0000187
cristyef618312011-06-25 12:26:44 +0000188#define LBR01PixelRGBO(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000189 { \
cristyef618312011-06-25 12:26:44 +0000190 LBR01PixelRGB((pixel)); \
191 LBR01PixelOpacity((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000192 }
glennrp8e58efd2011-05-20 12:16:29 +0000193
194/* LBR02: Replicate top 2 bits */
195
glennrp91d99252011-06-25 14:30:13 +0000196#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000197 { \
198 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
199 (pixelpacket).red=ScaleCharToQuantum( \
200 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
201 }
glennrp91d99252011-06-25 14:30:13 +0000202#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000203 { \
204 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
205 (pixelpacket).green=ScaleCharToQuantum( \
206 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
207 }
glennrp91d99252011-06-25 14:30:13 +0000208#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000209 { \
210 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
211 (pixelpacket).blue=ScaleCharToQuantum( \
212 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
213 }
glennrp91d99252011-06-25 14:30:13 +0000214#define LBR02PacketOpacity(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000215 { \
216 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).opacity) & 0xc0; \
217 (pixelpacket).opacity=ScaleCharToQuantum( \
218 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
219 }
220
glennrp91d99252011-06-25 14:30:13 +0000221#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000222 { \
glennrp91d99252011-06-25 14:30:13 +0000223 LBR02PacketRed((pixelpacket)); \
224 LBR02PacketGreen((pixelpacket)); \
225 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000226 }
glennrp8e58efd2011-05-20 12:16:29 +0000227
glennrp91d99252011-06-25 14:30:13 +0000228#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000229 { \
glennrp91d99252011-06-25 14:30:13 +0000230 LBR02PacketRGB((pixelpacket)); \
231 LBR02PacketOpacity((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000232 }
glennrp8e58efd2011-05-20 12:16:29 +0000233
cristyef618312011-06-25 12:26:44 +0000234#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000235 { \
cristyef618312011-06-25 12:26:44 +0000236 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed((pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000237 & 0xc0; \
cristyef618312011-06-25 12:26:44 +0000238 SetPixelRed((pixel), ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000239 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6)))); \
240 }
cristyef618312011-06-25 12:26:44 +0000241#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000242 { \
cristyef618312011-06-25 12:26:44 +0000243 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen((pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000244 & 0xc0; \
cristyef618312011-06-25 12:26:44 +0000245 SetPixelGreen((pixel), ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000246 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6)))); \
247 }
cristyef618312011-06-25 12:26:44 +0000248#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000249 { \
250 unsigned char lbr_bits= \
cristyef618312011-06-25 12:26:44 +0000251 ScaleQuantumToChar(GetPixelBlue((pixel))) & 0xc0; \
252 SetPixelBlue((pixel), ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000253 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6)))); \
254 }
cristyef618312011-06-25 12:26:44 +0000255#define LBR02PixelOpacity(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000256 { \
257 unsigned char lbr_bits= \
cristyef618312011-06-25 12:26:44 +0000258 ScaleQuantumToChar(GetPixelOpacity((pixel))) & 0xc0; \
259 SetPixelOpacity((pixel), ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000260 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6)))); \
261 }
262
cristyef618312011-06-25 12:26:44 +0000263#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000264 { \
cristyef618312011-06-25 12:26:44 +0000265 LBR02PixelRed((pixel)); \
266 LBR02PixelGreen((pixel)); \
267 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000268 }
glennrp8e58efd2011-05-20 12:16:29 +0000269
cristyef618312011-06-25 12:26:44 +0000270#define LBR02PixelRGBO(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000271 { \
cristyef618312011-06-25 12:26:44 +0000272 LBR02PixelRGB((pixel)); \
273 LBR02PixelOpacity((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000274 }
glennrp8e58efd2011-05-20 12:16:29 +0000275
276/* LBR03: Replicate top 3 bits (only used with opaque pixels during
277 PNG8 quantization) */
278
glennrp91d99252011-06-25 14:30:13 +0000279#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000280 { \
281 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
282 (pixelpacket).red=ScaleCharToQuantum( \
283 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
284 }
glennrp91d99252011-06-25 14:30:13 +0000285#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000286 { \
287 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
288 (pixelpacket).green=ScaleCharToQuantum( \
289 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
290 }
glennrp91d99252011-06-25 14:30:13 +0000291#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000292 { \
293 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
294 (pixelpacket).blue=ScaleCharToQuantum( \
295 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
296 }
297
glennrp91d99252011-06-25 14:30:13 +0000298#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000299 { \
glennrp91d99252011-06-25 14:30:13 +0000300 LBR03PacketRed((pixelpacket)); \
301 LBR03PacketGreen((pixelpacket)); \
302 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000303 }
glennrp8e58efd2011-05-20 12:16:29 +0000304
cristyef618312011-06-25 12:26:44 +0000305#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000306 { \
cristyef618312011-06-25 12:26:44 +0000307 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed((pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000308 & 0xe0; \
cristyef618312011-06-25 12:26:44 +0000309 SetPixelRed((pixel), ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000310 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6)))); \
311 }
cristyef618312011-06-25 12:26:44 +0000312#define LBR03PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000313 { \
cristyef618312011-06-25 12:26:44 +0000314 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen((pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000315 & 0xe0; \
cristyef618312011-06-25 12:26:44 +0000316 SetPixelGreen((pixel), ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6)))); \
318 }
cristyef618312011-06-25 12:26:44 +0000319#define LBR03PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000320 { \
cristyef618312011-06-25 12:26:44 +0000321 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue((pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000322 & 0xe0; \
cristyef618312011-06-25 12:26:44 +0000323 SetPixelBlue((pixel), ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6)))); \
325 }
326
cristyef618312011-06-25 12:26:44 +0000327#define LBR03PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000328 { \
cristyef618312011-06-25 12:26:44 +0000329 LBR03PixelRed((pixel)); \
330 LBR03PixelGreen((pixel)); \
331 LBR03PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000332 }
glennrp8e58efd2011-05-20 12:16:29 +0000333
334/* LBR04: Replicate top 4 bits */
335
glennrp91d99252011-06-25 14:30:13 +0000336#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000337 { \
338 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
339 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
340 }
glennrp91d99252011-06-25 14:30:13 +0000341#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000342 { \
343 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
344 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
345 }
glennrp91d99252011-06-25 14:30:13 +0000346#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000347 { \
348 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
349 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
350 }
glennrp91d99252011-06-25 14:30:13 +0000351#define LBR04PacketOpacity(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000352 { \
353 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).opacity) & 0xf0; \
354 (pixelpacket).opacity=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
355 }
356
glennrp91d99252011-06-25 14:30:13 +0000357#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000358 { \
glennrp91d99252011-06-25 14:30:13 +0000359 LBR04PacketRed((pixelpacket)); \
360 LBR04PacketGreen((pixelpacket)); \
361 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000362 }
glennrp8e58efd2011-05-20 12:16:29 +0000363
glennrp91d99252011-06-25 14:30:13 +0000364#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000365 { \
glennrp91d99252011-06-25 14:30:13 +0000366 LBR04PacketRGB((pixelpacket)); \
367 LBR04PacketOpacity((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000368 }
glennrp8e58efd2011-05-20 12:16:29 +0000369
cristyef618312011-06-25 12:26:44 +0000370#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000371 { \
cristyef618312011-06-25 12:26:44 +0000372 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed((pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000373 & 0xf0; \
cristyef618312011-06-25 12:26:44 +0000374 SetPixelRed((pixel),\
glennrp8e58efd2011-05-20 12:16:29 +0000375 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4)))); \
376 }
cristyef618312011-06-25 12:26:44 +0000377#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000378 { \
cristyef618312011-06-25 12:26:44 +0000379 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen((pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000380 & 0xf0; \
cristyef618312011-06-25 12:26:44 +0000381 SetPixelGreen((pixel),\
glennrp8e58efd2011-05-20 12:16:29 +0000382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4)))); \
383 }
cristyef618312011-06-25 12:26:44 +0000384#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000385 { \
386 unsigned char lbr_bits= \
cristyef618312011-06-25 12:26:44 +0000387 ScaleQuantumToChar(GetPixelBlue((pixel))) & 0xf0; \
388 SetPixelBlue((pixel),\
glennrp8e58efd2011-05-20 12:16:29 +0000389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4)))); \
390 }
cristyef618312011-06-25 12:26:44 +0000391#define LBR04PixelOpacity(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000392 { \
393 unsigned char lbr_bits= \
cristyef618312011-06-25 12:26:44 +0000394 ScaleQuantumToChar(GetPixelOpacity((pixel))) & 0xf0; \
395 SetPixelOpacity((pixel),\
glennrp8e58efd2011-05-20 12:16:29 +0000396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4)))); \
397 }
398
cristyef618312011-06-25 12:26:44 +0000399#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000400 { \
cristyef618312011-06-25 12:26:44 +0000401 LBR04PixelRed((pixel)); \
402 LBR04PixelGreen((pixel)); \
403 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000404 }
glennrp8e58efd2011-05-20 12:16:29 +0000405
cristyef618312011-06-25 12:26:44 +0000406#define LBR04PixelRGBO(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000407 { \
cristyef618312011-06-25 12:26:44 +0000408 LBR04PixelRGB((pixel)); \
409 LBR04PixelOpacity((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000410 }
glennrp8e58efd2011-05-20 12:16:29 +0000411
412
413/* LBR08: Replicate top 8 bits */
414
glennrp91d99252011-06-25 14:30:13 +0000415#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000416 { \
417 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
418 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
419 }
glennrp91d99252011-06-25 14:30:13 +0000420#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000421 { \
422 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
423 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
424 }
glennrp91d99252011-06-25 14:30:13 +0000425#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000426 { \
427 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
428 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
429 }
glennrp91d99252011-06-25 14:30:13 +0000430#define LBR08PacketOpacity(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000431 { \
432 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).opacity); \
433 (pixelpacket).opacity=ScaleCharToQuantum((lbr_bits)); \
434 }
435
glennrp91d99252011-06-25 14:30:13 +0000436#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000437 { \
glennrp91d99252011-06-25 14:30:13 +0000438 LBR08PacketRed((pixelpacket)); \
439 LBR08PacketGreen((pixelpacket)); \
440 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000441 }
glennrp8e58efd2011-05-20 12:16:29 +0000442
glennrp91d99252011-06-25 14:30:13 +0000443#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 { \
glennrp91d99252011-06-25 14:30:13 +0000445 LBR08PacketRGB((pixelpacket)); \
446 LBR08PacketOpacity((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000447 }
glennrp8e58efd2011-05-20 12:16:29 +0000448
cristyef618312011-06-25 12:26:44 +0000449#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000450 { \
451 unsigned char lbr_bits= \
cristyef618312011-06-25 12:26:44 +0000452 ScaleQuantumToChar(GetPixelRed((pixel))); \
453 SetPixelRed((pixel),\
glennrp8e58efd2011-05-20 12:16:29 +0000454 ScaleCharToQuantum((lbr_bits))); \
455 }
cristyef618312011-06-25 12:26:44 +0000456#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000457 { \
458 unsigned char lbr_bits= \
cristyef618312011-06-25 12:26:44 +0000459 ScaleQuantumToChar(GetPixelGreen((pixel))); \
460 SetPixelGreen((pixel),\
glennrp8e58efd2011-05-20 12:16:29 +0000461 ScaleCharToQuantum((lbr_bits))); \
462 }
cristyef618312011-06-25 12:26:44 +0000463#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000464 { \
465 unsigned char lbr_bits= \
cristyef618312011-06-25 12:26:44 +0000466 ScaleQuantumToChar(GetPixelBlue((pixel))); \
467 SetPixelBlue((pixel),\
glennrp8e58efd2011-05-20 12:16:29 +0000468 ScaleCharToQuantum((lbr_bits))); \
469 }
cristyef618312011-06-25 12:26:44 +0000470#define LBR08PixelOpacity(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000471 { \
472 unsigned char lbr_bits= \
cristyef618312011-06-25 12:26:44 +0000473 ScaleQuantumToChar(GetPixelOpacity((pixel))); \
474 SetPixelOpacity((pixel),\
glennrp8e58efd2011-05-20 12:16:29 +0000475 ScaleCharToQuantum((lbr_bits))); \
476 }
477
cristyef618312011-06-25 12:26:44 +0000478#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000479 { \
cristyef618312011-06-25 12:26:44 +0000480 LBR08PixelRed((pixel)); \
481 LBR08PixelGreen((pixel)); \
482 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000483 }
glennrp8e58efd2011-05-20 12:16:29 +0000484
cristyef618312011-06-25 12:26:44 +0000485#define LBR08PixelRGBO(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000486 { \
cristyef618312011-06-25 12:26:44 +0000487 LBR08PixelRGB((pixel)); \
488 LBR08PixelOpacity((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000489 }
glennrp8e58efd2011-05-20 12:16:29 +0000490
491
492/* LBR16: Replicate top 16 bits */
493
glennrp91d99252011-06-25 14:30:13 +0000494#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000495 { \
496 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
497 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
498 }
glennrp91d99252011-06-25 14:30:13 +0000499#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000500 { \
501 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
502 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
503 }
glennrp91d99252011-06-25 14:30:13 +0000504#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000505 { \
506 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
507 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
508 }
glennrp91d99252011-06-25 14:30:13 +0000509#define LBR16PacketOpacity(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000510 { \
511 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).opacity); \
512 (pixelpacket).opacity=ScaleShortToQuantum((lbr_bits)); \
513 }
514
glennrp91d99252011-06-25 14:30:13 +0000515#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000516 { \
glennrp91d99252011-06-25 14:30:13 +0000517 LBR16PacketRed((pixelpacket)); \
518 LBR16PacketGreen((pixelpacket)); \
519 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000520 }
glennrp8e58efd2011-05-20 12:16:29 +0000521
glennrp91d99252011-06-25 14:30:13 +0000522#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 { \
glennrp91d99252011-06-25 14:30:13 +0000524 LBR16PacketRGB((pixelpacket)); \
525 LBR16PacketOpacity((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000526 }
glennrp8e58efd2011-05-20 12:16:29 +0000527
cristyef618312011-06-25 12:26:44 +0000528#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000529 { \
530 unsigned short lbr_bits= \
cristyef618312011-06-25 12:26:44 +0000531 ScaleQuantumToShort(GetPixelRed((pixel))); \
532 SetPixelRed((pixel),\
glennrp8e58efd2011-05-20 12:16:29 +0000533 ScaleShortToQuantum((lbr_bits))); \
534 }
cristyef618312011-06-25 12:26:44 +0000535#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000536 { \
537 unsigned short lbr_bits= \
cristyef618312011-06-25 12:26:44 +0000538 ScaleQuantumToShort(GetPixelGreen((pixel))); \
539 SetPixelGreen((pixel),\
glennrp8e58efd2011-05-20 12:16:29 +0000540 ScaleShortToQuantum((lbr_bits))); \
541 }
cristyef618312011-06-25 12:26:44 +0000542#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000543 { \
544 unsigned short lbr_bits= \
cristyef618312011-06-25 12:26:44 +0000545 ScaleQuantumToShort(GetPixelBlue((pixel))); \
546 SetPixelBlue((pixel),\
glennrp8e58efd2011-05-20 12:16:29 +0000547 ScaleShortToQuantum((lbr_bits))); \
548 }
cristyef618312011-06-25 12:26:44 +0000549#define LBR16PixelOpacity(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000550 { \
551 unsigned short lbr_bits= \
cristyef618312011-06-25 12:26:44 +0000552 ScaleQuantumToShort(GetPixelOpacity((pixel))); \
553 SetPixelOpacity((pixel),\
glennrp8e58efd2011-05-20 12:16:29 +0000554 ScaleShortToQuantum((lbr_bits))); \
555 }
556
cristyef618312011-06-25 12:26:44 +0000557#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000558 { \
cristyef618312011-06-25 12:26:44 +0000559 LBR16PixelRed((pixel)); \
560 LBR16PixelGreen((pixel)); \
561 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000562 }
glennrp8e58efd2011-05-20 12:16:29 +0000563
cristyef618312011-06-25 12:26:44 +0000564#define LBR16PixelRGBO(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000565 { \
cristyef618312011-06-25 12:26:44 +0000566 LBR16PixelRGB((pixel)); \
567 LBR16PixelOpacity((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000568 }
glennrp8e58efd2011-05-20 12:16:29 +0000569
cristy3ed852e2009-09-05 21:47:34 +0000570/*
571 Establish thread safety.
572 setjmp/longjmp is claimed to be safe on these platforms:
573 setjmp/longjmp is alleged to be unsafe on these platforms:
574*/
575#ifndef SETJMP_IS_THREAD_SAFE
576#define PNG_SETJMP_NOT_THREAD_SAFE
577#endif
578
579#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
580static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000581 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000582#endif
583
584/*
585 This temporary until I set up malloc'ed object attributes array.
586 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
587 waste more memory.
588*/
589#define MNG_MAX_OBJECTS 256
590
591/*
592 If this not defined, spec is interpreted strictly. If it is
593 defined, an attempt will be made to recover from some errors,
594 including
595 o global PLTE too short
596*/
597#undef MNG_LOOSE
598
599/*
600 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
601 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
602 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
603 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
604 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
605 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
606 will be enabled by default in libpng-1.2.0.
607*/
cristy3ed852e2009-09-05 21:47:34 +0000608#ifdef PNG_MNG_FEATURES_SUPPORTED
609# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
610# define PNG_READ_EMPTY_PLTE_SUPPORTED
611# endif
612# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
613# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
614# endif
615#endif
616
617/*
cristybb503372010-05-27 20:51:26 +0000618 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000619 This macro is only defined in libpng-1.0.3 and later.
620 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
621*/
622#ifndef PNG_UINT_31_MAX
623#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
624#endif
625
626/*
627 Constant strings for known chunk types. If you need to add a chunk,
628 add a string holding the name here. To make the code more
629 portable, we use ASCII numbers like this, not characters.
630*/
631
632static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
633static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
634static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
635static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
636static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
637static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
638static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
639static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
640static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
641static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
642static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
643static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
644static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
645static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
646static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
647static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
648static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
649static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
650static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
651static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
652static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
653static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
654static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
655static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
656static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
657static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
658static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
659static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
660static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
661static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
662static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
663static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
664static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
665static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
666
667#if defined(JNG_SUPPORTED)
668static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
669static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
670static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
671static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
672static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
673static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
674#endif
675
676/*
677Other known chunks that are not yet supported by ImageMagick:
678static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
679static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
680static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
681static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
682static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
683static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
684static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
685static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
686*/
687
688typedef struct _MngBox
689{
cristy8182b072010-05-30 20:10:53 +0000690 long
cristy3ed852e2009-09-05 21:47:34 +0000691 left,
692 right,
693 top,
694 bottom;
695} MngBox;
696
697typedef struct _MngPair
698{
cristy8182b072010-05-30 20:10:53 +0000699 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000700 a,
701 b;
702} MngPair;
703
704#ifdef MNG_OBJECT_BUFFERS
705typedef struct _MngBuffer
706{
707
cristybb503372010-05-27 20:51:26 +0000708 size_t
cristy3ed852e2009-09-05 21:47:34 +0000709 height,
710 width;
711
712 Image
713 *image;
714
715 png_color
716 plte[256];
717
718 int
719 reference_count;
720
721 unsigned char
722 alpha_sample_depth,
723 compression_method,
724 color_type,
725 concrete,
726 filter_method,
727 frozen,
728 image_type,
729 interlace_method,
730 pixel_sample_depth,
731 plte_length,
732 sample_depth,
733 viewable;
734} MngBuffer;
735#endif
736
737typedef struct _MngInfo
738{
739
740#ifdef MNG_OBJECT_BUFFERS
741 MngBuffer
742 *ob[MNG_MAX_OBJECTS];
743#endif
744
745 Image *
746 image;
747
748 RectangleInfo
749 page;
750
751 int
752 adjoin,
753#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
754 bytes_in_read_buffer,
755 found_empty_plte,
756#endif
757 equal_backgrounds,
758 equal_chrms,
759 equal_gammas,
760#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
761 defined(PNG_MNG_FEATURES_SUPPORTED)
762 equal_palettes,
763#endif
764 equal_physs,
765 equal_srgbs,
766 framing_mode,
767 have_global_bkgd,
768 have_global_chrm,
769 have_global_gama,
770 have_global_phys,
771 have_global_sbit,
772 have_global_srgb,
773 have_saved_bkgd_index,
774 have_write_global_chrm,
775 have_write_global_gama,
776 have_write_global_plte,
777 have_write_global_srgb,
778 need_fram,
779 object_id,
780 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000781 saved_bkgd_index;
782
783 int
784 new_number_colors;
785
cristybb503372010-05-27 20:51:26 +0000786 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000787 image_found,
788 loop_count[256],
789 loop_iteration[256],
790 scenes_found,
791 x_off[MNG_MAX_OBJECTS],
792 y_off[MNG_MAX_OBJECTS];
793
794 MngBox
795 clip,
796 frame,
797 image_box,
798 object_clip[MNG_MAX_OBJECTS];
799
800 unsigned char
801 /* These flags could be combined into one byte */
802 exists[MNG_MAX_OBJECTS],
803 frozen[MNG_MAX_OBJECTS],
804 loop_active[256],
805 invisible[MNG_MAX_OBJECTS],
806 viewable[MNG_MAX_OBJECTS];
807
808 MagickOffsetType
809 loop_jump[256];
810
811 png_colorp
812 global_plte;
813
814 png_color_8
815 global_sbit;
816
817 png_byte
818#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
819 read_buffer[8],
820#endif
821 global_trns[256];
822
823 float
824 global_gamma;
825
826 ChromaticityInfo
827 global_chrm;
828
829 RenderingIntent
830 global_srgb_intent;
831
cristy35ef8242010-06-03 16:24:13 +0000832 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000833 delay,
834 global_plte_length,
835 global_trns_length,
836 global_x_pixels_per_unit,
837 global_y_pixels_per_unit,
838 mng_width,
839 mng_height,
840 ticks_per_second;
841
glennrpb9cfe272010-12-21 15:08:06 +0000842 MagickBooleanType
843 need_blob;
844
cristy3ed852e2009-09-05 21:47:34 +0000845 unsigned int
846 IsPalette,
847 global_phys_unit_type,
848 basi_warning,
849 clon_warning,
850 dhdr_warning,
851 jhdr_warning,
852 magn_warning,
853 past_warning,
854 phyg_warning,
855 phys_warning,
856 sbit_warning,
857 show_warning,
858 mng_type,
859 write_mng,
860 write_png_colortype,
861 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000862 write_png_compression_level,
863 write_png_compression_strategy,
864 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000865 write_png8,
866 write_png24,
867 write_png32;
868
869#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000870 size_t
cristy3ed852e2009-09-05 21:47:34 +0000871 basi_width,
872 basi_height;
873
874 unsigned int
875 basi_depth,
876 basi_color_type,
877 basi_compression_method,
878 basi_filter_type,
879 basi_interlace_method,
880 basi_red,
881 basi_green,
882 basi_blue,
883 basi_alpha,
884 basi_viewable;
885#endif
886
887 png_uint_16
888 magn_first,
889 magn_last,
890 magn_mb,
891 magn_ml,
892 magn_mr,
893 magn_mt,
894 magn_mx,
895 magn_my,
896 magn_methx,
897 magn_methy;
898
899 PixelPacket
900 mng_global_bkgd;
901
glennrp26f37912010-12-23 16:22:42 +0000902 /* Added at version 6.6.6-7 */
903 MagickBooleanType
904 ping_exclude_bKGD,
905 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000906 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000907 ping_exclude_EXIF,
908 ping_exclude_gAMA,
909 ping_exclude_iCCP,
910 /* ping_exclude_iTXt, */
911 ping_exclude_oFFs,
912 ping_exclude_pHYs,
913 ping_exclude_sRGB,
914 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000915 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000916 ping_exclude_vpAg,
917 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000918 ping_exclude_zTXt,
919 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000920
cristy3ed852e2009-09-05 21:47:34 +0000921} MngInfo;
922#endif /* VER */
923
924/*
925 Forward declarations.
926*/
927static MagickBooleanType
928 WritePNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000929
cristy3ed852e2009-09-05 21:47:34 +0000930static MagickBooleanType
931 WriteMNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000932
cristy3ed852e2009-09-05 21:47:34 +0000933#if defined(JNG_SUPPORTED)
934static MagickBooleanType
935 WriteJNGImage(const ImageInfo *,Image *);
936#endif
937
glennrp0c3e06b2010-11-19 13:45:02 +0000938#if PNG_LIBPNG_VER > 10011
939
glennrpfd05d622011-02-25 04:10:33 +0000940
glennrp0c3e06b2010-11-19 13:45:02 +0000941#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
942static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000943LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000944{
glennrp67b9c1a2011-04-22 18:47:36 +0000945 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
946 *
947 * This is true if the high byte and the next highest byte of
948 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000949 * are equal to each other. We check this by seeing if the samples
950 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000951 *
952 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000953 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000954 */
955
glennrp3faa9a32011-04-23 14:00:25 +0000956#define QuantumToCharToQuantumEqQuantum(quantum) \
957 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
958
glennrp0c3e06b2010-11-19 13:45:02 +0000959 MagickBooleanType
960 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000961
glennrp03e11f62011-04-22 13:30:16 +0000962 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000963 {
964
965 const PixelPacket
966 *p;
967
968 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000969 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
970 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
971 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
972 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000973
974 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
975 {
976 int indx;
977
978 for (indx=0; indx < (ssize_t) image->colors; indx++)
979 {
glennrp3faa9a32011-04-23 14:00:25 +0000980 ok_to_reduce=(
981 QuantumToCharToQuantumEqQuantum(
982 image->colormap[indx].red) &&
983 QuantumToCharToQuantumEqQuantum(
984 image->colormap[indx].green) &&
985 QuantumToCharToQuantumEqQuantum(
986 image->colormap[indx].blue)) ?
987 MagickTrue : MagickFalse;
988
glennrp0c3e06b2010-11-19 13:45:02 +0000989 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000990 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000991 }
992 }
993
994 if ((ok_to_reduce != MagickFalse) &&
995 (image->storage_class != PseudoClass))
996 {
997 ssize_t
998 y;
999
1000 register ssize_t
1001 x;
1002
1003 for (y=0; y < (ssize_t) image->rows; y++)
1004 {
1005 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1006
1007 if (p == (const PixelPacket *) NULL)
1008 {
1009 ok_to_reduce = MagickFalse;
1010 break;
1011 }
1012
1013 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1014 {
glennrp3faa9a32011-04-23 14:00:25 +00001015 ok_to_reduce=
cristyef618312011-06-25 12:26:44 +00001016 QuantumToCharToQuantumEqQuantum(GetPixelRed(p)) &&
1017 QuantumToCharToQuantumEqQuantum(GetPixelGreen(p)) &&
1018 QuantumToCharToQuantumEqQuantum(GetPixelBlue(p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001019 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001020
1021 if (ok_to_reduce == MagickFalse)
1022 break;
1023
1024 p++;
1025 }
glennrp8640fb52010-11-23 15:48:26 +00001026 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001027 break;
1028 }
1029 }
1030
1031 if (ok_to_reduce != MagickFalse)
1032 {
glennrp0c3e06b2010-11-19 13:45:02 +00001033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001034 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001035 }
glennrpa6a06632011-01-19 15:15:34 +00001036 else
1037 {
1038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001039 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001040 }
glennrp0c3e06b2010-11-19 13:45:02 +00001041 }
1042
1043 return ok_to_reduce;
1044}
1045#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1046
glennrpe610a072010-08-05 17:08:46 +00001047static int
glennrpcf002022011-01-30 02:38:15 +00001048Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001049{
glennrpe610a072010-08-05 17:08:46 +00001050 switch (intent)
1051 {
1052 case PerceptualIntent:
1053 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001054
glennrpe610a072010-08-05 17:08:46 +00001055 case RelativeIntent:
1056 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001057
glennrpe610a072010-08-05 17:08:46 +00001058 case SaturationIntent:
1059 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001060
glennrpe610a072010-08-05 17:08:46 +00001061 case AbsoluteIntent:
1062 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001063
glennrpe610a072010-08-05 17:08:46 +00001064 default:
1065 return -1;
1066 }
1067}
1068
1069static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001070Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001071{
glennrpcf002022011-01-30 02:38:15 +00001072 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001073 {
1074 case 0:
1075 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001076
glennrpe610a072010-08-05 17:08:46 +00001077 case 1:
1078 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001079
glennrpe610a072010-08-05 17:08:46 +00001080 case 2:
1081 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001082
glennrpe610a072010-08-05 17:08:46 +00001083 case 3:
1084 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001085
glennrpe610a072010-08-05 17:08:46 +00001086 default:
1087 return UndefinedIntent;
1088 }
1089}
1090
cristybb503372010-05-27 20:51:26 +00001091static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001092{
1093 if (x > y)
1094 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001095
cristy3ed852e2009-09-05 21:47:34 +00001096 return(y);
1097}
glennrp0c3e06b2010-11-19 13:45:02 +00001098
cristybb503372010-05-27 20:51:26 +00001099static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001100{
1101 if (x < y)
1102 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001103
cristy3ed852e2009-09-05 21:47:34 +00001104 return(y);
1105}
glennrp0c3e06b2010-11-19 13:45:02 +00001106
cristy3ed852e2009-09-05 21:47:34 +00001107
1108/*
1109%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1110% %
1111% %
1112% %
1113% I m a g e I s G r a y %
1114% %
1115% %
1116% %
1117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1118% %
1119% Like IsGrayImage except does not change DirectClass to PseudoClass %
1120% %
1121%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1122*/
1123static MagickBooleanType ImageIsGray(Image *image)
1124{
1125 register const PixelPacket
1126 *p;
1127
cristybb503372010-05-27 20:51:26 +00001128 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001129 i,
1130 x,
1131 y;
1132
1133 assert(image != (Image *) NULL);
1134 assert(image->signature == MagickSignature);
1135 if (image->debug != MagickFalse)
1136 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1137
1138 if (image->storage_class == PseudoClass)
1139 {
cristybb503372010-05-27 20:51:26 +00001140 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001141 if (IsGray(image->colormap+i) == MagickFalse)
1142 return(MagickFalse);
1143 return(MagickTrue);
1144 }
cristybb503372010-05-27 20:51:26 +00001145 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001146 {
1147 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1148 if (p == (const PixelPacket *) NULL)
1149 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001150 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001151 {
1152 if (IsGray(p) == MagickFalse)
1153 return(MagickFalse);
1154 p++;
1155 }
1156 }
1157 return(MagickTrue);
1158}
glennrpd5045b42010-03-24 12:40:35 +00001159#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001160#endif /* MAGICKCORE_PNG_DELEGATE */
1161
1162/*
1163%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1164% %
1165% %
1166% %
1167% I s M N G %
1168% %
1169% %
1170% %
1171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1172%
1173% IsMNG() returns MagickTrue if the image format type, identified by the
1174% magick string, is MNG.
1175%
1176% The format of the IsMNG method is:
1177%
1178% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1179%
1180% A description of each parameter follows:
1181%
1182% o magick: compare image format pattern against these bytes.
1183%
1184% o length: Specifies the length of the magick string.
1185%
1186%
1187*/
1188static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1189{
1190 if (length < 8)
1191 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001192
cristy3ed852e2009-09-05 21:47:34 +00001193 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1194 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001195
cristy3ed852e2009-09-05 21:47:34 +00001196 return(MagickFalse);
1197}
1198
1199/*
1200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1201% %
1202% %
1203% %
1204% I s J N G %
1205% %
1206% %
1207% %
1208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1209%
1210% IsJNG() returns MagickTrue if the image format type, identified by the
1211% magick string, is JNG.
1212%
1213% The format of the IsJNG method is:
1214%
1215% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1216%
1217% A description of each parameter follows:
1218%
1219% o magick: compare image format pattern against these bytes.
1220%
1221% o length: Specifies the length of the magick string.
1222%
1223%
1224*/
1225static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1226{
1227 if (length < 8)
1228 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001229
cristy3ed852e2009-09-05 21:47:34 +00001230 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1231 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001232
cristy3ed852e2009-09-05 21:47:34 +00001233 return(MagickFalse);
1234}
1235
1236/*
1237%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1238% %
1239% %
1240% %
1241% I s P N G %
1242% %
1243% %
1244% %
1245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1246%
1247% IsPNG() returns MagickTrue if the image format type, identified by the
1248% magick string, is PNG.
1249%
1250% The format of the IsPNG method is:
1251%
1252% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1253%
1254% A description of each parameter follows:
1255%
1256% o magick: compare image format pattern against these bytes.
1257%
1258% o length: Specifies the length of the magick string.
1259%
1260*/
1261static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1262{
1263 if (length < 8)
1264 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001265
cristy3ed852e2009-09-05 21:47:34 +00001266 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1267 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001268
cristy3ed852e2009-09-05 21:47:34 +00001269 return(MagickFalse);
1270}
1271
1272#if defined(MAGICKCORE_PNG_DELEGATE)
1273#if defined(__cplusplus) || defined(c_plusplus)
1274extern "C" {
1275#endif
1276
glennrpd5045b42010-03-24 12:40:35 +00001277#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001278static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001279{
1280 unsigned char
1281 buffer[4];
1282
1283 assert(image != (Image *) NULL);
1284 assert(image->signature == MagickSignature);
1285 buffer[0]=(unsigned char) (value >> 24);
1286 buffer[1]=(unsigned char) (value >> 16);
1287 buffer[2]=(unsigned char) (value >> 8);
1288 buffer[3]=(unsigned char) value;
1289 return((size_t) WriteBlob(image,4,buffer));
1290}
1291
1292static void PNGLong(png_bytep p,png_uint_32 value)
1293{
1294 *p++=(png_byte) ((value >> 24) & 0xff);
1295 *p++=(png_byte) ((value >> 16) & 0xff);
1296 *p++=(png_byte) ((value >> 8) & 0xff);
1297 *p++=(png_byte) (value & 0xff);
1298}
1299
glennrpa521b2f2010-10-29 04:11:03 +00001300#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001301static void PNGsLong(png_bytep p,png_int_32 value)
1302{
1303 *p++=(png_byte) ((value >> 24) & 0xff);
1304 *p++=(png_byte) ((value >> 16) & 0xff);
1305 *p++=(png_byte) ((value >> 8) & 0xff);
1306 *p++=(png_byte) (value & 0xff);
1307}
glennrpa521b2f2010-10-29 04:11:03 +00001308#endif
cristy3ed852e2009-09-05 21:47:34 +00001309
1310static void PNGShort(png_bytep p,png_uint_16 value)
1311{
1312 *p++=(png_byte) ((value >> 8) & 0xff);
1313 *p++=(png_byte) (value & 0xff);
1314}
1315
1316static void PNGType(png_bytep p,png_bytep type)
1317{
1318 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1319}
1320
glennrp03812ae2010-12-24 01:31:34 +00001321static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1322 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001323{
1324 if (logging != MagickFalse)
1325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001326 " Writing %c%c%c%c chunk, length: %.20g",
1327 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001328}
glennrpd5045b42010-03-24 12:40:35 +00001329#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001330
1331#if defined(__cplusplus) || defined(c_plusplus)
1332}
1333#endif
1334
glennrpd5045b42010-03-24 12:40:35 +00001335#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001336/*
1337%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1338% %
1339% %
1340% %
1341% R e a d P N G I m a g e %
1342% %
1343% %
1344% %
1345%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1346%
1347% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1348% Multiple-image Network Graphics (MNG) image file and returns it. It
1349% allocates the memory necessary for the new Image structure and returns a
1350% pointer to the new image or set of images.
1351%
1352% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1353%
1354% The format of the ReadPNGImage method is:
1355%
1356% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1357%
1358% A description of each parameter follows:
1359%
1360% o image_info: the image info.
1361%
1362% o exception: return any errors or warnings in this structure.
1363%
1364% To do, more or less in chronological order (as of version 5.5.2,
1365% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1366%
1367% Get 16-bit cheap transparency working.
1368%
1369% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1370%
1371% Preserve all unknown and not-yet-handled known chunks found in input
1372% PNG file and copy them into output PNG files according to the PNG
1373% copying rules.
1374%
1375% (At this point, PNG encoding should be in full MNG compliance)
1376%
1377% Provide options for choice of background to use when the MNG BACK
1378% chunk is not present or is not mandatory (i.e., leave transparent,
1379% user specified, MNG BACK, PNG bKGD)
1380%
1381% Implement LOOP/ENDL [done, but could do discretionary loops more
1382% efficiently by linking in the duplicate frames.].
1383%
1384% Decode and act on the MHDR simplicity profile (offer option to reject
1385% files or attempt to process them anyway when the profile isn't LC or VLC).
1386%
1387% Upgrade to full MNG without Delta-PNG.
1388%
1389% o BACK [done a while ago except for background image ID]
1390% o MOVE [done 15 May 1999]
1391% o CLIP [done 15 May 1999]
1392% o DISC [done 19 May 1999]
1393% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1394% o SEEK [partially done 19 May 1999 (discard function only)]
1395% o SHOW
1396% o PAST
1397% o BASI
1398% o MNG-level tEXt/iTXt/zTXt
1399% o pHYg
1400% o pHYs
1401% o sBIT
1402% o bKGD
1403% o iTXt (wait for libpng implementation).
1404%
1405% Use the scene signature to discover when an identical scene is
1406% being reused, and just point to the original image->exception instead
1407% of storing another set of pixels. This not specific to MNG
1408% but could be applied generally.
1409%
1410% Upgrade to full MNG with Delta-PNG.
1411%
1412% JNG tEXt/iTXt/zTXt
1413%
1414% We will not attempt to read files containing the CgBI chunk.
1415% They are really Xcode files meant for display on the iPhone.
1416% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001417% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001418% since irretrievable loss of color data has occurred due to the
1419% use of premultiplied alpha.
1420*/
1421
1422#if defined(__cplusplus) || defined(c_plusplus)
1423extern "C" {
1424#endif
1425
1426/*
1427 This the function that does the actual reading of data. It is
1428 the same as the one supplied in libpng, except that it receives the
1429 datastream from the ReadBlob() function instead of standard input.
1430*/
1431static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1432{
1433 Image
1434 *image;
1435
1436 image=(Image *) png_get_io_ptr(png_ptr);
1437 if (length)
1438 {
1439 png_size_t
1440 check;
1441
1442 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1443 if (check != length)
1444 {
1445 char
1446 msg[MaxTextExtent];
1447
cristy3b6fd2e2011-05-20 12:53:50 +00001448 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001449 "Expected %.20g bytes; found %.20g bytes",(double) length,
1450 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001451 png_warning(png_ptr,msg);
1452 png_error(png_ptr,"Read Exception");
1453 }
1454 }
1455}
1456
1457#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1458 !defined(PNG_MNG_FEATURES_SUPPORTED)
1459/* We use mng_get_data() instead of png_get_data() if we have a libpng
1460 * older than libpng-1.0.3a, which was the first to allow the empty
1461 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1462 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1463 * encountered after an empty PLTE, so we have to look ahead for bKGD
1464 * chunks and remove them from the datastream that is passed to libpng,
1465 * and store their contents for later use.
1466 */
1467static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1468{
1469 MngInfo
1470 *mng_info;
1471
1472 Image
1473 *image;
1474
1475 png_size_t
1476 check;
1477
cristybb503372010-05-27 20:51:26 +00001478 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001479 i;
1480
1481 i=0;
1482 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1483 image=(Image *) mng_info->image;
1484 while (mng_info->bytes_in_read_buffer && length)
1485 {
1486 data[i]=mng_info->read_buffer[i];
1487 mng_info->bytes_in_read_buffer--;
1488 length--;
1489 i++;
1490 }
1491 if (length)
1492 {
1493 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001494
cristy3ed852e2009-09-05 21:47:34 +00001495 if (check != length)
1496 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001497
cristy3ed852e2009-09-05 21:47:34 +00001498 if (length == 4)
1499 {
1500 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1501 (data[3] == 0))
1502 {
1503 check=(png_size_t) ReadBlob(image,(size_t) length,
1504 (char *) mng_info->read_buffer);
1505 mng_info->read_buffer[4]=0;
1506 mng_info->bytes_in_read_buffer=4;
1507 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1508 mng_info->found_empty_plte=MagickTrue;
1509 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1510 {
1511 mng_info->found_empty_plte=MagickFalse;
1512 mng_info->have_saved_bkgd_index=MagickFalse;
1513 }
1514 }
glennrp0fe50b42010-11-16 03:52:51 +00001515
cristy3ed852e2009-09-05 21:47:34 +00001516 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1517 (data[3] == 1))
1518 {
1519 check=(png_size_t) ReadBlob(image,(size_t) length,
1520 (char *) mng_info->read_buffer);
1521 mng_info->read_buffer[4]=0;
1522 mng_info->bytes_in_read_buffer=4;
1523 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1524 if (mng_info->found_empty_plte)
1525 {
1526 /*
1527 Skip the bKGD data byte and CRC.
1528 */
1529 check=(png_size_t)
1530 ReadBlob(image,5,(char *) mng_info->read_buffer);
1531 check=(png_size_t) ReadBlob(image,(size_t) length,
1532 (char *) mng_info->read_buffer);
1533 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1534 mng_info->have_saved_bkgd_index=MagickTrue;
1535 mng_info->bytes_in_read_buffer=0;
1536 }
1537 }
1538 }
1539 }
1540}
1541#endif
1542
1543static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1544{
1545 Image
1546 *image;
1547
1548 image=(Image *) png_get_io_ptr(png_ptr);
1549 if (length)
1550 {
1551 png_size_t
1552 check;
1553
cristybb503372010-05-27 20:51:26 +00001554 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001555
cristy3ed852e2009-09-05 21:47:34 +00001556 if (check != length)
1557 png_error(png_ptr,"WriteBlob Failed");
1558 }
1559}
1560
1561static void png_flush_data(png_structp png_ptr)
1562{
1563 (void) png_ptr;
1564}
1565
1566#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1567static int PalettesAreEqual(Image *a,Image *b)
1568{
cristybb503372010-05-27 20:51:26 +00001569 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001570 i;
1571
1572 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1573 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001574
cristy3ed852e2009-09-05 21:47:34 +00001575 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1576 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001577
cristy3ed852e2009-09-05 21:47:34 +00001578 if (a->colors != b->colors)
1579 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001580
cristybb503372010-05-27 20:51:26 +00001581 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001582 {
1583 if ((a->colormap[i].red != b->colormap[i].red) ||
1584 (a->colormap[i].green != b->colormap[i].green) ||
1585 (a->colormap[i].blue != b->colormap[i].blue))
1586 return((int) MagickFalse);
1587 }
glennrp0fe50b42010-11-16 03:52:51 +00001588
cristy3ed852e2009-09-05 21:47:34 +00001589 return((int) MagickTrue);
1590}
1591#endif
1592
1593static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1594{
1595 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1596 mng_info->exists[i] && !mng_info->frozen[i])
1597 {
1598#ifdef MNG_OBJECT_BUFFERS
1599 if (mng_info->ob[i] != (MngBuffer *) NULL)
1600 {
1601 if (mng_info->ob[i]->reference_count > 0)
1602 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001603
cristy3ed852e2009-09-05 21:47:34 +00001604 if (mng_info->ob[i]->reference_count == 0)
1605 {
1606 if (mng_info->ob[i]->image != (Image *) NULL)
1607 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001608
cristy3ed852e2009-09-05 21:47:34 +00001609 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1610 }
1611 }
1612 mng_info->ob[i]=(MngBuffer *) NULL;
1613#endif
1614 mng_info->exists[i]=MagickFalse;
1615 mng_info->invisible[i]=MagickFalse;
1616 mng_info->viewable[i]=MagickFalse;
1617 mng_info->frozen[i]=MagickFalse;
1618 mng_info->x_off[i]=0;
1619 mng_info->y_off[i]=0;
1620 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001621 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001622 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001623 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001624 }
1625}
1626
glennrp21f0e622011-01-07 16:20:57 +00001627static void MngInfoFreeStruct(MngInfo *mng_info,
1628 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001629{
glennrp21f0e622011-01-07 16:20:57 +00001630 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001631 {
cristybb503372010-05-27 20:51:26 +00001632 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001633 i;
1634
1635 for (i=1; i < MNG_MAX_OBJECTS; i++)
1636 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001637
cristy3ed852e2009-09-05 21:47:34 +00001638 if (mng_info->global_plte != (png_colorp) NULL)
1639 mng_info->global_plte=(png_colorp)
1640 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001641
cristy3ed852e2009-09-05 21:47:34 +00001642 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1643 *have_mng_structure=MagickFalse;
1644 }
1645}
1646
1647static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1648{
1649 MngBox
1650 box;
1651
1652 box=box1;
1653 if (box.left < box2.left)
1654 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001655
cristy3ed852e2009-09-05 21:47:34 +00001656 if (box.top < box2.top)
1657 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001658
cristy3ed852e2009-09-05 21:47:34 +00001659 if (box.right > box2.right)
1660 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001661
cristy3ed852e2009-09-05 21:47:34 +00001662 if (box.bottom > box2.bottom)
1663 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001664
cristy3ed852e2009-09-05 21:47:34 +00001665 return box;
1666}
1667
1668static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1669{
1670 MngBox
1671 box;
1672
1673 /*
1674 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1675 */
cristybb503372010-05-27 20:51:26 +00001676 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1677 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1678 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1679 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001680 if (delta_type != 0)
1681 {
1682 box.left+=previous_box.left;
1683 box.right+=previous_box.right;
1684 box.top+=previous_box.top;
1685 box.bottom+=previous_box.bottom;
1686 }
glennrp0fe50b42010-11-16 03:52:51 +00001687
cristy3ed852e2009-09-05 21:47:34 +00001688 return(box);
1689}
1690
1691static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1692 unsigned char *p)
1693{
1694 MngPair
1695 pair;
1696 /*
cristybb503372010-05-27 20:51:26 +00001697 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001698 */
cristy8182b072010-05-30 20:10:53 +00001699 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1700 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001701
cristy3ed852e2009-09-05 21:47:34 +00001702 if (delta_type != 0)
1703 {
1704 pair.a+=previous_pair.a;
1705 pair.b+=previous_pair.b;
1706 }
glennrp0fe50b42010-11-16 03:52:51 +00001707
cristy3ed852e2009-09-05 21:47:34 +00001708 return(pair);
1709}
1710
cristy8182b072010-05-30 20:10:53 +00001711static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001712{
cristy8182b072010-05-30 20:10:53 +00001713 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001714}
1715
glennrpcf002022011-01-30 02:38:15 +00001716static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001717{
1718 Image
1719 *image;
1720
1721 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001722
cristy3ed852e2009-09-05 21:47:34 +00001723 if (image->debug != MagickFalse)
1724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1725 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001726
cristy3ed852e2009-09-05 21:47:34 +00001727 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1728 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001729
glennrpe4017e32011-01-08 17:16:09 +00001730#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001731 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1732 * are building with libpng-1.4.x and can be ignored.
1733 */
cristy3ed852e2009-09-05 21:47:34 +00001734 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001735#else
1736 png_longjmp(ping,1);
1737#endif
cristy3ed852e2009-09-05 21:47:34 +00001738}
1739
glennrpcf002022011-01-30 02:38:15 +00001740static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001741{
1742 Image
1743 *image;
1744
1745 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1746 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001747
cristy3ed852e2009-09-05 21:47:34 +00001748 image=(Image *) png_get_error_ptr(ping);
1749 if (image->debug != MagickFalse)
1750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001751 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001752
cristy3ed852e2009-09-05 21:47:34 +00001753 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1754 message,"`%s'",image->filename);
1755}
1756
1757#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001758static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001759{
1760#if (PNG_LIBPNG_VER < 10011)
1761 png_voidp
1762 ret;
1763
1764 png_ptr=png_ptr;
1765 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001766
cristy3ed852e2009-09-05 21:47:34 +00001767 if (ret == NULL)
1768 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001769
cristy3ed852e2009-09-05 21:47:34 +00001770 return(ret);
1771#else
1772 png_ptr=png_ptr;
1773 return((png_voidp) AcquireMagickMemory((size_t) size));
1774#endif
1775}
1776
1777/*
1778 Free a pointer. It is removed from the list at the same time.
1779*/
glennrpcf002022011-01-30 02:38:15 +00001780static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001781{
1782 png_ptr=png_ptr;
1783 ptr=RelinquishMagickMemory(ptr);
1784 return((png_free_ptr) NULL);
1785}
1786#endif
1787
1788#if defined(__cplusplus) || defined(c_plusplus)
1789}
1790#endif
1791
1792static int
glennrpcf002022011-01-30 02:38:15 +00001793Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001794 png_textp text,int ii)
1795{
cristybb503372010-05-27 20:51:26 +00001796 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001797 i;
1798
1799 register unsigned char
1800 *dp;
1801
1802 register png_charp
1803 sp;
1804
1805 png_uint_32
1806 length,
1807 nibbles;
1808
1809 StringInfo
1810 *profile;
1811
glennrp0c3e06b2010-11-19 13:45:02 +00001812 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001813 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1814 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1815 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1816 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1817 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1818 13,14,15};
1819
1820 sp=text[ii].text+1;
1821 /* look for newline */
1822 while (*sp != '\n')
1823 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001824
cristy3ed852e2009-09-05 21:47:34 +00001825 /* look for length */
1826 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1827 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001828
cristyf2f27272009-12-17 14:48:46 +00001829 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001830
glennrp97f90e22011-02-22 05:47:58 +00001831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1832 " length: %lu",(unsigned long) length);
1833
cristy3ed852e2009-09-05 21:47:34 +00001834 while (*sp != ' ' && *sp != '\n')
1835 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001836
cristy3ed852e2009-09-05 21:47:34 +00001837 /* allocate space */
1838 if (length == 0)
1839 {
1840 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1841 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1842 return(MagickFalse);
1843 }
glennrp0fe50b42010-11-16 03:52:51 +00001844
cristy3ed852e2009-09-05 21:47:34 +00001845 profile=AcquireStringInfo(length);
glennrp0fe50b42010-11-16 03:52:51 +00001846
cristy3ed852e2009-09-05 21:47:34 +00001847 if (profile == (StringInfo *) NULL)
1848 {
1849 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1850 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1851 "unable to copy profile");
1852 return(MagickFalse);
1853 }
glennrp0fe50b42010-11-16 03:52:51 +00001854
cristy3ed852e2009-09-05 21:47:34 +00001855 /* copy profile, skipping white space and column 1 "=" signs */
1856 dp=GetStringInfoDatum(profile);
1857 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001858
cristybb503372010-05-27 20:51:26 +00001859 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001860 {
1861 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1862 {
1863 if (*sp == '\0')
1864 {
1865 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1866 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1867 profile=DestroyStringInfo(profile);
1868 return(MagickFalse);
1869 }
1870 sp++;
1871 }
glennrp0fe50b42010-11-16 03:52:51 +00001872
cristy3ed852e2009-09-05 21:47:34 +00001873 if (i%2 == 0)
1874 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001875
cristy3ed852e2009-09-05 21:47:34 +00001876 else
1877 (*dp++)+=unhex[(int) *sp++];
1878 }
1879 /*
1880 We have already read "Raw profile type.
1881 */
1882 (void) SetImageProfile(image,&text[ii].key[17],profile);
1883 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001884
cristy3ed852e2009-09-05 21:47:34 +00001885 if (image_info->verbose)
1886 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001887
cristy3ed852e2009-09-05 21:47:34 +00001888 return MagickTrue;
1889}
1890
1891#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1892static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1893{
1894 Image
1895 *image;
1896
1897
1898 /* The unknown chunk structure contains the chunk data:
1899 png_byte name[5];
1900 png_byte *data;
1901 png_size_t size;
1902
1903 Note that libpng has already taken care of the CRC handling.
1904 */
1905
1906
1907 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1908 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1909 return(0); /* Did not recognize */
1910
1911 /* recognized vpAg */
1912
1913 if (chunk->size != 9)
1914 return(-1); /* Error return */
1915
1916 if (chunk->data[8] != 0)
1917 return(0); /* ImageMagick requires pixel units */
1918
1919 image=(Image *) png_get_user_chunk_ptr(ping);
1920
cristybb503372010-05-27 20:51:26 +00001921 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001922 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001923
cristybb503372010-05-27 20:51:26 +00001924 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001925 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1926
1927 /* Return one of the following: */
1928 /* return(-n); chunk had an error */
1929 /* return(0); did not recognize */
1930 /* return(n); success */
1931
1932 return(1);
1933
1934}
1935#endif
1936
1937/*
1938%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1939% %
1940% %
1941% %
1942% R e a d O n e P N G I m a g e %
1943% %
1944% %
1945% %
1946%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1947%
1948% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1949% (minus the 8-byte signature) and returns it. It allocates the memory
1950% necessary for the new Image structure and returns a pointer to the new
1951% image.
1952%
1953% The format of the ReadOnePNGImage method is:
1954%
1955% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1956% ExceptionInfo *exception)
1957%
1958% A description of each parameter follows:
1959%
1960% o mng_info: Specifies a pointer to a MngInfo structure.
1961%
1962% o image_info: the image info.
1963%
1964% o exception: return any errors or warnings in this structure.
1965%
1966*/
1967static Image *ReadOnePNGImage(MngInfo *mng_info,
1968 const ImageInfo *image_info, ExceptionInfo *exception)
1969{
1970 /* Read one PNG image */
1971
glennrpcc95c3f2011-04-18 16:46:48 +00001972 /* To do: Read the tIME chunk into the date:modify property */
1973 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1974
cristy3ed852e2009-09-05 21:47:34 +00001975 Image
1976 *image;
1977
1978 int
glennrp4eb39312011-03-30 21:34:55 +00001979 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001980 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001981 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001982 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001983 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001984 pass,
1985 ping_bit_depth,
1986 ping_color_type,
1987 ping_interlace_method,
1988 ping_compression_method,
1989 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001990 ping_num_trans,
1991 unit_type;
1992
1993 double
1994 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00001995
glennrpa6a06632011-01-19 15:15:34 +00001996 LongPixelPacket
1997 transparent_color;
1998
cristy3ed852e2009-09-05 21:47:34 +00001999 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002000 logging,
cristy3ed852e2009-09-05 21:47:34 +00002001 status;
2002
glennrpfaa852b2010-03-30 12:17:00 +00002003 png_bytep
2004 ping_trans_alpha;
2005
2006 png_color_16p
2007 ping_background,
2008 ping_trans_color;
2009
cristy3ed852e2009-09-05 21:47:34 +00002010 png_info
2011 *end_info,
2012 *ping_info;
2013
2014 png_struct
2015 *ping;
2016
2017 png_textp
2018 text;
2019
glennrpfaa852b2010-03-30 12:17:00 +00002020 png_uint_32
2021 ping_height,
2022 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002023 ping_rowbytes,
2024 x_resolution,
2025 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002026
cristy3ed852e2009-09-05 21:47:34 +00002027 QuantumInfo
2028 *quantum_info;
2029
2030 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002031 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002032
cristybb503372010-05-27 20:51:26 +00002033 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002034 y;
2035
2036 register unsigned char
2037 *p;
2038
2039 register IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00002040 *indexes;
cristy3ed852e2009-09-05 21:47:34 +00002041
cristybb503372010-05-27 20:51:26 +00002042 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002043 i,
2044 x;
2045
2046 register PixelPacket
2047 *q;
2048
2049 size_t
glennrp39992b42010-11-14 00:03:43 +00002050 length,
cristy3ed852e2009-09-05 21:47:34 +00002051 row_offset;
2052
cristyeb3b22a2011-03-31 20:16:11 +00002053 ssize_t
2054 j;
2055
cristy3ed852e2009-09-05 21:47:34 +00002056#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2057 png_byte unused_chunks[]=
2058 {
2059 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2060 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2061 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2062 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2063 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2064 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2065 };
2066#endif
2067
2068 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002069 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002070
2071#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002072 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002073#endif
2074
glennrp25c1e2b2010-03-25 01:39:56 +00002075#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002076 if (image_info->verbose)
2077 printf("Your PNG library (libpng-%s) is rather old.\n",
2078 PNG_LIBPNG_VER_STRING);
2079#endif
2080
glennrp61b4c952009-11-10 20:40:41 +00002081#if (PNG_LIBPNG_VER >= 10400)
2082# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2083 if (image_info->verbose)
2084 {
2085 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2086 PNG_LIBPNG_VER_STRING);
2087 printf("Please update it.\n");
2088 }
2089# endif
2090#endif
2091
2092
cristyed552522009-10-16 14:04:35 +00002093 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002094 image=mng_info->image;
2095
glennrpa6a06632011-01-19 15:15:34 +00002096 if (logging != MagickFalse)
2097 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2098 " image->matte=%d",(int) image->matte);
2099
glennrp0e319732011-01-25 21:53:13 +00002100 /* Set to an out-of-range color unless tRNS chunk is present */
2101 transparent_color.red=65537;
2102 transparent_color.green=65537;
2103 transparent_color.blue=65537;
2104 transparent_color.opacity=65537;
2105
glennrpcb395ac2011-03-30 19:50:23 +00002106 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002107 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002108 num_raw_profiles = 0;
2109
cristy3ed852e2009-09-05 21:47:34 +00002110 /*
2111 Allocate the PNG structures
2112 */
2113#ifdef PNG_USER_MEM_SUPPORTED
2114 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00002115 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2116 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002117#else
2118 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00002119 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002120#endif
2121 if (ping == (png_struct *) NULL)
2122 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002123
cristy3ed852e2009-09-05 21:47:34 +00002124 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002125
cristy3ed852e2009-09-05 21:47:34 +00002126 if (ping_info == (png_info *) NULL)
2127 {
2128 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2129 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2130 }
glennrp0fe50b42010-11-16 03:52:51 +00002131
cristy3ed852e2009-09-05 21:47:34 +00002132 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002133
cristy3ed852e2009-09-05 21:47:34 +00002134 if (end_info == (png_info *) NULL)
2135 {
2136 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2137 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2138 }
glennrp0fe50b42010-11-16 03:52:51 +00002139
glennrpcf002022011-01-30 02:38:15 +00002140 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002141
glennrpfaa852b2010-03-30 12:17:00 +00002142 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002143 {
2144 /*
2145 PNG image is corrupt.
2146 */
2147 png_destroy_read_struct(&ping,&ping_info,&end_info);
2148#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002149 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002150#endif
2151 if (logging != MagickFalse)
2152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2153 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002154
cristy3ed852e2009-09-05 21:47:34 +00002155 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002156 {
2157 InheritException(exception,&image->exception);
2158 image->columns=0;
2159 }
glennrp0fe50b42010-11-16 03:52:51 +00002160
cristy3ed852e2009-09-05 21:47:34 +00002161 return(GetFirstImageInList(image));
2162 }
2163 /*
2164 Prepare PNG for reading.
2165 */
glennrpfaa852b2010-03-30 12:17:00 +00002166
cristy3ed852e2009-09-05 21:47:34 +00002167 mng_info->image_found++;
2168 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002169
cristy3ed852e2009-09-05 21:47:34 +00002170 if (LocaleCompare(image_info->magick,"MNG") == 0)
2171 {
2172#if defined(PNG_MNG_FEATURES_SUPPORTED)
2173 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2174 png_set_read_fn(ping,image,png_get_data);
2175#else
2176#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2177 png_permit_empty_plte(ping,MagickTrue);
2178 png_set_read_fn(ping,image,png_get_data);
2179#else
2180 mng_info->image=image;
2181 mng_info->bytes_in_read_buffer=0;
2182 mng_info->found_empty_plte=MagickFalse;
2183 mng_info->have_saved_bkgd_index=MagickFalse;
2184 png_set_read_fn(ping,mng_info,mng_get_data);
2185#endif
2186#endif
2187 }
glennrp0fe50b42010-11-16 03:52:51 +00002188
cristy3ed852e2009-09-05 21:47:34 +00002189 else
2190 png_set_read_fn(ping,image,png_get_data);
2191
2192#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2193 /* Ignore unused chunks and all unknown chunks except for vpAg */
2194 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2195 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2196 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2197 (int)sizeof(unused_chunks)/5);
2198 /* Callback for other unknown chunks */
2199 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2200#endif
2201
glennrp991e92a2010-01-28 03:09:00 +00002202#if (PNG_LIBPNG_VER < 10400)
2203# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2204 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002205 /* Disable thread-unsafe features of pnggccrd */
2206 if (png_access_version_number() >= 10200)
2207 {
2208 png_uint_32 mmx_disable_mask=0;
2209 png_uint_32 asm_flags;
2210
2211 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2212 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2213 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2214 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2215 asm_flags=png_get_asm_flags(ping);
2216 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2217 }
glennrp991e92a2010-01-28 03:09:00 +00002218# endif
cristy3ed852e2009-09-05 21:47:34 +00002219#endif
2220
2221 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002222
2223 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2224 &ping_bit_depth,&ping_color_type,
2225 &ping_interlace_method,&ping_compression_method,
2226 &ping_filter_method);
2227
2228 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2229 &ping_trans_color);
2230
2231 (void) png_get_bKGD(ping, ping_info, &ping_background);
2232
2233 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002234 {
glennrpfaa852b2010-03-30 12:17:00 +00002235 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2236 {
2237 png_set_packing(ping);
2238 ping_bit_depth = 8;
2239 }
cristy3ed852e2009-09-05 21:47:34 +00002240 }
glennrpfaa852b2010-03-30 12:17:00 +00002241
2242 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002243 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002244 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002245 if (logging != MagickFalse)
2246 {
2247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002248 " PNG width: %.20g, height: %.20g",
2249 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002250
cristy3ed852e2009-09-05 21:47:34 +00002251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2252 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002253 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002254
cristy3ed852e2009-09-05 21:47:34 +00002255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2256 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002257 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002258
cristy3ed852e2009-09-05 21:47:34 +00002259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2260 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002261 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002262 }
2263
glennrpfaa852b2010-03-30 12:17:00 +00002264#ifdef PNG_READ_iCCP_SUPPORTED
2265 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002266 {
2267 int
2268 compression;
2269
glennrpe4017e32011-01-08 17:16:09 +00002270#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002271 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002272 info;
2273#else
2274 png_bytep
2275 info;
2276#endif
2277
2278 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002279 name;
2280
2281 png_uint_32
2282 profile_length;
2283
2284 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2285 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002286
cristy3ed852e2009-09-05 21:47:34 +00002287 if (profile_length != 0)
2288 {
2289 StringInfo
2290 *profile;
2291
2292 if (logging != MagickFalse)
2293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2294 " Reading PNG iCCP chunk.");
2295 profile=AcquireStringInfo(profile_length);
2296 SetStringInfoDatum(profile,(const unsigned char *) info);
2297 (void) SetImageProfile(image,"icc",profile);
2298 profile=DestroyStringInfo(profile);
2299 }
2300 }
2301#endif
2302#if defined(PNG_READ_sRGB_SUPPORTED)
2303 {
cristy3ed852e2009-09-05 21:47:34 +00002304 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002305 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2306 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002307
cristy3ed852e2009-09-05 21:47:34 +00002308 if (png_get_sRGB(ping,ping_info,&intent))
2309 {
glennrpcf002022011-01-30 02:38:15 +00002310 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2311 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002312
cristy3ed852e2009-09-05 21:47:34 +00002313 if (logging != MagickFalse)
2314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002315 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002316 }
2317 }
2318#endif
2319 {
glennrpfaa852b2010-03-30 12:17:00 +00002320 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2321 if (mng_info->have_global_gama)
2322 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002323
cristy3ed852e2009-09-05 21:47:34 +00002324 if (png_get_gAMA(ping,ping_info,&file_gamma))
2325 {
2326 image->gamma=(float) file_gamma;
2327 if (logging != MagickFalse)
2328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2329 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2330 }
2331 }
glennrpfaa852b2010-03-30 12:17:00 +00002332 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2333 {
2334 if (mng_info->have_global_chrm != MagickFalse)
2335 {
2336 (void) png_set_cHRM(ping,ping_info,
2337 mng_info->global_chrm.white_point.x,
2338 mng_info->global_chrm.white_point.y,
2339 mng_info->global_chrm.red_primary.x,
2340 mng_info->global_chrm.red_primary.y,
2341 mng_info->global_chrm.green_primary.x,
2342 mng_info->global_chrm.green_primary.y,
2343 mng_info->global_chrm.blue_primary.x,
2344 mng_info->global_chrm.blue_primary.y);
2345 }
2346 }
glennrp0fe50b42010-11-16 03:52:51 +00002347
glennrpfaa852b2010-03-30 12:17:00 +00002348 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002349 {
2350 (void) png_get_cHRM(ping,ping_info,
2351 &image->chromaticity.white_point.x,
2352 &image->chromaticity.white_point.y,
2353 &image->chromaticity.red_primary.x,
2354 &image->chromaticity.red_primary.y,
2355 &image->chromaticity.green_primary.x,
2356 &image->chromaticity.green_primary.y,
2357 &image->chromaticity.blue_primary.x,
2358 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002359
cristy3ed852e2009-09-05 21:47:34 +00002360 if (logging != MagickFalse)
2361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2362 " Reading PNG cHRM chunk.");
2363 }
glennrp0fe50b42010-11-16 03:52:51 +00002364
glennrpe610a072010-08-05 17:08:46 +00002365 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002366 {
glennrpe610a072010-08-05 17:08:46 +00002367 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002368 Magick_RenderingIntent_to_PNG_RenderingIntent
2369 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002370 png_set_gAMA(ping,ping_info,0.45455f);
2371 png_set_cHRM(ping,ping_info,
2372 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2373 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002374 }
cristy3ed852e2009-09-05 21:47:34 +00002375#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002376 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002377 {
cristy905ef802011-02-23 00:29:18 +00002378 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2379 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002380
cristy3ed852e2009-09-05 21:47:34 +00002381 if (logging != MagickFalse)
2382 if (image->page.x || image->page.y)
2383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002384 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2385 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002386 }
2387#endif
2388#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002389 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2390 {
2391 if (mng_info->have_global_phys)
2392 {
2393 png_set_pHYs(ping,ping_info,
2394 mng_info->global_x_pixels_per_unit,
2395 mng_info->global_y_pixels_per_unit,
2396 mng_info->global_phys_unit_type);
2397 }
2398 }
2399
2400 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002401 {
cristy3ed852e2009-09-05 21:47:34 +00002402 /*
2403 Set image resolution.
2404 */
2405 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002406 &unit_type);
2407 image->x_resolution=(double) x_resolution;
2408 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002409
cristy3ed852e2009-09-05 21:47:34 +00002410 if (unit_type == PNG_RESOLUTION_METER)
2411 {
2412 image->units=PixelsPerCentimeterResolution;
2413 image->x_resolution=(double) x_resolution/100.0;
2414 image->y_resolution=(double) y_resolution/100.0;
2415 }
glennrp0fe50b42010-11-16 03:52:51 +00002416
cristy3ed852e2009-09-05 21:47:34 +00002417 if (logging != MagickFalse)
2418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002419 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2420 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002421 }
cristy3ed852e2009-09-05 21:47:34 +00002422#endif
glennrp823b55c2011-03-14 18:46:46 +00002423
glennrpfaa852b2010-03-30 12:17:00 +00002424 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002425 {
2426 int
2427 number_colors;
2428
2429 png_colorp
2430 palette;
2431
2432 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002433
cristy3ed852e2009-09-05 21:47:34 +00002434 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002435 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002436 {
2437 if (mng_info->global_plte_length)
2438 {
2439 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2440 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002441
glennrpfaa852b2010-03-30 12:17:00 +00002442 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002443 if (mng_info->global_trns_length)
2444 {
2445 if (mng_info->global_trns_length >
2446 mng_info->global_plte_length)
2447 (void) ThrowMagickException(&image->exception,
2448 GetMagickModule(),CoderError,
2449 "global tRNS has more entries than global PLTE",
2450 "`%s'",image_info->filename);
2451 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2452 (int) mng_info->global_trns_length,NULL);
2453 }
glennrpbfd9e612011-04-22 14:02:20 +00002454#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002455 if (
2456#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2457 mng_info->have_saved_bkgd_index ||
2458#endif
glennrpfaa852b2010-03-30 12:17:00 +00002459 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002460 {
2461 png_color_16
2462 background;
2463
2464#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2465 if (mng_info->have_saved_bkgd_index)
2466 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002467#endif
glennrpfaa852b2010-03-30 12:17:00 +00002468 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2469 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002470
cristy3ed852e2009-09-05 21:47:34 +00002471 background.red=(png_uint_16)
2472 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002473
cristy3ed852e2009-09-05 21:47:34 +00002474 background.green=(png_uint_16)
2475 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002476
cristy3ed852e2009-09-05 21:47:34 +00002477 background.blue=(png_uint_16)
2478 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002479
glennrpc6c391a2011-04-27 02:23:56 +00002480 background.gray=(png_uint_16)
2481 mng_info->global_plte[background.index].green;
2482
cristy3ed852e2009-09-05 21:47:34 +00002483 png_set_bKGD(ping,ping_info,&background);
2484 }
2485#endif
2486 }
2487 else
2488 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2489 CoderError,"No global PLTE in file","`%s'",
2490 image_info->filename);
2491 }
2492 }
2493
glennrpbfd9e612011-04-22 14:02:20 +00002494#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002495 if (mng_info->have_global_bkgd &&
2496 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002497 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002498
glennrpfaa852b2010-03-30 12:17:00 +00002499 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002500 {
glennrpbfd9e612011-04-22 14:02:20 +00002501 unsigned int
2502 bkgd_scale;
2503
cristy3ed852e2009-09-05 21:47:34 +00002504 /*
2505 Set image background color.
2506 */
2507 if (logging != MagickFalse)
2508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2509 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002510
glennrpbfd9e612011-04-22 14:02:20 +00002511 /* Scale background components to 16-bit, then scale
2512 * to quantum depth
2513 */
2514 if (logging != MagickFalse)
2515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2516 " raw ping_background=(%d,%d,%d).",ping_background->red,
2517 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002518
glennrpbfd9e612011-04-22 14:02:20 +00002519 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002520
glennrpbfd9e612011-04-22 14:02:20 +00002521 if (ping_bit_depth == 1)
2522 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002523
glennrpbfd9e612011-04-22 14:02:20 +00002524 else if (ping_bit_depth == 2)
2525 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002526
glennrpbfd9e612011-04-22 14:02:20 +00002527 else if (ping_bit_depth == 4)
2528 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002529
glennrpbfd9e612011-04-22 14:02:20 +00002530 if (ping_bit_depth <= 8)
2531 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002532
glennrpbfd9e612011-04-22 14:02:20 +00002533 ping_background->red *= bkgd_scale;
2534 ping_background->green *= bkgd_scale;
2535 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002536
glennrpbfd9e612011-04-22 14:02:20 +00002537 if (logging != MagickFalse)
2538 {
glennrp2cbb4482010-06-02 04:37:24 +00002539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2540 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002541
glennrp2cbb4482010-06-02 04:37:24 +00002542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2543 " ping_background=(%d,%d,%d).",ping_background->red,
2544 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002545 }
glennrp2cbb4482010-06-02 04:37:24 +00002546
glennrpbfd9e612011-04-22 14:02:20 +00002547 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002548 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002549
glennrpbfd9e612011-04-22 14:02:20 +00002550 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002551 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002552
glennrpbfd9e612011-04-22 14:02:20 +00002553 image->background_color.blue=
2554 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002555
glennrpbfd9e612011-04-22 14:02:20 +00002556 image->background_color.opacity=OpaqueOpacity;
glennrp2cbb4482010-06-02 04:37:24 +00002557
glennrpbfd9e612011-04-22 14:02:20 +00002558 if (logging != MagickFalse)
2559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2560 " image->background_color=(%.20g,%.20g,%.20g).",
2561 (double) image->background_color.red,
2562 (double) image->background_color.green,
2563 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002564 }
glennrpbfd9e612011-04-22 14:02:20 +00002565#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002566
glennrpfaa852b2010-03-30 12:17:00 +00002567 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002568 {
2569 /*
glennrpa6a06632011-01-19 15:15:34 +00002570 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002571 */
2572 int
2573 max_sample;
2574
cristy35ef8242010-06-03 16:24:13 +00002575 size_t
2576 one=1;
2577
cristy3ed852e2009-09-05 21:47:34 +00002578 if (logging != MagickFalse)
2579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2580 " Reading PNG tRNS chunk.");
2581
cristyf9cca6a2010-06-04 23:49:28 +00002582 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002583
glennrpfaa852b2010-03-30 12:17:00 +00002584 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2585 (int)ping_trans_color->gray > max_sample) ||
2586 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2587 ((int)ping_trans_color->red > max_sample ||
2588 (int)ping_trans_color->green > max_sample ||
2589 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002590 {
2591 if (logging != MagickFalse)
2592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2593 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002594 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002595 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002596 image->matte=MagickFalse;
2597 }
2598 else
2599 {
glennrpa6a06632011-01-19 15:15:34 +00002600 int
2601 scale_to_short;
2602
2603 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2604
2605 /* Scale transparent_color to short */
2606 transparent_color.red= scale_to_short*ping_trans_color->red;
2607 transparent_color.green= scale_to_short*ping_trans_color->green;
2608 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2609 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002610
glennrpfaa852b2010-03-30 12:17:00 +00002611 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002612 {
glennrp0f111982010-07-07 20:18:33 +00002613 if (logging != MagickFalse)
2614 {
2615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2616 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002617
glennrp0f111982010-07-07 20:18:33 +00002618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2619 " scaled graylevel is %d.",transparent_color.opacity);
2620 }
cristy3ed852e2009-09-05 21:47:34 +00002621 transparent_color.red=transparent_color.opacity;
2622 transparent_color.green=transparent_color.opacity;
2623 transparent_color.blue=transparent_color.opacity;
2624 }
2625 }
2626 }
2627#if defined(PNG_READ_sBIT_SUPPORTED)
2628 if (mng_info->have_global_sbit)
2629 {
glennrpfaa852b2010-03-30 12:17:00 +00002630 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002631 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2632 }
2633#endif
2634 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002635
cristy3ed852e2009-09-05 21:47:34 +00002636 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002637
2638 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2639
cristy3ed852e2009-09-05 21:47:34 +00002640 /*
2641 Initialize image structure.
2642 */
2643 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002644 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002645 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002646 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002647 if (mng_info->mng_type == 0)
2648 {
glennrpfaa852b2010-03-30 12:17:00 +00002649 mng_info->mng_width=ping_width;
2650 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002651 mng_info->frame=mng_info->image_box;
2652 mng_info->clip=mng_info->image_box;
2653 }
glennrp0fe50b42010-11-16 03:52:51 +00002654
cristy3ed852e2009-09-05 21:47:34 +00002655 else
2656 {
2657 image->page.y=mng_info->y_off[mng_info->object_id];
2658 }
glennrp0fe50b42010-11-16 03:52:51 +00002659
cristy3ed852e2009-09-05 21:47:34 +00002660 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002661 image->columns=ping_width;
2662 image->rows=ping_height;
2663 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002664 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002665 {
cristybefe4d22010-06-07 01:18:58 +00002666 size_t
2667 one;
2668
cristy3ed852e2009-09-05 21:47:34 +00002669 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002670 one=1;
2671 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002672#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2673 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002674 image->colors=256;
2675#else
2676 if (image->colors > 65536L)
2677 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002678#endif
glennrpfaa852b2010-03-30 12:17:00 +00002679 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002680 {
2681 int
2682 number_colors;
2683
2684 png_colorp
2685 palette;
2686
2687 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002688 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002689
cristy3ed852e2009-09-05 21:47:34 +00002690 if (logging != MagickFalse)
2691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2692 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2693 }
2694 }
2695
2696 if (image->storage_class == PseudoClass)
2697 {
2698 /*
2699 Initialize image colormap.
2700 */
2701 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2702 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002703
glennrpfaa852b2010-03-30 12:17:00 +00002704 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002705 {
2706 int
2707 number_colors;
2708
2709 png_colorp
2710 palette;
2711
2712 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002713
glennrp6af6cf12011-04-22 13:05:16 +00002714 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002715 {
2716 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2717 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2718 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2719 }
glennrp6af6cf12011-04-22 13:05:16 +00002720
glennrp67b9c1a2011-04-22 18:47:36 +00002721 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002722 {
2723 image->colormap[i].red=0;
2724 image->colormap[i].green=0;
2725 image->colormap[i].blue=0;
2726 }
cristy3ed852e2009-09-05 21:47:34 +00002727 }
glennrp0fe50b42010-11-16 03:52:51 +00002728
cristy3ed852e2009-09-05 21:47:34 +00002729 else
2730 {
cristybb503372010-05-27 20:51:26 +00002731 size_t
cristy3ed852e2009-09-05 21:47:34 +00002732 scale;
2733
glennrpfaa852b2010-03-30 12:17:00 +00002734 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002735
cristy3ed852e2009-09-05 21:47:34 +00002736 if (scale < 1)
2737 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002738
cristybb503372010-05-27 20:51:26 +00002739 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002740 {
2741 image->colormap[i].red=(Quantum) (i*scale);
2742 image->colormap[i].green=(Quantum) (i*scale);
2743 image->colormap[i].blue=(Quantum) (i*scale);
2744 }
2745 }
2746 }
glennrp147bc912011-03-30 18:47:21 +00002747
glennrpcb395ac2011-03-30 19:50:23 +00002748 /* Set some properties for reporting by "identify" */
2749 {
glennrp147bc912011-03-30 18:47:21 +00002750 char
2751 msg[MaxTextExtent];
2752
2753 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2754 ping_interlace_method in value */
2755
cristy3b6fd2e2011-05-20 12:53:50 +00002756 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002757 "%d, %d",(int) ping_width, (int) ping_height);
2758 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002759
cristy3b6fd2e2011-05-20 12:53:50 +00002760 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
glennrp147bc912011-03-30 18:47:21 +00002761 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2762
cristy3b6fd2e2011-05-20 12:53:50 +00002763 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
glennrp147bc912011-03-30 18:47:21 +00002764 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2765
cristy3b6fd2e2011-05-20 12:53:50 +00002766 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002767 (int) ping_interlace_method);
2768 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002769 }
glennrp147bc912011-03-30 18:47:21 +00002770
cristy3ed852e2009-09-05 21:47:34 +00002771 /*
2772 Read image scanlines.
2773 */
2774 if (image->delay != 0)
2775 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002776
glennrp0ca69b12010-07-26 01:57:52 +00002777 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002778 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2779 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002780 {
2781 if (logging != MagickFalse)
2782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002783 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002784 mng_info->scenes_found-1);
2785 png_destroy_read_struct(&ping,&ping_info,&end_info);
2786#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002787 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002788#endif
2789 if (logging != MagickFalse)
2790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2791 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002792
cristy3ed852e2009-09-05 21:47:34 +00002793 return(image);
2794 }
glennrp0fe50b42010-11-16 03:52:51 +00002795
cristy3ed852e2009-09-05 21:47:34 +00002796 if (logging != MagickFalse)
2797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2798 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002799
cristy3ed852e2009-09-05 21:47:34 +00002800 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002801 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2802 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002803
cristy3ed852e2009-09-05 21:47:34 +00002804 else
glennrpcf002022011-01-30 02:38:15 +00002805 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2806 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002807
glennrpcf002022011-01-30 02:38:15 +00002808 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002809 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002810
cristy3ed852e2009-09-05 21:47:34 +00002811 if (logging != MagickFalse)
2812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2813 " Converting PNG pixels to pixel packets");
2814 /*
2815 Convert PNG pixels to pixel packets.
2816 */
glennrpfaa852b2010-03-30 12:17:00 +00002817 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002818 {
2819 /*
2820 PNG image is corrupt.
2821 */
2822 png_destroy_read_struct(&ping,&ping_info,&end_info);
2823#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002824 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002825#endif
2826 if (quantum_info != (QuantumInfo *) NULL)
2827 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002828
glennrpcf002022011-01-30 02:38:15 +00002829 if (ping_pixels != (unsigned char *) NULL)
2830 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002831
cristy3ed852e2009-09-05 21:47:34 +00002832 if (logging != MagickFalse)
2833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2834 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002835
cristy3ed852e2009-09-05 21:47:34 +00002836 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002837 {
2838 InheritException(exception,&image->exception);
2839 image->columns=0;
2840 }
glennrp0fe50b42010-11-16 03:52:51 +00002841
cristy3ed852e2009-09-05 21:47:34 +00002842 return(GetFirstImageInList(image));
2843 }
glennrp0fe50b42010-11-16 03:52:51 +00002844
cristyed552522009-10-16 14:04:35 +00002845 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002846
cristyed552522009-10-16 14:04:35 +00002847 if (quantum_info == (QuantumInfo *) NULL)
2848 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002849
glennrpc8cbc5d2011-01-01 00:12:34 +00002850 {
2851
2852 MagickBooleanType
2853 found_transparent_pixel;
2854
2855 found_transparent_pixel=MagickFalse;
2856
cristy3ed852e2009-09-05 21:47:34 +00002857 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002858 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002859 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002860 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002861 /*
2862 Convert image to DirectClass pixel packets.
2863 */
glennrp67b9c1a2011-04-22 18:47:36 +00002864#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2865 int
2866 depth;
2867
2868 depth=(ssize_t) ping_bit_depth;
2869#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002870 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2871 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2872 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2873 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002874
glennrpc8cbc5d2011-01-01 00:12:34 +00002875 for (y=0; y < (ssize_t) image->rows; y++)
2876 {
2877 if (num_passes > 1)
2878 row_offset=ping_rowbytes*y;
2879
2880 else
2881 row_offset=0;
2882
glennrpcf002022011-01-30 02:38:15 +00002883 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002884 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2885
2886 if (q == (PixelPacket *) NULL)
2887 break;
2888
glennrpc8cbc5d2011-01-01 00:12:34 +00002889 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2890 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002891 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002892
2893 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2894 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002895 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002896
2897 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2898 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002899 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002900
2901 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2902 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002903 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002904
2905 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2906 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002907 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002908
glennrpc8cbc5d2011-01-01 00:12:34 +00002909 if (found_transparent_pixel == MagickFalse)
2910 {
2911 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002912 if (y== 0 && logging != MagickFalse)
2913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2914 " Looking for cheap transparent pixel");
2915
glennrpc8cbc5d2011-01-01 00:12:34 +00002916 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2917 {
glennrp5aa37f62011-01-02 03:07:57 +00002918 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2919 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristyef618312011-06-25 12:26:44 +00002920 (GetPixelOpacity(q) != OpaqueOpacity))
glennrpc8cbc5d2011-01-01 00:12:34 +00002921 {
glennrpa6a06632011-01-19 15:15:34 +00002922 if (logging != MagickFalse)
2923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2924 " ...got one.");
2925
glennrpc8cbc5d2011-01-01 00:12:34 +00002926 found_transparent_pixel = MagickTrue;
2927 break;
2928 }
glennrp4f25bd02011-01-01 18:51:28 +00002929 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2930 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
cristyef618312011-06-25 12:26:44 +00002931 (ScaleQuantumToShort(GetPixelRed(q))
glennrp8b698592011-04-26 03:38:21 +00002932 == transparent_color.red &&
cristyef618312011-06-25 12:26:44 +00002933 ScaleQuantumToShort(GetPixelGreen(q))
glennrp8b698592011-04-26 03:38:21 +00002934 == transparent_color.green &&
cristyef618312011-06-25 12:26:44 +00002935 ScaleQuantumToShort(GetPixelBlue(q))
glennrp8b698592011-04-26 03:38:21 +00002936 == transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002937 {
glennrpa6a06632011-01-19 15:15:34 +00002938 if (logging != MagickFalse)
2939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2940 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002941 found_transparent_pixel = MagickTrue;
2942 break;
2943 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002944 q++;
2945 }
2946 }
2947
2948 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2949 {
2950 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2951 image->rows);
2952
2953 if (status == MagickFalse)
2954 break;
2955 }
2956 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2957 break;
2958 }
2959
2960 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2961 {
2962 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002963 if (status == MagickFalse)
2964 break;
2965 }
cristy3ed852e2009-09-05 21:47:34 +00002966 }
cristy3ed852e2009-09-05 21:47:34 +00002967 }
glennrp0fe50b42010-11-16 03:52:51 +00002968
cristy3ed852e2009-09-05 21:47:34 +00002969 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002970
cristy3ed852e2009-09-05 21:47:34 +00002971 for (pass=0; pass < num_passes; pass++)
2972 {
2973 Quantum
2974 *quantum_scanline;
2975
2976 register Quantum
2977 *r;
2978
2979 /*
2980 Convert grayscale image to PseudoClass pixel packets.
2981 */
glennrpc17d96f2011-06-27 01:20:11 +00002982
2983 if (logging != MagickFalse)
2984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2985 " Converting grayscale pixels to pixel packets");
glennrpfaa852b2010-03-30 12:17:00 +00002986 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002987 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002988
cristy3ed852e2009-09-05 21:47:34 +00002989 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2990 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00002991
cristy3ed852e2009-09-05 21:47:34 +00002992 if (quantum_scanline == (Quantum *) NULL)
2993 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002994
cristybb503372010-05-27 20:51:26 +00002995 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002996 {
2997 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002998 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00002999
cristy3ed852e2009-09-05 21:47:34 +00003000 else
3001 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003002
glennrpcf002022011-01-30 02:38:15 +00003003 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003004 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003005
cristy3ed852e2009-09-05 21:47:34 +00003006 if (q == (PixelPacket *) NULL)
3007 break;
glennrp0fe50b42010-11-16 03:52:51 +00003008
cristy5c6f7892010-05-05 22:53:29 +00003009 indexes=GetAuthenticIndexQueue(image);
glennrpcf002022011-01-30 02:38:15 +00003010 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003011 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003012
glennrpfaa852b2010-03-30 12:17:00 +00003013 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003014 {
3015 case 1:
3016 {
cristybb503372010-05-27 20:51:26 +00003017 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003018 bit;
3019
cristybb503372010-05-27 20:51:26 +00003020 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003021 {
3022 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003023 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003024 p++;
3025 }
glennrp0fe50b42010-11-16 03:52:51 +00003026
cristy3ed852e2009-09-05 21:47:34 +00003027 if ((image->columns % 8) != 0)
3028 {
cristybb503372010-05-27 20:51:26 +00003029 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003030 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003031 }
glennrp0fe50b42010-11-16 03:52:51 +00003032
cristy3ed852e2009-09-05 21:47:34 +00003033 break;
3034 }
glennrp47b9dd52010-11-24 18:12:06 +00003035
cristy3ed852e2009-09-05 21:47:34 +00003036 case 2:
3037 {
cristybb503372010-05-27 20:51:26 +00003038 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003039 {
glennrpa18d5bc2011-04-23 14:51:34 +00003040 *r++=(*p >> 6) & 0x03;
3041 *r++=(*p >> 4) & 0x03;
3042 *r++=(*p >> 2) & 0x03;
3043 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003044 }
glennrp0fe50b42010-11-16 03:52:51 +00003045
cristy3ed852e2009-09-05 21:47:34 +00003046 if ((image->columns % 4) != 0)
3047 {
cristybb503372010-05-27 20:51:26 +00003048 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003049 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003050 }
glennrp0fe50b42010-11-16 03:52:51 +00003051
cristy3ed852e2009-09-05 21:47:34 +00003052 break;
3053 }
glennrp47b9dd52010-11-24 18:12:06 +00003054
cristy3ed852e2009-09-05 21:47:34 +00003055 case 4:
3056 {
cristybb503372010-05-27 20:51:26 +00003057 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003058 {
glennrpa18d5bc2011-04-23 14:51:34 +00003059 *r++=(*p >> 4) & 0x0f;
3060 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003061 }
glennrp0fe50b42010-11-16 03:52:51 +00003062
cristy3ed852e2009-09-05 21:47:34 +00003063 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003064 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003065
cristy3ed852e2009-09-05 21:47:34 +00003066 break;
3067 }
glennrp47b9dd52010-11-24 18:12:06 +00003068
cristy3ed852e2009-09-05 21:47:34 +00003069 case 8:
3070 {
glennrpfaa852b2010-03-30 12:17:00 +00003071 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003072 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003073 {
glennrpa18d5bc2011-04-23 14:51:34 +00003074 *r++=*p++;
cristy3ed852e2009-09-05 21:47:34 +00003075 /* In image.h, OpaqueOpacity is 0
3076 * TransparentOpacity is QuantumRange
3077 * In a PNG datastream, Opaque is QuantumRange
3078 * and Transparent is 0.
3079 */
cristyef618312011-06-25 12:26:44 +00003080 SetPixelAlpha(q,ScaleCharToQuantum((unsigned char)
cristy34d83b22011-05-30 00:41:18 +00003081 *p++));
cristyef618312011-06-25 12:26:44 +00003082 if (GetPixelOpacity(q) != OpaqueOpacity)
glennrp0b206f52011-01-07 04:55:32 +00003083 found_transparent_pixel = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00003084 q++;
3085 }
glennrp0fe50b42010-11-16 03:52:51 +00003086
cristy3ed852e2009-09-05 21:47:34 +00003087 else
cristybb503372010-05-27 20:51:26 +00003088 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003089 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003090
cristy3ed852e2009-09-05 21:47:34 +00003091 break;
3092 }
glennrp47b9dd52010-11-24 18:12:06 +00003093
cristy3ed852e2009-09-05 21:47:34 +00003094 case 16:
3095 {
cristybb503372010-05-27 20:51:26 +00003096 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003097 {
glennrpc17d96f2011-06-27 01:20:11 +00003098#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003099 size_t
3100 quantum;
3101
3102 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003103 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003104
3105 else
glennrpc17d96f2011-06-27 01:20:11 +00003106 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003107
glennrp58f77c72011-04-23 14:09:09 +00003108 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003109
3110 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003111 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003112
3113 if (ping_color_type == 4)
3114 {
glennrpc17d96f2011-06-27 01:20:11 +00003115 if (image->colors > 256)
3116 quantum=((*p++) << 8);
3117 else
3118 quantum=0;
3119
glennrp58f77c72011-04-23 14:09:09 +00003120 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003121 SetPixelAlpha(q,ScaleShortToQuantum(quantum));
cristyef618312011-06-25 12:26:44 +00003122 if (GetPixelOpacity(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00003123 found_transparent_pixel = MagickTrue;
3124 q++;
3125 }
glennrp58f77c72011-04-23 14:09:09 +00003126
3127#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3128 *r++=(*p++);
3129 p++; /* strip low byte */
3130
3131 if (ping_color_type == 4)
3132 {
cristyef618312011-06-25 12:26:44 +00003133 SetPixelAlpha(q,*p++);
3134 if (GetPixelOpacity(q) != OpaqueOpacity)
glennrp9d0ea4d2011-04-22 18:35:57 +00003135 found_transparent_pixel = MagickTrue;
3136 p++;
3137 q++;
3138 }
cristy3ed852e2009-09-05 21:47:34 +00003139#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003140 }
glennrp47b9dd52010-11-24 18:12:06 +00003141
cristy3ed852e2009-09-05 21:47:34 +00003142 break;
3143 }
glennrp47b9dd52010-11-24 18:12:06 +00003144
cristy3ed852e2009-09-05 21:47:34 +00003145 default:
3146 break;
3147 }
glennrp3faa9a32011-04-23 14:00:25 +00003148
cristy3ed852e2009-09-05 21:47:34 +00003149 /*
3150 Transfer image scanline.
3151 */
3152 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003153
cristybb503372010-05-27 20:51:26 +00003154 for (x=0; x < (ssize_t) image->columns; x++)
cristyef618312011-06-25 12:26:44 +00003155 SetPixelIndex(indexes+x,*r++);
glennrp0fe50b42010-11-16 03:52:51 +00003156
cristy3ed852e2009-09-05 21:47:34 +00003157 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3158 break;
glennrp0fe50b42010-11-16 03:52:51 +00003159
cristy7a287bf2010-02-14 02:18:09 +00003160 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3161 {
cristycee97112010-05-28 00:44:52 +00003162 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003163 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003164
cristy7a287bf2010-02-14 02:18:09 +00003165 if (status == MagickFalse)
3166 break;
3167 }
cristy3ed852e2009-09-05 21:47:34 +00003168 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003169
cristy7a287bf2010-02-14 02:18:09 +00003170 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003171 {
3172 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003173
cristy3ed852e2009-09-05 21:47:34 +00003174 if (status == MagickFalse)
3175 break;
3176 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003177
cristy3ed852e2009-09-05 21:47:34 +00003178 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3179 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003180
3181 image->matte=found_transparent_pixel;
3182
3183 if (logging != MagickFalse)
3184 {
3185 if (found_transparent_pixel != MagickFalse)
3186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3187 " Found transparent pixel");
3188 else
glennrp5aa37f62011-01-02 03:07:57 +00003189 {
3190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3191 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003192
glennrp5aa37f62011-01-02 03:07:57 +00003193 ping_color_type&=0x03;
3194 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003195 }
3196 }
3197
cristyb32b90a2009-09-07 21:45:48 +00003198 if (quantum_info != (QuantumInfo *) NULL)
3199 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003200
cristy5c6f7892010-05-05 22:53:29 +00003201 if (image->storage_class == PseudoClass)
3202 {
cristyaeb2cbc2010-05-07 13:28:58 +00003203 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003204 matte;
3205
3206 matte=image->matte;
3207 image->matte=MagickFalse;
3208 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00003209 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003210 }
glennrp47b9dd52010-11-24 18:12:06 +00003211
glennrp4eb39312011-03-30 21:34:55 +00003212 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003213
3214 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003215 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003216 {
3217 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003218 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003219 image->colors=2;
3220 (void) SetImageBackgroundColor(image);
3221#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003222 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003223#endif
3224 if (logging != MagickFalse)
3225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3226 " exit ReadOnePNGImage() early.");
3227 return(image);
3228 }
glennrp47b9dd52010-11-24 18:12:06 +00003229
glennrpfaa852b2010-03-30 12:17:00 +00003230 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003231 {
3232 ClassType
3233 storage_class;
3234
3235 /*
3236 Image has a transparent background.
3237 */
3238 storage_class=image->storage_class;
3239 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003240
glennrp3c218112010-11-27 15:31:26 +00003241/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003242
glennrp0fe50b42010-11-16 03:52:51 +00003243 if (storage_class == PseudoClass)
3244 {
3245 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003246 {
glennrp0fe50b42010-11-16 03:52:51 +00003247 for (x=0; x < ping_num_trans; x++)
3248 {
3249 image->colormap[x].opacity =
3250 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
3251 }
glennrpc11cf6a2010-03-20 16:46:19 +00003252 }
glennrp47b9dd52010-11-24 18:12:06 +00003253
glennrp0fe50b42010-11-16 03:52:51 +00003254 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3255 {
3256 for (x=0; x < (int) image->colors; x++)
3257 {
3258 if (ScaleQuantumToShort(image->colormap[x].red) ==
3259 transparent_color.opacity)
3260 {
3261 image->colormap[x].opacity = (Quantum) TransparentOpacity;
3262 }
3263 }
3264 }
3265 (void) SyncImage(image);
3266 }
glennrp47b9dd52010-11-24 18:12:06 +00003267
glennrpa6a06632011-01-19 15:15:34 +00003268#if 1 /* Should have already been done above, but glennrp problem P10
3269 * needs this.
3270 */
glennrp0fe50b42010-11-16 03:52:51 +00003271 else
3272 {
3273 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003274 {
glennrp0fe50b42010-11-16 03:52:51 +00003275 image->storage_class=storage_class;
3276 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3277
3278 if (q == (PixelPacket *) NULL)
3279 break;
3280
3281 indexes=GetAuthenticIndexQueue(image);
3282
glennrpa6a06632011-01-19 15:15:34 +00003283 /* Caution: on a Q8 build, this does not distinguish between
3284 * 16-bit colors that differ only in the low byte
3285 */
glennrp0fe50b42010-11-16 03:52:51 +00003286 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3287 {
cristyef618312011-06-25 12:26:44 +00003288 if (ScaleQuantumToShort(GetPixelRed(q))
glennrp8b698592011-04-26 03:38:21 +00003289 == transparent_color.red &&
cristyef618312011-06-25 12:26:44 +00003290 ScaleQuantumToShort(GetPixelGreen(q))
glennrp8b698592011-04-26 03:38:21 +00003291 == transparent_color.green &&
cristyef618312011-06-25 12:26:44 +00003292 ScaleQuantumToShort(GetPixelBlue(q))
glennrp8b698592011-04-26 03:38:21 +00003293 == transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003294 {
cristyef618312011-06-25 12:26:44 +00003295 SetPixelOpacity(q,TransparentOpacity);
glennrp4f25bd02011-01-01 18:51:28 +00003296 }
glennrp0fe50b42010-11-16 03:52:51 +00003297
glennrp67b9c1a2011-04-22 18:47:36 +00003298#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003299 else
glennrp4f25bd02011-01-01 18:51:28 +00003300 {
cristyef618312011-06-25 12:26:44 +00003301 SetPixelOpacity(q)=(Quantum) OpaqueOpacity;
glennrp4f25bd02011-01-01 18:51:28 +00003302 }
glennrpa6a06632011-01-19 15:15:34 +00003303#endif
glennrp0fe50b42010-11-16 03:52:51 +00003304
3305 q++;
3306 }
3307
3308 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3309 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003310 }
glennrp0fe50b42010-11-16 03:52:51 +00003311 }
glennrpa6a06632011-01-19 15:15:34 +00003312#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003313
cristy3ed852e2009-09-05 21:47:34 +00003314 image->storage_class=DirectClass;
3315 }
glennrp3c218112010-11-27 15:31:26 +00003316
cristyb40fc462010-08-08 00:49:49 +00003317 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3318 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3319 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003320
cristyeb3b22a2011-03-31 20:16:11 +00003321 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003322 {
3323 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003324 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3325 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003326 else
glennrpa0ed0092011-04-18 16:36:29 +00003327 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3328 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003329
glennrp4eb39312011-03-30 21:34:55 +00003330 if (status != MagickFalse)
3331 for (i=0; i < (ssize_t) num_text; i++)
3332 {
3333 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003334
glennrp4eb39312011-03-30 21:34:55 +00003335 if (logging != MagickFalse)
3336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3337 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003338
glennrp4eb39312011-03-30 21:34:55 +00003339 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003340 {
glennrp4eb39312011-03-30 21:34:55 +00003341 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
3342 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003343 }
glennrp0fe50b42010-11-16 03:52:51 +00003344
glennrp4eb39312011-03-30 21:34:55 +00003345 else
3346 {
3347 char
3348 *value;
3349
3350 length=text[i].text_length;
3351 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3352 sizeof(*value));
3353 if (value == (char *) NULL)
3354 {
3355 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3356 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3357 image->filename);
3358 break;
3359 }
3360 *value='\0';
3361 (void) ConcatenateMagickString(value,text[i].text,length+2);
3362
3363 /* Don't save "density" or "units" property if we have a pHYs
3364 * chunk
3365 */
3366 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3367 (LocaleCompare(text[i].key,"density") != 0 &&
3368 LocaleCompare(text[i].key,"units") != 0))
3369 (void) SetImageProperty(image,text[i].key,value);
3370
3371 if (logging != MagickFalse)
3372 {
3373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3374 " length: %lu",(unsigned long) length);
3375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3376 " Keyword: %s",text[i].key);
3377 }
3378
3379 value=DestroyString(value);
3380 }
3381 }
3382 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003383 }
glennrp3c218112010-11-27 15:31:26 +00003384
cristy3ed852e2009-09-05 21:47:34 +00003385#ifdef MNG_OBJECT_BUFFERS
3386 /*
3387 Store the object if necessary.
3388 */
3389 if (object_id && !mng_info->frozen[object_id])
3390 {
3391 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3392 {
3393 /*
3394 create a new object buffer.
3395 */
3396 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003397 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003398
cristy3ed852e2009-09-05 21:47:34 +00003399 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3400 {
3401 mng_info->ob[object_id]->image=(Image *) NULL;
3402 mng_info->ob[object_id]->reference_count=1;
3403 }
3404 }
glennrp47b9dd52010-11-24 18:12:06 +00003405
cristy3ed852e2009-09-05 21:47:34 +00003406 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3407 mng_info->ob[object_id]->frozen)
3408 {
3409 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3410 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3411 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3412 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003413
cristy3ed852e2009-09-05 21:47:34 +00003414 if (mng_info->ob[object_id]->frozen)
3415 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3416 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3417 "`%s'",image->filename);
3418 }
glennrp0fe50b42010-11-16 03:52:51 +00003419
cristy3ed852e2009-09-05 21:47:34 +00003420 else
3421 {
cristy3ed852e2009-09-05 21:47:34 +00003422
3423 if (mng_info->ob[object_id]->image != (Image *) NULL)
3424 mng_info->ob[object_id]->image=DestroyImage
3425 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003426
cristy3ed852e2009-09-05 21:47:34 +00003427 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3428 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003429
cristy3ed852e2009-09-05 21:47:34 +00003430 if (mng_info->ob[object_id]->image != (Image *) NULL)
3431 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003432
cristy3ed852e2009-09-05 21:47:34 +00003433 else
3434 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3435 ResourceLimitError,"Cloning image for object buffer failed",
3436 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003437
glennrpfaa852b2010-03-30 12:17:00 +00003438 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003439 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003440
glennrpfaa852b2010-03-30 12:17:00 +00003441 mng_info->ob[object_id]->width=ping_width;
3442 mng_info->ob[object_id]->height=ping_height;
3443 mng_info->ob[object_id]->color_type=ping_color_type;
3444 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3445 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3446 mng_info->ob[object_id]->compression_method=
3447 ping_compression_method;
3448 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003449
glennrpfaa852b2010-03-30 12:17:00 +00003450 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003451 {
3452 int
3453 number_colors;
3454
3455 png_colorp
3456 plte;
3457
3458 /*
3459 Copy the PLTE to the object buffer.
3460 */
3461 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3462 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003463
cristy3ed852e2009-09-05 21:47:34 +00003464 for (i=0; i < number_colors; i++)
3465 {
3466 mng_info->ob[object_id]->plte[i]=plte[i];
3467 }
3468 }
glennrp47b9dd52010-11-24 18:12:06 +00003469
cristy3ed852e2009-09-05 21:47:34 +00003470 else
3471 mng_info->ob[object_id]->plte_length=0;
3472 }
3473 }
3474#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003475
3476 /* Set image->matte to MagickTrue if the input colortype supports
3477 * alpha or if a valid tRNS chunk is present, no matter whether there
3478 * is actual transparency present.
3479 */
3480 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3481 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3482 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3483 MagickTrue : MagickFalse;
3484
glennrpcb395ac2011-03-30 19:50:23 +00003485 /* Set more properties for identify to retrieve */
3486 {
3487 char
3488 msg[MaxTextExtent];
3489
glennrp4eb39312011-03-30 21:34:55 +00003490 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003491 {
3492 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003493 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003494 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003495 (void) SetImageProperty(image,"PNG:text ",msg);
3496 }
3497
3498 if (num_raw_profiles != 0)
3499 {
cristy3b6fd2e2011-05-20 12:53:50 +00003500 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003501 "%d were found", num_raw_profiles);
3502 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3503 }
3504
glennrpcb395ac2011-03-30 19:50:23 +00003505 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003506 {
cristy3b6fd2e2011-05-20 12:53:50 +00003507 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003508 "chunk was found (see Chromaticity, above)");
3509 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3510 }
glennrpcb395ac2011-03-30 19:50:23 +00003511
3512 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003513 {
cristy3b6fd2e2011-05-20 12:53:50 +00003514 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003515 "chunk was found (see Background color, above)");
3516 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3517 }
3518
cristy3b6fd2e2011-05-20 12:53:50 +00003519 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003520 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003521
3522 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3523 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3524
glennrpcb395ac2011-03-30 19:50:23 +00003525 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3526 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003527
3528#if defined(PNG_sRGB_SUPPORTED)
3529 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3530 {
cristy3b6fd2e2011-05-20 12:53:50 +00003531 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003532 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003533 (int) intent);
3534 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3535 }
3536#endif
3537
3538 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3539 {
cristy3b6fd2e2011-05-20 12:53:50 +00003540 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003541 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003542 file_gamma);
3543 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3544 }
3545
3546#if defined(PNG_pHYs_SUPPORTED)
3547 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3548 {
cristy3b6fd2e2011-05-20 12:53:50 +00003549 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003550 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003551 (double) x_resolution,(double) y_resolution, unit_type);
3552 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3553 }
3554#endif
3555
3556#if defined(PNG_oFFs_SUPPORTED)
3557 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3558 {
cristy3b6fd2e2011-05-20 12:53:50 +00003559 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003560 (double) image->page.x,(double) image->page.y);
3561 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3562 }
3563#endif
3564
glennrp07523c72011-03-31 18:12:10 +00003565 if ((image->page.width != 0 && image->page.width != image->columns) ||
3566 (image->page.height != 0 && image->page.height != image->rows))
3567 {
cristy3b6fd2e2011-05-20 12:53:50 +00003568 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003569 "width=%.20g, height=%.20g",
3570 (double) image->page.width,(double) image->page.height);
3571 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3572 }
glennrpcb395ac2011-03-30 19:50:23 +00003573 }
3574
cristy3ed852e2009-09-05 21:47:34 +00003575 /*
3576 Relinquish resources.
3577 */
3578 png_destroy_read_struct(&ping,&ping_info,&end_info);
3579
glennrpcf002022011-01-30 02:38:15 +00003580 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003581#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003582 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003583#endif
3584
3585 if (logging != MagickFalse)
3586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3587 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003588
cristy3ed852e2009-09-05 21:47:34 +00003589 return(image);
3590
3591/* end of reading one PNG image */
3592}
3593
3594static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3595{
3596 Image
3597 *image,
3598 *previous;
3599
3600 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003601 have_mng_structure,
3602 logging,
cristy3ed852e2009-09-05 21:47:34 +00003603 status;
3604
3605 MngInfo
3606 *mng_info;
3607
3608 char
3609 magic_number[MaxTextExtent];
3610
cristy3ed852e2009-09-05 21:47:34 +00003611 ssize_t
3612 count;
3613
3614 /*
3615 Open image file.
3616 */
3617 assert(image_info != (const ImageInfo *) NULL);
3618 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003619
cristy3ed852e2009-09-05 21:47:34 +00003620 if (image_info->debug != MagickFalse)
3621 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3622 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003623
cristy3ed852e2009-09-05 21:47:34 +00003624 assert(exception != (ExceptionInfo *) NULL);
3625 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003626 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003627 image=AcquireImage(image_info);
3628 mng_info=(MngInfo *) NULL;
3629 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003630
cristy3ed852e2009-09-05 21:47:34 +00003631 if (status == MagickFalse)
3632 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003633
cristy3ed852e2009-09-05 21:47:34 +00003634 /*
3635 Verify PNG signature.
3636 */
3637 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003638
glennrpdde35db2011-02-21 12:06:32 +00003639 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003640 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003641
cristy3ed852e2009-09-05 21:47:34 +00003642 /*
3643 Allocate a MngInfo structure.
3644 */
3645 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003646 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003647
cristy3ed852e2009-09-05 21:47:34 +00003648 if (mng_info == (MngInfo *) NULL)
3649 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003650
cristy3ed852e2009-09-05 21:47:34 +00003651 /*
3652 Initialize members of the MngInfo structure.
3653 */
3654 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3655 mng_info->image=image;
3656 have_mng_structure=MagickTrue;
3657
3658 previous=image;
3659 image=ReadOnePNGImage(mng_info,image_info,exception);
3660 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003661
cristy3ed852e2009-09-05 21:47:34 +00003662 if (image == (Image *) NULL)
3663 {
3664 if (previous != (Image *) NULL)
3665 {
3666 if (previous->signature != MagickSignature)
3667 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003668
cristy3ed852e2009-09-05 21:47:34 +00003669 (void) CloseBlob(previous);
3670 (void) DestroyImageList(previous);
3671 }
glennrp0fe50b42010-11-16 03:52:51 +00003672
cristy3ed852e2009-09-05 21:47:34 +00003673 if (logging != MagickFalse)
3674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3675 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003676
cristy3ed852e2009-09-05 21:47:34 +00003677 return((Image *) NULL);
3678 }
glennrp47b9dd52010-11-24 18:12:06 +00003679
cristy3ed852e2009-09-05 21:47:34 +00003680 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003681
cristy3ed852e2009-09-05 21:47:34 +00003682 if ((image->columns == 0) || (image->rows == 0))
3683 {
3684 if (logging != MagickFalse)
3685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3686 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003687
cristy3ed852e2009-09-05 21:47:34 +00003688 ThrowReaderException(CorruptImageError,"CorruptImage");
3689 }
glennrp47b9dd52010-11-24 18:12:06 +00003690
cristy3ed852e2009-09-05 21:47:34 +00003691 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3692 {
3693 (void) SetImageType(image,TrueColorType);
3694 image->matte=MagickFalse;
3695 }
glennrp0fe50b42010-11-16 03:52:51 +00003696
cristy3ed852e2009-09-05 21:47:34 +00003697 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3698 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003699
cristy3ed852e2009-09-05 21:47:34 +00003700 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3702 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3703 (double) image->page.width,(double) image->page.height,
3704 (double) image->page.x,(double) image->page.y);
3705
3706 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003708
cristy3ed852e2009-09-05 21:47:34 +00003709 return(image);
3710}
3711
3712
3713
3714#if defined(JNG_SUPPORTED)
3715/*
3716%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3717% %
3718% %
3719% %
3720% R e a d O n e J N G I m a g e %
3721% %
3722% %
3723% %
3724%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3725%
3726% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3727% (minus the 8-byte signature) and returns it. It allocates the memory
3728% necessary for the new Image structure and returns a pointer to the new
3729% image.
3730%
3731% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3732%
3733% The format of the ReadOneJNGImage method is:
3734%
3735% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3736% ExceptionInfo *exception)
3737%
3738% A description of each parameter follows:
3739%
3740% o mng_info: Specifies a pointer to a MngInfo structure.
3741%
3742% o image_info: the image info.
3743%
3744% o exception: return any errors or warnings in this structure.
3745%
3746*/
3747static Image *ReadOneJNGImage(MngInfo *mng_info,
3748 const ImageInfo *image_info, ExceptionInfo *exception)
3749{
3750 Image
3751 *alpha_image,
3752 *color_image,
3753 *image,
3754 *jng_image;
3755
3756 ImageInfo
3757 *alpha_image_info,
3758 *color_image_info;
3759
cristy4383ec82011-01-05 15:42:32 +00003760 MagickBooleanType
3761 logging;
3762
cristybb503372010-05-27 20:51:26 +00003763 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003764 y;
3765
3766 MagickBooleanType
3767 status;
3768
3769 png_uint_32
3770 jng_height,
3771 jng_width;
3772
3773 png_byte
3774 jng_color_type,
3775 jng_image_sample_depth,
3776 jng_image_compression_method,
3777 jng_image_interlace_method,
3778 jng_alpha_sample_depth,
3779 jng_alpha_compression_method,
3780 jng_alpha_filter_method,
3781 jng_alpha_interlace_method;
3782
3783 register const PixelPacket
3784 *s;
3785
cristybb503372010-05-27 20:51:26 +00003786 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003787 i,
3788 x;
3789
3790 register PixelPacket
3791 *q;
3792
3793 register unsigned char
3794 *p;
3795
3796 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003797 read_JSEP,
3798 reading_idat,
3799 skip_to_iend;
3800
cristybb503372010-05-27 20:51:26 +00003801 size_t
cristy3ed852e2009-09-05 21:47:34 +00003802 length;
3803
3804 jng_alpha_compression_method=0;
3805 jng_alpha_sample_depth=8;
3806 jng_color_type=0;
3807 jng_height=0;
3808 jng_width=0;
3809 alpha_image=(Image *) NULL;
3810 color_image=(Image *) NULL;
3811 alpha_image_info=(ImageInfo *) NULL;
3812 color_image_info=(ImageInfo *) NULL;
3813
3814 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003815 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003816
3817 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003818
cristy3ed852e2009-09-05 21:47:34 +00003819 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3820 {
3821 /*
3822 Allocate next image structure.
3823 */
3824 if (logging != MagickFalse)
3825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3826 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003827
cristy3ed852e2009-09-05 21:47:34 +00003828 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003829
cristy3ed852e2009-09-05 21:47:34 +00003830 if (GetNextImageInList(image) == (Image *) NULL)
3831 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003832
cristy3ed852e2009-09-05 21:47:34 +00003833 image=SyncNextImageInList(image);
3834 }
3835 mng_info->image=image;
3836
3837 /*
3838 Signature bytes have already been read.
3839 */
3840
3841 read_JSEP=MagickFalse;
3842 reading_idat=MagickFalse;
3843 skip_to_iend=MagickFalse;
3844 for (;;)
3845 {
3846 char
3847 type[MaxTextExtent];
3848
3849 unsigned char
3850 *chunk;
3851
3852 unsigned int
3853 count;
3854
3855 /*
3856 Read a new JNG chunk.
3857 */
3858 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3859 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003860
cristy3ed852e2009-09-05 21:47:34 +00003861 if (status == MagickFalse)
3862 break;
glennrp0fe50b42010-11-16 03:52:51 +00003863
cristy3ed852e2009-09-05 21:47:34 +00003864 type[0]='\0';
3865 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3866 length=ReadBlobMSBLong(image);
3867 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3868
3869 if (logging != MagickFalse)
3870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003871 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3872 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003873
3874 if (length > PNG_UINT_31_MAX || count == 0)
3875 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003876
cristy3ed852e2009-09-05 21:47:34 +00003877 p=NULL;
3878 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003879
cristy3ed852e2009-09-05 21:47:34 +00003880 if (length)
3881 {
3882 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003883
cristy3ed852e2009-09-05 21:47:34 +00003884 if (chunk == (unsigned char *) NULL)
3885 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003886
cristybb503372010-05-27 20:51:26 +00003887 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003888 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003889
cristy3ed852e2009-09-05 21:47:34 +00003890 p=chunk;
3891 }
glennrp47b9dd52010-11-24 18:12:06 +00003892
cristy3ed852e2009-09-05 21:47:34 +00003893 (void) ReadBlobMSBLong(image); /* read crc word */
3894
3895 if (skip_to_iend)
3896 {
3897 if (length)
3898 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003899
cristy3ed852e2009-09-05 21:47:34 +00003900 continue;
3901 }
3902
3903 if (memcmp(type,mng_JHDR,4) == 0)
3904 {
3905 if (length == 16)
3906 {
cristybb503372010-05-27 20:51:26 +00003907 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003908 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003909 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003910 (p[6] << 8) | p[7]);
3911 jng_color_type=p[8];
3912 jng_image_sample_depth=p[9];
3913 jng_image_compression_method=p[10];
3914 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003915
cristy3ed852e2009-09-05 21:47:34 +00003916 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3917 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003918
cristy3ed852e2009-09-05 21:47:34 +00003919 jng_alpha_sample_depth=p[12];
3920 jng_alpha_compression_method=p[13];
3921 jng_alpha_filter_method=p[14];
3922 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003923
cristy3ed852e2009-09-05 21:47:34 +00003924 if (logging != MagickFalse)
3925 {
3926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003927 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003928
cristy3ed852e2009-09-05 21:47:34 +00003929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003930 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003931
cristy3ed852e2009-09-05 21:47:34 +00003932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3933 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003934
cristy3ed852e2009-09-05 21:47:34 +00003935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3936 " jng_image_sample_depth: %3d",
3937 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003938
cristy3ed852e2009-09-05 21:47:34 +00003939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3940 " jng_image_compression_method:%3d",
3941 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003942
cristy3ed852e2009-09-05 21:47:34 +00003943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3944 " jng_image_interlace_method: %3d",
3945 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003946
cristy3ed852e2009-09-05 21:47:34 +00003947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3948 " jng_alpha_sample_depth: %3d",
3949 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003950
cristy3ed852e2009-09-05 21:47:34 +00003951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3952 " jng_alpha_compression_method:%3d",
3953 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003954
cristy3ed852e2009-09-05 21:47:34 +00003955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3956 " jng_alpha_filter_method: %3d",
3957 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003958
cristy3ed852e2009-09-05 21:47:34 +00003959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3960 " jng_alpha_interlace_method: %3d",
3961 jng_alpha_interlace_method);
3962 }
3963 }
glennrp47b9dd52010-11-24 18:12:06 +00003964
cristy3ed852e2009-09-05 21:47:34 +00003965 if (length)
3966 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 continue;
3969 }
3970
3971
3972 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3973 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3974 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3975 {
3976 /*
3977 o create color_image
3978 o open color_blob, attached to color_image
3979 o if (color type has alpha)
3980 open alpha_blob, attached to alpha_image
3981 */
3982
cristy73bd4a52010-10-05 11:24:23 +00003983 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003984
cristy3ed852e2009-09-05 21:47:34 +00003985 if (color_image_info == (ImageInfo *) NULL)
3986 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003987
cristy3ed852e2009-09-05 21:47:34 +00003988 GetImageInfo(color_image_info);
3989 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003990
cristy3ed852e2009-09-05 21:47:34 +00003991 if (color_image == (Image *) NULL)
3992 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3993
3994 if (logging != MagickFalse)
3995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3996 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003997
cristy3ed852e2009-09-05 21:47:34 +00003998 (void) AcquireUniqueFilename(color_image->filename);
3999 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4000 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004001
cristy3ed852e2009-09-05 21:47:34 +00004002 if (status == MagickFalse)
4003 return((Image *) NULL);
4004
4005 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4006 {
4007 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004008 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004009
cristy3ed852e2009-09-05 21:47:34 +00004010 if (alpha_image_info == (ImageInfo *) NULL)
4011 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004012
cristy3ed852e2009-09-05 21:47:34 +00004013 GetImageInfo(alpha_image_info);
4014 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00004015
cristy3ed852e2009-09-05 21:47:34 +00004016 if (alpha_image == (Image *) NULL)
4017 {
4018 alpha_image=DestroyImage(alpha_image);
4019 ThrowReaderException(ResourceLimitError,
4020 "MemoryAllocationFailed");
4021 }
glennrp0fe50b42010-11-16 03:52:51 +00004022
cristy3ed852e2009-09-05 21:47:34 +00004023 if (logging != MagickFalse)
4024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4025 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004026
cristy3ed852e2009-09-05 21:47:34 +00004027 (void) AcquireUniqueFilename(alpha_image->filename);
4028 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4029 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004030
cristy3ed852e2009-09-05 21:47:34 +00004031 if (status == MagickFalse)
4032 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004033
cristy3ed852e2009-09-05 21:47:34 +00004034 if (jng_alpha_compression_method == 0)
4035 {
4036 unsigned char
4037 data[18];
4038
4039 if (logging != MagickFalse)
4040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4041 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004042
cristy3ed852e2009-09-05 21:47:34 +00004043 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4044 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004045
cristy3ed852e2009-09-05 21:47:34 +00004046 (void) WriteBlobMSBULong(alpha_image,13L);
4047 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004048 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004049 PNGLong(data+4,jng_width);
4050 PNGLong(data+8,jng_height);
4051 data[12]=jng_alpha_sample_depth;
4052 data[13]=0; /* color_type gray */
4053 data[14]=0; /* compression method 0 */
4054 data[15]=0; /* filter_method 0 */
4055 data[16]=0; /* interlace_method 0 */
4056 (void) WriteBlob(alpha_image,17,data);
4057 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4058 }
4059 }
4060 reading_idat=MagickTrue;
4061 }
4062
4063 if (memcmp(type,mng_JDAT,4) == 0)
4064 {
glennrp47b9dd52010-11-24 18:12:06 +00004065 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004066
4067 if (logging != MagickFalse)
4068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4069 " Copying JDAT chunk data to color_blob.");
4070
4071 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004072
cristy3ed852e2009-09-05 21:47:34 +00004073 if (length)
4074 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004075
cristy3ed852e2009-09-05 21:47:34 +00004076 continue;
4077 }
4078
4079 if (memcmp(type,mng_IDAT,4) == 0)
4080 {
4081 png_byte
4082 data[5];
4083
glennrp47b9dd52010-11-24 18:12:06 +00004084 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004085
4086 if (image_info->ping == MagickFalse)
4087 {
4088 if (logging != MagickFalse)
4089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4090 " Copying IDAT chunk data to alpha_blob.");
4091
cristybb503372010-05-27 20:51:26 +00004092 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004093 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004094 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004095 (void) WriteBlob(alpha_image,4,data);
4096 (void) WriteBlob(alpha_image,length,chunk);
4097 (void) WriteBlobMSBULong(alpha_image,
4098 crc32(crc32(0,data,4),chunk,(uInt) length));
4099 }
glennrp0fe50b42010-11-16 03:52:51 +00004100
cristy3ed852e2009-09-05 21:47:34 +00004101 if (length)
4102 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004103
cristy3ed852e2009-09-05 21:47:34 +00004104 continue;
4105 }
4106
4107 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4108 {
glennrp47b9dd52010-11-24 18:12:06 +00004109 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004110
4111 if (image_info->ping == MagickFalse)
4112 {
4113 if (logging != MagickFalse)
4114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4115 " Copying JDAA chunk data to alpha_blob.");
4116
4117 (void) WriteBlob(alpha_image,length,chunk);
4118 }
glennrp0fe50b42010-11-16 03:52:51 +00004119
cristy3ed852e2009-09-05 21:47:34 +00004120 if (length)
4121 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004122
cristy3ed852e2009-09-05 21:47:34 +00004123 continue;
4124 }
4125
4126 if (memcmp(type,mng_JSEP,4) == 0)
4127 {
4128 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004129
cristy3ed852e2009-09-05 21:47:34 +00004130 if (length)
4131 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004132
cristy3ed852e2009-09-05 21:47:34 +00004133 continue;
4134 }
4135
4136 if (memcmp(type,mng_bKGD,4) == 0)
4137 {
4138 if (length == 2)
4139 {
4140 image->background_color.red=ScaleCharToQuantum(p[1]);
4141 image->background_color.green=image->background_color.red;
4142 image->background_color.blue=image->background_color.red;
4143 }
glennrp0fe50b42010-11-16 03:52:51 +00004144
cristy3ed852e2009-09-05 21:47:34 +00004145 if (length == 6)
4146 {
4147 image->background_color.red=ScaleCharToQuantum(p[1]);
4148 image->background_color.green=ScaleCharToQuantum(p[3]);
4149 image->background_color.blue=ScaleCharToQuantum(p[5]);
4150 }
glennrp0fe50b42010-11-16 03:52:51 +00004151
cristy3ed852e2009-09-05 21:47:34 +00004152 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4153 continue;
4154 }
4155
4156 if (memcmp(type,mng_gAMA,4) == 0)
4157 {
4158 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004159 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004160
cristy3ed852e2009-09-05 21:47:34 +00004161 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4162 continue;
4163 }
4164
4165 if (memcmp(type,mng_cHRM,4) == 0)
4166 {
4167 if (length == 32)
4168 {
cristy8182b072010-05-30 20:10:53 +00004169 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4170 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4171 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4172 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4173 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4174 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4175 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4176 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004177 }
glennrp47b9dd52010-11-24 18:12:06 +00004178
cristy3ed852e2009-09-05 21:47:34 +00004179 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4180 continue;
4181 }
4182
4183 if (memcmp(type,mng_sRGB,4) == 0)
4184 {
4185 if (length == 1)
4186 {
glennrpe610a072010-08-05 17:08:46 +00004187 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004188 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004189 image->gamma=0.45455f;
4190 image->chromaticity.red_primary.x=0.6400f;
4191 image->chromaticity.red_primary.y=0.3300f;
4192 image->chromaticity.green_primary.x=0.3000f;
4193 image->chromaticity.green_primary.y=0.6000f;
4194 image->chromaticity.blue_primary.x=0.1500f;
4195 image->chromaticity.blue_primary.y=0.0600f;
4196 image->chromaticity.white_point.x=0.3127f;
4197 image->chromaticity.white_point.y=0.3290f;
4198 }
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_oFFs,4) == 0)
4205 {
4206 if (length > 8)
4207 {
glennrp5eae7602011-02-22 15:21:32 +00004208 image->page.x=(ssize_t) mng_get_long(p);
4209 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004210
cristy3ed852e2009-09-05 21:47:34 +00004211 if ((int) p[8] != 0)
4212 {
4213 image->page.x/=10000;
4214 image->page.y/=10000;
4215 }
4216 }
glennrp47b9dd52010-11-24 18:12:06 +00004217
cristy3ed852e2009-09-05 21:47:34 +00004218 if (length)
4219 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004220
cristy3ed852e2009-09-05 21:47:34 +00004221 continue;
4222 }
4223
4224 if (memcmp(type,mng_pHYs,4) == 0)
4225 {
4226 if (length > 8)
4227 {
cristy8182b072010-05-30 20:10:53 +00004228 image->x_resolution=(double) mng_get_long(p);
4229 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004230 if ((int) p[8] == PNG_RESOLUTION_METER)
4231 {
4232 image->units=PixelsPerCentimeterResolution;
4233 image->x_resolution=image->x_resolution/100.0f;
4234 image->y_resolution=image->y_resolution/100.0f;
4235 }
4236 }
glennrp0fe50b42010-11-16 03:52:51 +00004237
cristy3ed852e2009-09-05 21:47:34 +00004238 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4239 continue;
4240 }
4241
4242#if 0
4243 if (memcmp(type,mng_iCCP,4) == 0)
4244 {
glennrpfd05d622011-02-25 04:10:33 +00004245 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004246 if (length)
4247 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004248
cristy3ed852e2009-09-05 21:47:34 +00004249 continue;
4250 }
4251#endif
4252
4253 if (length)
4254 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4255
4256 if (memcmp(type,mng_IEND,4))
4257 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004258
cristy3ed852e2009-09-05 21:47:34 +00004259 break;
4260 }
4261
4262
4263 /* IEND found */
4264
4265 /*
4266 Finish up reading image data:
4267
4268 o read main image from color_blob.
4269
4270 o close color_blob.
4271
4272 o if (color_type has alpha)
4273 if alpha_encoding is PNG
4274 read secondary image from alpha_blob via ReadPNG
4275 if alpha_encoding is JPEG
4276 read secondary image from alpha_blob via ReadJPEG
4277
4278 o close alpha_blob.
4279
4280 o copy intensity of secondary image into
4281 opacity samples of main image.
4282
4283 o destroy the secondary image.
4284 */
4285
4286 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004287
cristy3ed852e2009-09-05 21:47:34 +00004288 if (logging != MagickFalse)
4289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4290 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004291
cristy3b6fd2e2011-05-20 12:53:50 +00004292 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004293 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004294
cristy3ed852e2009-09-05 21:47:34 +00004295 color_image_info->ping=MagickFalse; /* To do: avoid this */
4296 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004297
cristy3ed852e2009-09-05 21:47:34 +00004298 if (jng_image == (Image *) NULL)
4299 return((Image *) NULL);
4300
4301 (void) RelinquishUniqueFileResource(color_image->filename);
4302 color_image=DestroyImage(color_image);
4303 color_image_info=DestroyImageInfo(color_image_info);
4304
4305 if (jng_image == (Image *) NULL)
4306 return((Image *) NULL);
4307
4308 if (logging != MagickFalse)
4309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4310 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004311
cristy3ed852e2009-09-05 21:47:34 +00004312 image->rows=jng_height;
4313 image->columns=jng_width;
4314 length=image->columns*sizeof(PixelPacket);
glennrp0fe50b42010-11-16 03:52:51 +00004315
cristybb503372010-05-27 20:51:26 +00004316 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004317 {
4318 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4319 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4320 (void) CopyMagickMemory(q,s,length);
glennrp47b9dd52010-11-24 18:12:06 +00004321
cristy3ed852e2009-09-05 21:47:34 +00004322 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4323 break;
4324 }
glennrp0fe50b42010-11-16 03:52:51 +00004325
cristy3ed852e2009-09-05 21:47:34 +00004326 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004327
cristy3ed852e2009-09-05 21:47:34 +00004328 if (image_info->ping == MagickFalse)
4329 {
4330 if (jng_color_type >= 12)
4331 {
4332 if (jng_alpha_compression_method == 0)
4333 {
4334 png_byte
4335 data[5];
4336 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4337 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004338 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004339 (void) WriteBlob(alpha_image,4,data);
4340 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4341 }
glennrp0fe50b42010-11-16 03:52:51 +00004342
cristy3ed852e2009-09-05 21:47:34 +00004343 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004344
cristy3ed852e2009-09-05 21:47:34 +00004345 if (logging != MagickFalse)
4346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4347 " Reading opacity from alpha_blob.");
4348
cristy3b6fd2e2011-05-20 12:53:50 +00004349 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004350 "%s",alpha_image->filename);
4351
4352 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004353
cristy3ed852e2009-09-05 21:47:34 +00004354 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004355 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004356 {
4357 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4358 &image->exception);
4359 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004360
cristy3ed852e2009-09-05 21:47:34 +00004361 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00004362 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristyef618312011-06-25 12:26:44 +00004363 SetPixelOpacity(q,(Quantum) QuantumRange-
4364 GetPixelRed(s));
glennrp0fe50b42010-11-16 03:52:51 +00004365
cristy3ed852e2009-09-05 21:47:34 +00004366 else
cristybb503372010-05-27 20:51:26 +00004367 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00004368 {
cristyef618312011-06-25 12:26:44 +00004369 SetPixelAlpha(q,GetPixelRed(s));
4370 if (GetPixelOpacity(q) != OpaqueOpacity)
cristy3ed852e2009-09-05 21:47:34 +00004371 image->matte=MagickTrue;
4372 }
glennrp0fe50b42010-11-16 03:52:51 +00004373
cristy3ed852e2009-09-05 21:47:34 +00004374 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4375 break;
4376 }
4377 (void) RelinquishUniqueFileResource(alpha_image->filename);
4378 alpha_image=DestroyImage(alpha_image);
4379 alpha_image_info=DestroyImageInfo(alpha_image_info);
4380 if (jng_image != (Image *) NULL)
4381 jng_image=DestroyImage(jng_image);
4382 }
4383 }
4384
glennrp47b9dd52010-11-24 18:12:06 +00004385 /* Read the JNG image. */
4386
cristy3ed852e2009-09-05 21:47:34 +00004387 if (mng_info->mng_type == 0)
4388 {
4389 mng_info->mng_width=jng_width;
4390 mng_info->mng_height=jng_height;
4391 }
glennrp0fe50b42010-11-16 03:52:51 +00004392
cristy3ed852e2009-09-05 21:47:34 +00004393 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004394 {
4395 image->page.width=jng_width;
4396 image->page.height=jng_height;
4397 }
4398
cristy3ed852e2009-09-05 21:47:34 +00004399 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004400 {
4401 image->page.x=mng_info->x_off[mng_info->object_id];
4402 image->page.y=mng_info->y_off[mng_info->object_id];
4403 }
4404
cristy3ed852e2009-09-05 21:47:34 +00004405 else
glennrp0fe50b42010-11-16 03:52:51 +00004406 {
4407 image->page.y=mng_info->y_off[mng_info->object_id];
4408 }
4409
cristy3ed852e2009-09-05 21:47:34 +00004410 mng_info->image_found++;
4411 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4412 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004413
cristy3ed852e2009-09-05 21:47:34 +00004414 if (logging != MagickFalse)
4415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4416 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004417
cristy3ed852e2009-09-05 21:47:34 +00004418 return(image);
4419}
4420
4421/*
4422%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4423% %
4424% %
4425% %
4426% R e a d J N G I m a g e %
4427% %
4428% %
4429% %
4430%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4431%
4432% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4433% (including the 8-byte signature) and returns it. It allocates the memory
4434% necessary for the new Image structure and returns a pointer to the new
4435% image.
4436%
4437% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4438%
4439% The format of the ReadJNGImage method is:
4440%
4441% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4442% *exception)
4443%
4444% A description of each parameter follows:
4445%
4446% o image_info: the image info.
4447%
4448% o exception: return any errors or warnings in this structure.
4449%
4450*/
4451
4452static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4453{
4454 Image
4455 *image,
4456 *previous;
4457
4458 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004459 have_mng_structure,
4460 logging,
cristy3ed852e2009-09-05 21:47:34 +00004461 status;
4462
4463 MngInfo
4464 *mng_info;
4465
4466 char
4467 magic_number[MaxTextExtent];
4468
cristy3ed852e2009-09-05 21:47:34 +00004469 size_t
4470 count;
4471
4472 /*
4473 Open image file.
4474 */
4475 assert(image_info != (const ImageInfo *) NULL);
4476 assert(image_info->signature == MagickSignature);
4477 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4478 assert(exception != (ExceptionInfo *) NULL);
4479 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004480 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004481 image=AcquireImage(image_info);
4482 mng_info=(MngInfo *) NULL;
4483 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004484
cristy3ed852e2009-09-05 21:47:34 +00004485 if (status == MagickFalse)
4486 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004487
cristy3ed852e2009-09-05 21:47:34 +00004488 if (LocaleCompare(image_info->magick,"JNG") != 0)
4489 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004490
glennrp47b9dd52010-11-24 18:12:06 +00004491 /* Verify JNG signature. */
4492
cristy3ed852e2009-09-05 21:47:34 +00004493 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004494
glennrp3b8763e2011-02-21 12:08:18 +00004495 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004496 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004497
glennrp47b9dd52010-11-24 18:12:06 +00004498 /* Allocate a MngInfo structure. */
4499
cristy3ed852e2009-09-05 21:47:34 +00004500 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004501 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004502
cristy3ed852e2009-09-05 21:47:34 +00004503 if (mng_info == (MngInfo *) NULL)
4504 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004505
glennrp47b9dd52010-11-24 18:12:06 +00004506 /* Initialize members of the MngInfo structure. */
4507
cristy3ed852e2009-09-05 21:47:34 +00004508 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4509 have_mng_structure=MagickTrue;
4510
4511 mng_info->image=image;
4512 previous=image;
4513 image=ReadOneJNGImage(mng_info,image_info,exception);
4514 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004515
cristy3ed852e2009-09-05 21:47:34 +00004516 if (image == (Image *) NULL)
4517 {
4518 if (IsImageObject(previous) != MagickFalse)
4519 {
4520 (void) CloseBlob(previous);
4521 (void) DestroyImageList(previous);
4522 }
glennrp0fe50b42010-11-16 03:52:51 +00004523
cristy3ed852e2009-09-05 21:47:34 +00004524 if (logging != MagickFalse)
4525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4526 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004527
cristy3ed852e2009-09-05 21:47:34 +00004528 return((Image *) NULL);
4529 }
4530 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004531
cristy3ed852e2009-09-05 21:47:34 +00004532 if (image->columns == 0 || image->rows == 0)
4533 {
4534 if (logging != MagickFalse)
4535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4536 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004537
cristy3ed852e2009-09-05 21:47:34 +00004538 ThrowReaderException(CorruptImageError,"CorruptImage");
4539 }
glennrp0fe50b42010-11-16 03:52:51 +00004540
cristy3ed852e2009-09-05 21:47:34 +00004541 if (logging != MagickFalse)
4542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004543
cristy3ed852e2009-09-05 21:47:34 +00004544 return(image);
4545}
4546#endif
4547
4548static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4549{
4550 char
4551 page_geometry[MaxTextExtent];
4552
4553 Image
4554 *image,
4555 *previous;
4556
cristy4383ec82011-01-05 15:42:32 +00004557 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004558 logging,
4559 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004560
cristy3ed852e2009-09-05 21:47:34 +00004561 volatile int
4562 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004563 object_id,
4564 term_chunk_found,
4565 skip_to_iend;
4566
cristybb503372010-05-27 20:51:26 +00004567 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004568 image_count=0;
4569
4570 MagickBooleanType
4571 status;
4572
4573 MagickOffsetType
4574 offset;
4575
4576 MngInfo
4577 *mng_info;
4578
4579 MngBox
4580 default_fb,
4581 fb,
4582 previous_fb;
4583
4584#if defined(MNG_INSERT_LAYERS)
4585 PixelPacket
4586 mng_background_color;
4587#endif
4588
4589 register unsigned char
4590 *p;
4591
cristybb503372010-05-27 20:51:26 +00004592 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004593 i;
4594
4595 size_t
4596 count;
4597
cristybb503372010-05-27 20:51:26 +00004598 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004599 loop_level;
4600
4601 volatile short
4602 skipping_loop;
4603
4604#if defined(MNG_INSERT_LAYERS)
4605 unsigned int
4606 mandatory_back=0;
4607#endif
4608
4609 volatile unsigned int
4610#ifdef MNG_OBJECT_BUFFERS
4611 mng_background_object=0,
4612#endif
4613 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4614
cristybb503372010-05-27 20:51:26 +00004615 size_t
cristy3ed852e2009-09-05 21:47:34 +00004616 default_frame_timeout,
4617 frame_timeout,
4618#if defined(MNG_INSERT_LAYERS)
4619 image_height,
4620 image_width,
4621#endif
4622 length;
4623
glennrp38ea0832010-06-02 18:50:28 +00004624 /* These delays are all measured in image ticks_per_second,
4625 * not in MNG ticks_per_second
4626 */
cristybb503372010-05-27 20:51:26 +00004627 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004628 default_frame_delay,
4629 final_delay,
4630 final_image_delay,
4631 frame_delay,
4632#if defined(MNG_INSERT_LAYERS)
4633 insert_layers,
4634#endif
4635 mng_iterations=1,
4636 simplicity=0,
4637 subframe_height=0,
4638 subframe_width=0;
4639
4640 previous_fb.top=0;
4641 previous_fb.bottom=0;
4642 previous_fb.left=0;
4643 previous_fb.right=0;
4644 default_fb.top=0;
4645 default_fb.bottom=0;
4646 default_fb.left=0;
4647 default_fb.right=0;
4648
glennrp47b9dd52010-11-24 18:12:06 +00004649 /* Open image file. */
4650
cristy3ed852e2009-09-05 21:47:34 +00004651 assert(image_info != (const ImageInfo *) NULL);
4652 assert(image_info->signature == MagickSignature);
4653 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4654 assert(exception != (ExceptionInfo *) NULL);
4655 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004656 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004657 image=AcquireImage(image_info);
4658 mng_info=(MngInfo *) NULL;
4659 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004660
cristy3ed852e2009-09-05 21:47:34 +00004661 if (status == MagickFalse)
4662 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004663
cristy3ed852e2009-09-05 21:47:34 +00004664 first_mng_object=MagickFalse;
4665 skipping_loop=(-1);
4666 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004667
4668 /* Allocate a MngInfo structure. */
4669
cristy73bd4a52010-10-05 11:24:23 +00004670 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004671
cristy3ed852e2009-09-05 21:47:34 +00004672 if (mng_info == (MngInfo *) NULL)
4673 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004674
glennrp47b9dd52010-11-24 18:12:06 +00004675 /* Initialize members of the MngInfo structure. */
4676
cristy3ed852e2009-09-05 21:47:34 +00004677 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4678 mng_info->image=image;
4679 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004680
4681 if (LocaleCompare(image_info->magick,"MNG") == 0)
4682 {
4683 char
4684 magic_number[MaxTextExtent];
4685
glennrp47b9dd52010-11-24 18:12:06 +00004686 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004687 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4688 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4689 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004690
4691 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004692 for (i=0; i < MNG_MAX_OBJECTS; i++)
4693 {
cristybb503372010-05-27 20:51:26 +00004694 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4695 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004696 }
4697 mng_info->exists[0]=MagickTrue;
4698 }
glennrp47b9dd52010-11-24 18:12:06 +00004699
cristy3ed852e2009-09-05 21:47:34 +00004700 first_mng_object=MagickTrue;
4701 mng_type=0;
4702#if defined(MNG_INSERT_LAYERS)
4703 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4704#endif
4705 default_frame_delay=0;
4706 default_frame_timeout=0;
4707 frame_delay=0;
4708 final_delay=1;
4709 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4710 object_id=0;
4711 skip_to_iend=MagickFalse;
4712 term_chunk_found=MagickFalse;
4713 mng_info->framing_mode=1;
4714#if defined(MNG_INSERT_LAYERS)
4715 mandatory_back=MagickFalse;
4716#endif
4717#if defined(MNG_INSERT_LAYERS)
4718 mng_background_color=image->background_color;
4719#endif
4720 default_fb=mng_info->frame;
4721 previous_fb=mng_info->frame;
4722 do
4723 {
4724 char
4725 type[MaxTextExtent];
4726
4727 if (LocaleCompare(image_info->magick,"MNG") == 0)
4728 {
4729 unsigned char
4730 *chunk;
4731
4732 /*
4733 Read a new chunk.
4734 */
4735 type[0]='\0';
4736 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4737 length=ReadBlobMSBLong(image);
4738 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4739
4740 if (logging != MagickFalse)
4741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004742 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4743 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004744
4745 if (length > PNG_UINT_31_MAX)
4746 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004747
cristy3ed852e2009-09-05 21:47:34 +00004748 if (count == 0)
4749 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004750
cristy3ed852e2009-09-05 21:47:34 +00004751 p=NULL;
4752 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004753
cristy3ed852e2009-09-05 21:47:34 +00004754 if (length)
4755 {
4756 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004757
cristy3ed852e2009-09-05 21:47:34 +00004758 if (chunk == (unsigned char *) NULL)
4759 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004760
cristybb503372010-05-27 20:51:26 +00004761 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004762 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004763
cristy3ed852e2009-09-05 21:47:34 +00004764 p=chunk;
4765 }
glennrp0fe50b42010-11-16 03:52:51 +00004766
cristy3ed852e2009-09-05 21:47:34 +00004767 (void) ReadBlobMSBLong(image); /* read crc word */
4768
4769#if !defined(JNG_SUPPORTED)
4770 if (memcmp(type,mng_JHDR,4) == 0)
4771 {
4772 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004773
cristy3ed852e2009-09-05 21:47:34 +00004774 if (mng_info->jhdr_warning == 0)
4775 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4776 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004777
cristy3ed852e2009-09-05 21:47:34 +00004778 mng_info->jhdr_warning++;
4779 }
4780#endif
4781 if (memcmp(type,mng_DHDR,4) == 0)
4782 {
4783 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004784
cristy3ed852e2009-09-05 21:47:34 +00004785 if (mng_info->dhdr_warning == 0)
4786 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4787 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004788
cristy3ed852e2009-09-05 21:47:34 +00004789 mng_info->dhdr_warning++;
4790 }
4791 if (memcmp(type,mng_MEND,4) == 0)
4792 break;
glennrp47b9dd52010-11-24 18:12:06 +00004793
cristy3ed852e2009-09-05 21:47:34 +00004794 if (skip_to_iend)
4795 {
4796 if (memcmp(type,mng_IEND,4) == 0)
4797 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004798
cristy3ed852e2009-09-05 21:47:34 +00004799 if (length)
4800 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004801
cristy3ed852e2009-09-05 21:47:34 +00004802 if (logging != MagickFalse)
4803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4804 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004805
cristy3ed852e2009-09-05 21:47:34 +00004806 continue;
4807 }
glennrp0fe50b42010-11-16 03:52:51 +00004808
cristy3ed852e2009-09-05 21:47:34 +00004809 if (memcmp(type,mng_MHDR,4) == 0)
4810 {
cristybb503372010-05-27 20:51:26 +00004811 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004812 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004813
cristybb503372010-05-27 20:51:26 +00004814 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004815 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004816
cristy3ed852e2009-09-05 21:47:34 +00004817 if (logging != MagickFalse)
4818 {
4819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004820 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004822 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004823 }
glennrp0fe50b42010-11-16 03:52:51 +00004824
cristy3ed852e2009-09-05 21:47:34 +00004825 p+=8;
cristy8182b072010-05-30 20:10:53 +00004826 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004827
cristy3ed852e2009-09-05 21:47:34 +00004828 if (mng_info->ticks_per_second == 0)
4829 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004830
cristy3ed852e2009-09-05 21:47:34 +00004831 else
4832 default_frame_delay=1UL*image->ticks_per_second/
4833 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004834
cristy3ed852e2009-09-05 21:47:34 +00004835 frame_delay=default_frame_delay;
4836 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004837
cristy3ed852e2009-09-05 21:47:34 +00004838 if (length > 16)
4839 {
4840 p+=16;
cristy8182b072010-05-30 20:10:53 +00004841 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004842 }
glennrp0fe50b42010-11-16 03:52:51 +00004843
cristy3ed852e2009-09-05 21:47:34 +00004844 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004845
cristy3ed852e2009-09-05 21:47:34 +00004846 if ((simplicity != 0) && ((simplicity | 11) == 11))
4847 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004848
cristy3ed852e2009-09-05 21:47:34 +00004849 if ((simplicity != 0) && ((simplicity | 9) == 9))
4850 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004851
cristy3ed852e2009-09-05 21:47:34 +00004852#if defined(MNG_INSERT_LAYERS)
4853 if (mng_type != 3)
4854 insert_layers=MagickTrue;
4855#endif
4856 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4857 {
glennrp47b9dd52010-11-24 18:12:06 +00004858 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004859 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004860
cristy3ed852e2009-09-05 21:47:34 +00004861 if (GetNextImageInList(image) == (Image *) NULL)
4862 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864 image=SyncNextImageInList(image);
4865 mng_info->image=image;
4866 }
4867
4868 if ((mng_info->mng_width > 65535L) ||
4869 (mng_info->mng_height > 65535L))
4870 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004871
cristy3b6fd2e2011-05-20 12:53:50 +00004872 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004873 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004874 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004875
cristy3ed852e2009-09-05 21:47:34 +00004876 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004877 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004878 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004879 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004880 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004881
cristy3ed852e2009-09-05 21:47:34 +00004882 for (i=0; i < MNG_MAX_OBJECTS; i++)
4883 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004884
cristy3ed852e2009-09-05 21:47:34 +00004885 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4886 continue;
4887 }
4888
4889 if (memcmp(type,mng_TERM,4) == 0)
4890 {
4891 int
4892 repeat=0;
4893
4894
4895 if (length)
4896 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004897
cristy3ed852e2009-09-05 21:47:34 +00004898 if (repeat == 3)
4899 {
cristy8182b072010-05-30 20:10:53 +00004900 final_delay=(png_uint_32) mng_get_long(&p[2]);
4901 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004902
cristy3ed852e2009-09-05 21:47:34 +00004903 if (mng_iterations == PNG_UINT_31_MAX)
4904 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004905
cristy3ed852e2009-09-05 21:47:34 +00004906 image->iterations=mng_iterations;
4907 term_chunk_found=MagickTrue;
4908 }
glennrp0fe50b42010-11-16 03:52:51 +00004909
cristy3ed852e2009-09-05 21:47:34 +00004910 if (logging != MagickFalse)
4911 {
4912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4913 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004914
cristy3ed852e2009-09-05 21:47:34 +00004915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004916 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004917
cristy3ed852e2009-09-05 21:47:34 +00004918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004919 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004920 }
glennrp0fe50b42010-11-16 03:52:51 +00004921
cristy3ed852e2009-09-05 21:47:34 +00004922 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4923 continue;
4924 }
4925 if (memcmp(type,mng_DEFI,4) == 0)
4926 {
4927 if (mng_type == 3)
4928 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4929 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4930 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004931
cristy3ed852e2009-09-05 21:47:34 +00004932 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004933
cristy3ed852e2009-09-05 21:47:34 +00004934 if (mng_type == 2 && object_id != 0)
4935 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4936 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4937 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004938
cristy3ed852e2009-09-05 21:47:34 +00004939 if (object_id > MNG_MAX_OBJECTS)
4940 {
4941 /*
4942 Instead ofsuing a warning we should allocate a larger
4943 MngInfo structure and continue.
4944 */
4945 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4946 CoderError,"object id too large","`%s'",image->filename);
4947 object_id=MNG_MAX_OBJECTS;
4948 }
glennrp0fe50b42010-11-16 03:52:51 +00004949
cristy3ed852e2009-09-05 21:47:34 +00004950 if (mng_info->exists[object_id])
4951 if (mng_info->frozen[object_id])
4952 {
4953 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4954 (void) ThrowMagickException(&image->exception,
4955 GetMagickModule(),CoderError,
4956 "DEFI cannot redefine a frozen MNG object","`%s'",
4957 image->filename);
4958 continue;
4959 }
glennrp0fe50b42010-11-16 03:52:51 +00004960
cristy3ed852e2009-09-05 21:47:34 +00004961 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004962
cristy3ed852e2009-09-05 21:47:34 +00004963 if (length > 2)
4964 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004965
cristy3ed852e2009-09-05 21:47:34 +00004966 /*
4967 Extract object offset info.
4968 */
4969 if (length > 11)
4970 {
glennrp0fe50b42010-11-16 03:52:51 +00004971 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4972 (p[5] << 16) | (p[6] << 8) | p[7]);
4973
4974 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4975 (p[9] << 16) | (p[10] << 8) | p[11]);
4976
cristy3ed852e2009-09-05 21:47:34 +00004977 if (logging != MagickFalse)
4978 {
4979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004980 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004981 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004982
cristy3ed852e2009-09-05 21:47:34 +00004983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004984 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004985 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004986 }
4987 }
glennrp0fe50b42010-11-16 03:52:51 +00004988
cristy3ed852e2009-09-05 21:47:34 +00004989 /*
4990 Extract object clipping info.
4991 */
4992 if (length > 27)
4993 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4994 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00004995
cristy3ed852e2009-09-05 21:47:34 +00004996 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4997 continue;
4998 }
4999 if (memcmp(type,mng_bKGD,4) == 0)
5000 {
5001 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005002
cristy3ed852e2009-09-05 21:47:34 +00005003 if (length > 5)
5004 {
5005 mng_info->mng_global_bkgd.red=
5006 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005007
cristy3ed852e2009-09-05 21:47:34 +00005008 mng_info->mng_global_bkgd.green=
5009 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005010
cristy3ed852e2009-09-05 21:47:34 +00005011 mng_info->mng_global_bkgd.blue=
5012 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005013
cristy3ed852e2009-09-05 21:47:34 +00005014 mng_info->have_global_bkgd=MagickTrue;
5015 }
glennrp0fe50b42010-11-16 03:52:51 +00005016
cristy3ed852e2009-09-05 21:47:34 +00005017 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5018 continue;
5019 }
5020 if (memcmp(type,mng_BACK,4) == 0)
5021 {
5022#if defined(MNG_INSERT_LAYERS)
5023 if (length > 6)
5024 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005025
cristy3ed852e2009-09-05 21:47:34 +00005026 else
5027 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005028
cristy3ed852e2009-09-05 21:47:34 +00005029 if (mandatory_back && length > 5)
5030 {
5031 mng_background_color.red=
5032 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005033
cristy3ed852e2009-09-05 21:47:34 +00005034 mng_background_color.green=
5035 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005036
cristy3ed852e2009-09-05 21:47:34 +00005037 mng_background_color.blue=
5038 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005039
cristy3ed852e2009-09-05 21:47:34 +00005040 mng_background_color.opacity=OpaqueOpacity;
5041 }
glennrp0fe50b42010-11-16 03:52:51 +00005042
cristy3ed852e2009-09-05 21:47:34 +00005043#ifdef MNG_OBJECT_BUFFERS
5044 if (length > 8)
5045 mng_background_object=(p[7] << 8) | p[8];
5046#endif
5047#endif
5048 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5049 continue;
5050 }
glennrp47b9dd52010-11-24 18:12:06 +00005051
cristy3ed852e2009-09-05 21:47:34 +00005052 if (memcmp(type,mng_PLTE,4) == 0)
5053 {
glennrp47b9dd52010-11-24 18:12:06 +00005054 /* Read global PLTE. */
5055
cristy3ed852e2009-09-05 21:47:34 +00005056 if (length && (length < 769))
5057 {
5058 if (mng_info->global_plte == (png_colorp) NULL)
5059 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5060 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005061
cristybb503372010-05-27 20:51:26 +00005062 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005063 {
5064 mng_info->global_plte[i].red=p[3*i];
5065 mng_info->global_plte[i].green=p[3*i+1];
5066 mng_info->global_plte[i].blue=p[3*i+2];
5067 }
glennrp0fe50b42010-11-16 03:52:51 +00005068
cristy35ef8242010-06-03 16:24:13 +00005069 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005070 }
5071#ifdef MNG_LOOSE
5072 for ( ; i < 256; i++)
5073 {
5074 mng_info->global_plte[i].red=i;
5075 mng_info->global_plte[i].green=i;
5076 mng_info->global_plte[i].blue=i;
5077 }
glennrp0fe50b42010-11-16 03:52:51 +00005078
cristy3ed852e2009-09-05 21:47:34 +00005079 if (length)
5080 mng_info->global_plte_length=256;
5081#endif
5082 else
5083 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005084
cristy3ed852e2009-09-05 21:47:34 +00005085 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5086 continue;
5087 }
glennrp47b9dd52010-11-24 18:12:06 +00005088
cristy3ed852e2009-09-05 21:47:34 +00005089 if (memcmp(type,mng_tRNS,4) == 0)
5090 {
5091 /* read global tRNS */
5092
5093 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005094 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005095 mng_info->global_trns[i]=p[i];
5096
5097#ifdef MNG_LOOSE
5098 for ( ; i < 256; i++)
5099 mng_info->global_trns[i]=255;
5100#endif
cristy12560f32010-06-03 16:51:08 +00005101 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005102 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5103 continue;
5104 }
5105 if (memcmp(type,mng_gAMA,4) == 0)
5106 {
5107 if (length == 4)
5108 {
cristybb503372010-05-27 20:51:26 +00005109 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005110 igamma;
5111
cristy8182b072010-05-30 20:10:53 +00005112 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005113 mng_info->global_gamma=((float) igamma)*0.00001;
5114 mng_info->have_global_gama=MagickTrue;
5115 }
glennrp0fe50b42010-11-16 03:52:51 +00005116
cristy3ed852e2009-09-05 21:47:34 +00005117 else
5118 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005119
cristy3ed852e2009-09-05 21:47:34 +00005120 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5121 continue;
5122 }
5123
5124 if (memcmp(type,mng_cHRM,4) == 0)
5125 {
glennrp47b9dd52010-11-24 18:12:06 +00005126 /* Read global cHRM */
5127
cristy3ed852e2009-09-05 21:47:34 +00005128 if (length == 32)
5129 {
cristy8182b072010-05-30 20:10:53 +00005130 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5131 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5132 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005133 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005134 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005135 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005136 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005137 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005138 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005139 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005140 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005141 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005142 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005143 mng_info->have_global_chrm=MagickTrue;
5144 }
5145 else
5146 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005147
cristy3ed852e2009-09-05 21:47:34 +00005148 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5149 continue;
5150 }
glennrp47b9dd52010-11-24 18:12:06 +00005151
cristy3ed852e2009-09-05 21:47:34 +00005152 if (memcmp(type,mng_sRGB,4) == 0)
5153 {
5154 /*
5155 Read global sRGB.
5156 */
5157 if (length)
5158 {
glennrpe610a072010-08-05 17:08:46 +00005159 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005160 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005161 mng_info->have_global_srgb=MagickTrue;
5162 }
5163 else
5164 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005165
cristy3ed852e2009-09-05 21:47:34 +00005166 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5167 continue;
5168 }
glennrp47b9dd52010-11-24 18:12:06 +00005169
cristy3ed852e2009-09-05 21:47:34 +00005170 if (memcmp(type,mng_iCCP,4) == 0)
5171 {
glennrpfd05d622011-02-25 04:10:33 +00005172 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005173
5174 /*
5175 Read global iCCP.
5176 */
5177 if (length)
5178 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005179
cristy3ed852e2009-09-05 21:47:34 +00005180 continue;
5181 }
glennrp47b9dd52010-11-24 18:12:06 +00005182
cristy3ed852e2009-09-05 21:47:34 +00005183 if (memcmp(type,mng_FRAM,4) == 0)
5184 {
5185 if (mng_type == 3)
5186 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5187 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5188 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005189
cristy3ed852e2009-09-05 21:47:34 +00005190 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5191 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005192
cristy3ed852e2009-09-05 21:47:34 +00005193 frame_delay=default_frame_delay;
5194 frame_timeout=default_frame_timeout;
5195 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005196
cristy3ed852e2009-09-05 21:47:34 +00005197 if (length)
5198 if (p[0])
5199 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005200
cristy3ed852e2009-09-05 21:47:34 +00005201 if (logging != MagickFalse)
5202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5203 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005204
cristy3ed852e2009-09-05 21:47:34 +00005205 if (length > 6)
5206 {
glennrp47b9dd52010-11-24 18:12:06 +00005207 /* Note the delay and frame clipping boundaries. */
5208
cristy3ed852e2009-09-05 21:47:34 +00005209 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005210
cristybb503372010-05-27 20:51:26 +00005211 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005212 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005213
cristy3ed852e2009-09-05 21:47:34 +00005214 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005215
cristybb503372010-05-27 20:51:26 +00005216 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005217 {
5218 int
5219 change_delay,
5220 change_timeout,
5221 change_clipping;
5222
5223 change_delay=(*p++);
5224 change_timeout=(*p++);
5225 change_clipping=(*p++);
5226 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005227
cristy3ed852e2009-09-05 21:47:34 +00005228 if (change_delay)
5229 {
cristy8182b072010-05-30 20:10:53 +00005230 frame_delay=1UL*image->ticks_per_second*
5231 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005232
cristy8182b072010-05-30 20:10:53 +00005233 if (mng_info->ticks_per_second != 0)
5234 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005235
glennrpbb010dd2010-06-01 13:07:15 +00005236 else
5237 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005238
cristy3ed852e2009-09-05 21:47:34 +00005239 if (change_delay == 2)
5240 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005241
cristy3ed852e2009-09-05 21:47:34 +00005242 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005243
cristy3ed852e2009-09-05 21:47:34 +00005244 if (logging != MagickFalse)
5245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005246 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005247 }
glennrp47b9dd52010-11-24 18:12:06 +00005248
cristy3ed852e2009-09-05 21:47:34 +00005249 if (change_timeout)
5250 {
glennrpbb010dd2010-06-01 13:07:15 +00005251 frame_timeout=1UL*image->ticks_per_second*
5252 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005253
glennrpbb010dd2010-06-01 13:07:15 +00005254 if (mng_info->ticks_per_second != 0)
5255 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005256
glennrpbb010dd2010-06-01 13:07:15 +00005257 else
5258 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005259
cristy3ed852e2009-09-05 21:47:34 +00005260 if (change_delay == 2)
5261 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005262
cristy3ed852e2009-09-05 21:47:34 +00005263 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005264
cristy3ed852e2009-09-05 21:47:34 +00005265 if (logging != MagickFalse)
5266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005267 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005268 }
glennrp47b9dd52010-11-24 18:12:06 +00005269
cristy3ed852e2009-09-05 21:47:34 +00005270 if (change_clipping)
5271 {
5272 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5273 p+=17;
5274 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005275
cristy3ed852e2009-09-05 21:47:34 +00005276 if (logging != MagickFalse)
5277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005278 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005279 (double) fb.left,(double) fb.right,(double) fb.top,
5280 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005281
cristy3ed852e2009-09-05 21:47:34 +00005282 if (change_clipping == 2)
5283 default_fb=fb;
5284 }
5285 }
5286 }
5287 mng_info->clip=fb;
5288 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005289
cristybb503372010-05-27 20:51:26 +00005290 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005291 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005292
cristybb503372010-05-27 20:51:26 +00005293 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005294 -mng_info->clip.top);
5295 /*
5296 Insert a background layer behind the frame if framing_mode is 4.
5297 */
5298#if defined(MNG_INSERT_LAYERS)
5299 if (logging != MagickFalse)
5300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005301 " subframe_width=%.20g, subframe_height=%.20g",(double)
5302 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005303
cristy3ed852e2009-09-05 21:47:34 +00005304 if (insert_layers && (mng_info->framing_mode == 4) &&
5305 (subframe_width) && (subframe_height))
5306 {
glennrp47b9dd52010-11-24 18:12:06 +00005307 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00005308 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5309 {
5310 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005311
cristy3ed852e2009-09-05 21:47:34 +00005312 if (GetNextImageInList(image) == (Image *) NULL)
5313 {
5314 image=DestroyImageList(image);
5315 MngInfoFreeStruct(mng_info,&have_mng_structure);
5316 return((Image *) NULL);
5317 }
glennrp47b9dd52010-11-24 18:12:06 +00005318
cristy3ed852e2009-09-05 21:47:34 +00005319 image=SyncNextImageInList(image);
5320 }
glennrp0fe50b42010-11-16 03:52:51 +00005321
cristy3ed852e2009-09-05 21:47:34 +00005322 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005323
cristy3ed852e2009-09-05 21:47:34 +00005324 if (term_chunk_found)
5325 {
5326 image->start_loop=MagickTrue;
5327 image->iterations=mng_iterations;
5328 term_chunk_found=MagickFalse;
5329 }
glennrp0fe50b42010-11-16 03:52:51 +00005330
cristy3ed852e2009-09-05 21:47:34 +00005331 else
5332 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005333
cristy3ed852e2009-09-05 21:47:34 +00005334 image->columns=subframe_width;
5335 image->rows=subframe_height;
5336 image->page.width=subframe_width;
5337 image->page.height=subframe_height;
5338 image->page.x=mng_info->clip.left;
5339 image->page.y=mng_info->clip.top;
5340 image->background_color=mng_background_color;
5341 image->matte=MagickFalse;
5342 image->delay=0;
5343 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005344
cristy3ed852e2009-09-05 21:47:34 +00005345 if (logging != MagickFalse)
5346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005347 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005348 (double) mng_info->clip.left,(double) mng_info->clip.right,
5349 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005350 }
5351#endif
5352 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5353 continue;
5354 }
5355 if (memcmp(type,mng_CLIP,4) == 0)
5356 {
5357 unsigned int
5358 first_object,
5359 last_object;
5360
5361 /*
5362 Read CLIP.
5363 */
5364 first_object=(p[0] << 8) | p[1];
5365 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005366
cristy3ed852e2009-09-05 21:47:34 +00005367 for (i=(int) first_object; i <= (int) last_object; i++)
5368 {
5369 if (mng_info->exists[i] && !mng_info->frozen[i])
5370 {
5371 MngBox
5372 box;
5373
5374 box=mng_info->object_clip[i];
5375 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5376 }
5377 }
glennrp47b9dd52010-11-24 18:12:06 +00005378
cristy3ed852e2009-09-05 21:47:34 +00005379 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5380 continue;
5381 }
5382 if (memcmp(type,mng_SAVE,4) == 0)
5383 {
5384 for (i=1; i < MNG_MAX_OBJECTS; i++)
5385 if (mng_info->exists[i])
5386 {
5387 mng_info->frozen[i]=MagickTrue;
5388#ifdef MNG_OBJECT_BUFFERS
5389 if (mng_info->ob[i] != (MngBuffer *) NULL)
5390 mng_info->ob[i]->frozen=MagickTrue;
5391#endif
5392 }
glennrp0fe50b42010-11-16 03:52:51 +00005393
cristy3ed852e2009-09-05 21:47:34 +00005394 if (length)
5395 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005396
cristy3ed852e2009-09-05 21:47:34 +00005397 continue;
5398 }
5399
5400 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5401 {
glennrp47b9dd52010-11-24 18:12:06 +00005402 /* Read DISC or SEEK. */
5403
cristy3ed852e2009-09-05 21:47:34 +00005404 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5405 {
5406 for (i=1; i < MNG_MAX_OBJECTS; i++)
5407 MngInfoDiscardObject(mng_info,i);
5408 }
glennrp0fe50b42010-11-16 03:52:51 +00005409
cristy3ed852e2009-09-05 21:47:34 +00005410 else
5411 {
cristybb503372010-05-27 20:51:26 +00005412 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005413 j;
5414
cristybb503372010-05-27 20:51:26 +00005415 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005416 {
5417 i=p[j] << 8 | p[j+1];
5418 MngInfoDiscardObject(mng_info,i);
5419 }
5420 }
glennrp0fe50b42010-11-16 03:52:51 +00005421
cristy3ed852e2009-09-05 21:47:34 +00005422 if (length)
5423 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005424
cristy3ed852e2009-09-05 21:47:34 +00005425 continue;
5426 }
glennrp47b9dd52010-11-24 18:12:06 +00005427
cristy3ed852e2009-09-05 21:47:34 +00005428 if (memcmp(type,mng_MOVE,4) == 0)
5429 {
cristybb503372010-05-27 20:51:26 +00005430 size_t
cristy3ed852e2009-09-05 21:47:34 +00005431 first_object,
5432 last_object;
5433
glennrp47b9dd52010-11-24 18:12:06 +00005434 /* read MOVE */
5435
cristy3ed852e2009-09-05 21:47:34 +00005436 first_object=(p[0] << 8) | p[1];
5437 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005438 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005439 {
5440 if (mng_info->exists[i] && !mng_info->frozen[i])
5441 {
5442 MngPair
5443 new_pair;
5444
5445 MngPair
5446 old_pair;
5447
5448 old_pair.a=mng_info->x_off[i];
5449 old_pair.b=mng_info->y_off[i];
5450 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5451 mng_info->x_off[i]=new_pair.a;
5452 mng_info->y_off[i]=new_pair.b;
5453 }
5454 }
glennrp47b9dd52010-11-24 18:12:06 +00005455
cristy3ed852e2009-09-05 21:47:34 +00005456 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5457 continue;
5458 }
5459
5460 if (memcmp(type,mng_LOOP,4) == 0)
5461 {
cristybb503372010-05-27 20:51:26 +00005462 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005463 loop_level=chunk[0];
5464 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005465
5466 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005467 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005468
cristy3ed852e2009-09-05 21:47:34 +00005469 if (logging != MagickFalse)
5470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005471 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5472 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005473
cristy3ed852e2009-09-05 21:47:34 +00005474 if (loop_iters == 0)
5475 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005476
cristy3ed852e2009-09-05 21:47:34 +00005477 else
5478 {
5479 mng_info->loop_jump[loop_level]=TellBlob(image);
5480 mng_info->loop_count[loop_level]=loop_iters;
5481 }
glennrp0fe50b42010-11-16 03:52:51 +00005482
cristy3ed852e2009-09-05 21:47:34 +00005483 mng_info->loop_iteration[loop_level]=0;
5484 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5485 continue;
5486 }
glennrp47b9dd52010-11-24 18:12:06 +00005487
cristy3ed852e2009-09-05 21:47:34 +00005488 if (memcmp(type,mng_ENDL,4) == 0)
5489 {
5490 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005491
cristy3ed852e2009-09-05 21:47:34 +00005492 if (skipping_loop > 0)
5493 {
5494 if (skipping_loop == loop_level)
5495 {
5496 /*
5497 Found end of zero-iteration loop.
5498 */
5499 skipping_loop=(-1);
5500 mng_info->loop_active[loop_level]=0;
5501 }
5502 }
glennrp47b9dd52010-11-24 18:12:06 +00005503
cristy3ed852e2009-09-05 21:47:34 +00005504 else
5505 {
5506 if (mng_info->loop_active[loop_level] == 1)
5507 {
5508 mng_info->loop_count[loop_level]--;
5509 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005510
cristy3ed852e2009-09-05 21:47:34 +00005511 if (logging != MagickFalse)
5512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005513 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005514 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005515 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005516
cristy3ed852e2009-09-05 21:47:34 +00005517 if (mng_info->loop_count[loop_level] != 0)
5518 {
5519 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5520 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005521
cristy3ed852e2009-09-05 21:47:34 +00005522 if (offset < 0)
5523 ThrowReaderException(CorruptImageError,
5524 "ImproperImageHeader");
5525 }
glennrp47b9dd52010-11-24 18:12:06 +00005526
cristy3ed852e2009-09-05 21:47:34 +00005527 else
5528 {
5529 short
5530 last_level;
5531
5532 /*
5533 Finished loop.
5534 */
5535 mng_info->loop_active[loop_level]=0;
5536 last_level=(-1);
5537 for (i=0; i < loop_level; i++)
5538 if (mng_info->loop_active[i] == 1)
5539 last_level=(short) i;
5540 loop_level=last_level;
5541 }
5542 }
5543 }
glennrp47b9dd52010-11-24 18:12:06 +00005544
cristy3ed852e2009-09-05 21:47:34 +00005545 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5546 continue;
5547 }
glennrp47b9dd52010-11-24 18:12:06 +00005548
cristy3ed852e2009-09-05 21:47:34 +00005549 if (memcmp(type,mng_CLON,4) == 0)
5550 {
5551 if (mng_info->clon_warning == 0)
5552 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5553 CoderError,"CLON is not implemented yet","`%s'",
5554 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005555
cristy3ed852e2009-09-05 21:47:34 +00005556 mng_info->clon_warning++;
5557 }
glennrp47b9dd52010-11-24 18:12:06 +00005558
cristy3ed852e2009-09-05 21:47:34 +00005559 if (memcmp(type,mng_MAGN,4) == 0)
5560 {
5561 png_uint_16
5562 magn_first,
5563 magn_last,
5564 magn_mb,
5565 magn_ml,
5566 magn_mr,
5567 magn_mt,
5568 magn_mx,
5569 magn_my,
5570 magn_methx,
5571 magn_methy;
5572
5573 if (length > 1)
5574 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005575
cristy3ed852e2009-09-05 21:47:34 +00005576 else
5577 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005578
cristy3ed852e2009-09-05 21:47:34 +00005579 if (length > 3)
5580 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005581
cristy3ed852e2009-09-05 21:47:34 +00005582 else
5583 magn_last=magn_first;
5584#ifndef MNG_OBJECT_BUFFERS
5585 if (magn_first || magn_last)
5586 if (mng_info->magn_warning == 0)
5587 {
5588 (void) ThrowMagickException(&image->exception,
5589 GetMagickModule(),CoderError,
5590 "MAGN is not implemented yet for nonzero objects",
5591 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005592
cristy3ed852e2009-09-05 21:47:34 +00005593 mng_info->magn_warning++;
5594 }
5595#endif
5596 if (length > 4)
5597 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005598
cristy3ed852e2009-09-05 21:47:34 +00005599 else
5600 magn_methx=0;
5601
5602 if (length > 6)
5603 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005604
cristy3ed852e2009-09-05 21:47:34 +00005605 else
5606 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005607
cristy3ed852e2009-09-05 21:47:34 +00005608 if (magn_mx == 0)
5609 magn_mx=1;
5610
5611 if (length > 8)
5612 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005613
cristy3ed852e2009-09-05 21:47:34 +00005614 else
5615 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005616
cristy3ed852e2009-09-05 21:47:34 +00005617 if (magn_my == 0)
5618 magn_my=1;
5619
5620 if (length > 10)
5621 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005622
cristy3ed852e2009-09-05 21:47:34 +00005623 else
5624 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005625
cristy3ed852e2009-09-05 21:47:34 +00005626 if (magn_ml == 0)
5627 magn_ml=1;
5628
5629 if (length > 12)
5630 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005631
cristy3ed852e2009-09-05 21:47:34 +00005632 else
5633 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005634
cristy3ed852e2009-09-05 21:47:34 +00005635 if (magn_mr == 0)
5636 magn_mr=1;
5637
5638 if (length > 14)
5639 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005640
cristy3ed852e2009-09-05 21:47:34 +00005641 else
5642 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005643
cristy3ed852e2009-09-05 21:47:34 +00005644 if (magn_mt == 0)
5645 magn_mt=1;
5646
5647 if (length > 16)
5648 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005649
cristy3ed852e2009-09-05 21:47:34 +00005650 else
5651 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005652
cristy3ed852e2009-09-05 21:47:34 +00005653 if (magn_mb == 0)
5654 magn_mb=1;
5655
5656 if (length > 17)
5657 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005658
cristy3ed852e2009-09-05 21:47:34 +00005659 else
5660 magn_methy=magn_methx;
5661
glennrp47b9dd52010-11-24 18:12:06 +00005662
cristy3ed852e2009-09-05 21:47:34 +00005663 if (magn_methx > 5 || magn_methy > 5)
5664 if (mng_info->magn_warning == 0)
5665 {
5666 (void) ThrowMagickException(&image->exception,
5667 GetMagickModule(),CoderError,
5668 "Unknown MAGN method in MNG datastream","`%s'",
5669 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005670
cristy3ed852e2009-09-05 21:47:34 +00005671 mng_info->magn_warning++;
5672 }
5673#ifdef MNG_OBJECT_BUFFERS
5674 /* Magnify existing objects in the range magn_first to magn_last */
5675#endif
5676 if (magn_first == 0 || magn_last == 0)
5677 {
5678 /* Save the magnification factors for object 0 */
5679 mng_info->magn_mb=magn_mb;
5680 mng_info->magn_ml=magn_ml;
5681 mng_info->magn_mr=magn_mr;
5682 mng_info->magn_mt=magn_mt;
5683 mng_info->magn_mx=magn_mx;
5684 mng_info->magn_my=magn_my;
5685 mng_info->magn_methx=magn_methx;
5686 mng_info->magn_methy=magn_methy;
5687 }
5688 }
glennrp47b9dd52010-11-24 18:12:06 +00005689
cristy3ed852e2009-09-05 21:47:34 +00005690 if (memcmp(type,mng_PAST,4) == 0)
5691 {
5692 if (mng_info->past_warning == 0)
5693 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5694 CoderError,"PAST is not implemented yet","`%s'",
5695 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005696
cristy3ed852e2009-09-05 21:47:34 +00005697 mng_info->past_warning++;
5698 }
glennrp47b9dd52010-11-24 18:12:06 +00005699
cristy3ed852e2009-09-05 21:47:34 +00005700 if (memcmp(type,mng_SHOW,4) == 0)
5701 {
5702 if (mng_info->show_warning == 0)
5703 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5704 CoderError,"SHOW is not implemented yet","`%s'",
5705 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005706
cristy3ed852e2009-09-05 21:47:34 +00005707 mng_info->show_warning++;
5708 }
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 if (memcmp(type,mng_sBIT,4) == 0)
5711 {
5712 if (length < 4)
5713 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005714
cristy3ed852e2009-09-05 21:47:34 +00005715 else
5716 {
5717 mng_info->global_sbit.gray=p[0];
5718 mng_info->global_sbit.red=p[0];
5719 mng_info->global_sbit.green=p[1];
5720 mng_info->global_sbit.blue=p[2];
5721 mng_info->global_sbit.alpha=p[3];
5722 mng_info->have_global_sbit=MagickTrue;
5723 }
5724 }
5725 if (memcmp(type,mng_pHYs,4) == 0)
5726 {
5727 if (length > 8)
5728 {
5729 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005730 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005731 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005732 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005733 mng_info->global_phys_unit_type=p[8];
5734 mng_info->have_global_phys=MagickTrue;
5735 }
glennrp47b9dd52010-11-24 18:12:06 +00005736
cristy3ed852e2009-09-05 21:47:34 +00005737 else
5738 mng_info->have_global_phys=MagickFalse;
5739 }
5740 if (memcmp(type,mng_pHYg,4) == 0)
5741 {
5742 if (mng_info->phyg_warning == 0)
5743 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5744 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005745
cristy3ed852e2009-09-05 21:47:34 +00005746 mng_info->phyg_warning++;
5747 }
5748 if (memcmp(type,mng_BASI,4) == 0)
5749 {
5750 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005751
cristy3ed852e2009-09-05 21:47:34 +00005752 if (mng_info->basi_warning == 0)
5753 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5754 CoderError,"BASI is not implemented yet","`%s'",
5755 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005756
cristy3ed852e2009-09-05 21:47:34 +00005757 mng_info->basi_warning++;
5758#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005759 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005760 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005761 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005762 (p[6] << 8) | p[7]);
5763 basi_color_type=p[8];
5764 basi_compression_method=p[9];
5765 basi_filter_type=p[10];
5766 basi_interlace_method=p[11];
5767 if (length > 11)
5768 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005769
cristy3ed852e2009-09-05 21:47:34 +00005770 else
5771 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005772
cristy3ed852e2009-09-05 21:47:34 +00005773 if (length > 13)
5774 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005775
cristy3ed852e2009-09-05 21:47:34 +00005776 else
5777 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005778
cristy3ed852e2009-09-05 21:47:34 +00005779 if (length > 15)
5780 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005781
cristy3ed852e2009-09-05 21:47:34 +00005782 else
5783 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005784
cristy3ed852e2009-09-05 21:47:34 +00005785 if (length > 17)
5786 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005787
cristy3ed852e2009-09-05 21:47:34 +00005788 else
5789 {
5790 if (basi_sample_depth == 16)
5791 basi_alpha=65535L;
5792 else
5793 basi_alpha=255;
5794 }
glennrp47b9dd52010-11-24 18:12:06 +00005795
cristy3ed852e2009-09-05 21:47:34 +00005796 if (length > 19)
5797 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005798
cristy3ed852e2009-09-05 21:47:34 +00005799 else
5800 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005801
cristy3ed852e2009-09-05 21:47:34 +00005802#endif
5803 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5804 continue;
5805 }
glennrp47b9dd52010-11-24 18:12:06 +00005806
cristy3ed852e2009-09-05 21:47:34 +00005807 if (memcmp(type,mng_IHDR,4)
5808#if defined(JNG_SUPPORTED)
5809 && memcmp(type,mng_JHDR,4)
5810#endif
5811 )
5812 {
5813 /* Not an IHDR or JHDR chunk */
5814 if (length)
5815 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 continue;
5818 }
5819/* Process IHDR */
5820 if (logging != MagickFalse)
5821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5822 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005823
cristy3ed852e2009-09-05 21:47:34 +00005824 mng_info->exists[object_id]=MagickTrue;
5825 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005826
cristy3ed852e2009-09-05 21:47:34 +00005827 if (mng_info->invisible[object_id])
5828 {
5829 if (logging != MagickFalse)
5830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5831 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005832
cristy3ed852e2009-09-05 21:47:34 +00005833 skip_to_iend=MagickTrue;
5834 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5835 continue;
5836 }
5837#if defined(MNG_INSERT_LAYERS)
5838 if (length < 8)
5839 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005840
cristy8182b072010-05-30 20:10:53 +00005841 image_width=(size_t) mng_get_long(p);
5842 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005843#endif
5844 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5845
5846 /*
5847 Insert a transparent background layer behind the entire animation
5848 if it is not full screen.
5849 */
5850#if defined(MNG_INSERT_LAYERS)
5851 if (insert_layers && mng_type && first_mng_object)
5852 {
5853 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5854 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005855 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005856 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005857 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005858 {
5859 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5860 {
5861 /*
5862 Allocate next image structure.
5863 */
5864 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005865
cristy3ed852e2009-09-05 21:47:34 +00005866 if (GetNextImageInList(image) == (Image *) NULL)
5867 {
5868 image=DestroyImageList(image);
5869 MngInfoFreeStruct(mng_info,&have_mng_structure);
5870 return((Image *) NULL);
5871 }
glennrp47b9dd52010-11-24 18:12:06 +00005872
cristy3ed852e2009-09-05 21:47:34 +00005873 image=SyncNextImageInList(image);
5874 }
5875 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005876
cristy3ed852e2009-09-05 21:47:34 +00005877 if (term_chunk_found)
5878 {
5879 image->start_loop=MagickTrue;
5880 image->iterations=mng_iterations;
5881 term_chunk_found=MagickFalse;
5882 }
glennrp47b9dd52010-11-24 18:12:06 +00005883
cristy3ed852e2009-09-05 21:47:34 +00005884 else
5885 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005886
5887 /* Make a background rectangle. */
5888
cristy3ed852e2009-09-05 21:47:34 +00005889 image->delay=0;
5890 image->columns=mng_info->mng_width;
5891 image->rows=mng_info->mng_height;
5892 image->page.width=mng_info->mng_width;
5893 image->page.height=mng_info->mng_height;
5894 image->page.x=0;
5895 image->page.y=0;
5896 image->background_color=mng_background_color;
5897 (void) SetImageBackgroundColor(image);
5898 if (logging != MagickFalse)
5899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005900 " Inserted transparent background layer, W=%.20g, H=%.20g",
5901 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005902 }
5903 }
5904 /*
5905 Insert a background layer behind the upcoming image if
5906 framing_mode is 3, and we haven't already inserted one.
5907 */
5908 if (insert_layers && (mng_info->framing_mode == 3) &&
5909 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5910 (simplicity & 0x08)))
5911 {
5912 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5913 {
5914 /*
5915 Allocate next image structure.
5916 */
5917 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005918
cristy3ed852e2009-09-05 21:47:34 +00005919 if (GetNextImageInList(image) == (Image *) NULL)
5920 {
5921 image=DestroyImageList(image);
5922 MngInfoFreeStruct(mng_info,&have_mng_structure);
5923 return((Image *) NULL);
5924 }
glennrp47b9dd52010-11-24 18:12:06 +00005925
cristy3ed852e2009-09-05 21:47:34 +00005926 image=SyncNextImageInList(image);
5927 }
glennrp0fe50b42010-11-16 03:52:51 +00005928
cristy3ed852e2009-09-05 21:47:34 +00005929 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005930
cristy3ed852e2009-09-05 21:47:34 +00005931 if (term_chunk_found)
5932 {
5933 image->start_loop=MagickTrue;
5934 image->iterations=mng_iterations;
5935 term_chunk_found=MagickFalse;
5936 }
glennrp0fe50b42010-11-16 03:52:51 +00005937
cristy3ed852e2009-09-05 21:47:34 +00005938 else
5939 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005940
cristy3ed852e2009-09-05 21:47:34 +00005941 image->delay=0;
5942 image->columns=subframe_width;
5943 image->rows=subframe_height;
5944 image->page.width=subframe_width;
5945 image->page.height=subframe_height;
5946 image->page.x=mng_info->clip.left;
5947 image->page.y=mng_info->clip.top;
5948 image->background_color=mng_background_color;
5949 image->matte=MagickFalse;
5950 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005951
cristy3ed852e2009-09-05 21:47:34 +00005952 if (logging != MagickFalse)
5953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005954 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005955 (double) mng_info->clip.left,(double) mng_info->clip.right,
5956 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005957 }
5958#endif /* MNG_INSERT_LAYERS */
5959 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005960
cristy3ed852e2009-09-05 21:47:34 +00005961 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5962 {
5963 /*
5964 Allocate next image structure.
5965 */
5966 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005967
cristy3ed852e2009-09-05 21:47:34 +00005968 if (GetNextImageInList(image) == (Image *) NULL)
5969 {
5970 image=DestroyImageList(image);
5971 MngInfoFreeStruct(mng_info,&have_mng_structure);
5972 return((Image *) NULL);
5973 }
glennrp47b9dd52010-11-24 18:12:06 +00005974
cristy3ed852e2009-09-05 21:47:34 +00005975 image=SyncNextImageInList(image);
5976 }
5977 mng_info->image=image;
5978 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5979 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005980
cristy3ed852e2009-09-05 21:47:34 +00005981 if (status == MagickFalse)
5982 break;
glennrp0fe50b42010-11-16 03:52:51 +00005983
cristy3ed852e2009-09-05 21:47:34 +00005984 if (term_chunk_found)
5985 {
5986 image->start_loop=MagickTrue;
5987 term_chunk_found=MagickFalse;
5988 }
glennrp0fe50b42010-11-16 03:52:51 +00005989
cristy3ed852e2009-09-05 21:47:34 +00005990 else
5991 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005992
cristy3ed852e2009-09-05 21:47:34 +00005993 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5994 {
5995 image->delay=frame_delay;
5996 frame_delay=default_frame_delay;
5997 }
glennrp0fe50b42010-11-16 03:52:51 +00005998
cristy3ed852e2009-09-05 21:47:34 +00005999 else
6000 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006001
cristy3ed852e2009-09-05 21:47:34 +00006002 image->page.width=mng_info->mng_width;
6003 image->page.height=mng_info->mng_height;
6004 image->page.x=mng_info->x_off[object_id];
6005 image->page.y=mng_info->y_off[object_id];
6006 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006007
cristy3ed852e2009-09-05 21:47:34 +00006008 /*
6009 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6010 */
glennrp47b9dd52010-11-24 18:12:06 +00006011
cristy3ed852e2009-09-05 21:47:34 +00006012 if (logging != MagickFalse)
6013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6014 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6015 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006016
cristybb503372010-05-27 20:51:26 +00006017 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006018
cristy3ed852e2009-09-05 21:47:34 +00006019 if (offset < 0)
6020 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6021 }
6022
6023 previous=image;
6024 mng_info->image=image;
6025 mng_info->mng_type=mng_type;
6026 mng_info->object_id=object_id;
6027
6028 if (memcmp(type,mng_IHDR,4) == 0)
6029 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006030
cristy3ed852e2009-09-05 21:47:34 +00006031#if defined(JNG_SUPPORTED)
6032 else
6033 image=ReadOneJNGImage(mng_info,image_info,exception);
6034#endif
6035
6036 if (image == (Image *) NULL)
6037 {
6038 if (IsImageObject(previous) != MagickFalse)
6039 {
6040 (void) DestroyImageList(previous);
6041 (void) CloseBlob(previous);
6042 }
glennrp47b9dd52010-11-24 18:12:06 +00006043
cristy3ed852e2009-09-05 21:47:34 +00006044 MngInfoFreeStruct(mng_info,&have_mng_structure);
6045 return((Image *) NULL);
6046 }
glennrp0fe50b42010-11-16 03:52:51 +00006047
cristy3ed852e2009-09-05 21:47:34 +00006048 if (image->columns == 0 || image->rows == 0)
6049 {
6050 (void) CloseBlob(image);
6051 image=DestroyImageList(image);
6052 MngInfoFreeStruct(mng_info,&have_mng_structure);
6053 return((Image *) NULL);
6054 }
glennrp0fe50b42010-11-16 03:52:51 +00006055
cristy3ed852e2009-09-05 21:47:34 +00006056 mng_info->image=image;
6057
6058 if (mng_type)
6059 {
6060 MngBox
6061 crop_box;
6062
6063 if (mng_info->magn_methx || mng_info->magn_methy)
6064 {
6065 png_uint_32
6066 magnified_height,
6067 magnified_width;
6068
6069 if (logging != MagickFalse)
6070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6071 " Processing MNG MAGN chunk");
6072
6073 if (mng_info->magn_methx == 1)
6074 {
6075 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006076
cristy3ed852e2009-09-05 21:47:34 +00006077 if (image->columns > 1)
6078 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006079
cristy3ed852e2009-09-05 21:47:34 +00006080 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006081 magnified_width += (png_uint_32)
6082 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006083 }
glennrp47b9dd52010-11-24 18:12:06 +00006084
cristy3ed852e2009-09-05 21:47:34 +00006085 else
6086 {
cristy4e5bc842010-06-09 13:56:01 +00006087 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006088
cristy3ed852e2009-09-05 21:47:34 +00006089 if (image->columns > 1)
6090 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006091
cristy3ed852e2009-09-05 21:47:34 +00006092 if (image->columns > 2)
6093 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006094
cristy3ed852e2009-09-05 21:47:34 +00006095 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006096 magnified_width += (png_uint_32)
6097 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006098 }
glennrp47b9dd52010-11-24 18:12:06 +00006099
cristy3ed852e2009-09-05 21:47:34 +00006100 if (mng_info->magn_methy == 1)
6101 {
6102 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006103
cristy3ed852e2009-09-05 21:47:34 +00006104 if (image->rows > 1)
6105 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006106
cristy3ed852e2009-09-05 21:47:34 +00006107 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006108 magnified_height += (png_uint_32)
6109 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006110 }
glennrp47b9dd52010-11-24 18:12:06 +00006111
cristy3ed852e2009-09-05 21:47:34 +00006112 else
6113 {
cristy4e5bc842010-06-09 13:56:01 +00006114 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006115
cristy3ed852e2009-09-05 21:47:34 +00006116 if (image->rows > 1)
6117 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006118
cristy3ed852e2009-09-05 21:47:34 +00006119 if (image->rows > 2)
6120 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006121
cristy3ed852e2009-09-05 21:47:34 +00006122 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006123 magnified_height += (png_uint_32)
6124 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006125 }
glennrp47b9dd52010-11-24 18:12:06 +00006126
cristy3ed852e2009-09-05 21:47:34 +00006127 if (magnified_height > image->rows ||
6128 magnified_width > image->columns)
6129 {
6130 Image
6131 *large_image;
6132
6133 int
6134 yy;
6135
cristybb503372010-05-27 20:51:26 +00006136 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006137 m,
6138 y;
6139
cristybb503372010-05-27 20:51:26 +00006140 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006141 x;
6142
6143 register PixelPacket
6144 *n,
6145 *q;
6146
6147 PixelPacket
6148 *next,
6149 *prev;
6150
6151 png_uint_16
6152 magn_methx,
6153 magn_methy;
6154
glennrp47b9dd52010-11-24 18:12:06 +00006155 /* Allocate next image structure. */
6156
cristy3ed852e2009-09-05 21:47:34 +00006157 if (logging != MagickFalse)
6158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6159 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006160
cristy3ed852e2009-09-05 21:47:34 +00006161 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00006162
cristy3ed852e2009-09-05 21:47:34 +00006163 if (GetNextImageInList(image) == (Image *) NULL)
6164 {
6165 image=DestroyImageList(image);
6166 MngInfoFreeStruct(mng_info,&have_mng_structure);
6167 return((Image *) NULL);
6168 }
6169
6170 large_image=SyncNextImageInList(image);
6171
6172 large_image->columns=magnified_width;
6173 large_image->rows=magnified_height;
6174
6175 magn_methx=mng_info->magn_methx;
6176 magn_methy=mng_info->magn_methy;
6177
glennrp3faa9a32011-04-23 14:00:25 +00006178#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006179#define QM unsigned short
6180 if (magn_methx != 1 || magn_methy != 1)
6181 {
6182 /*
6183 Scale pixels to unsigned shorts to prevent
6184 overflow of intermediate values of interpolations
6185 */
cristybb503372010-05-27 20:51:26 +00006186 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006187 {
6188 q=GetAuthenticPixels(image,0,y,image->columns,1,
6189 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006190
cristybb503372010-05-27 20:51:26 +00006191 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006192 {
cristyef618312011-06-25 12:26:44 +00006193 SetPixelRed(q,ScaleQuantumToShort(
6194 GetPixelRed(q)));
6195 SetPixelGreen(q,ScaleQuantumToShort(
6196 GetPixelGreen(q)));
6197 SetPixelBlue(q,ScaleQuantumToShort(
6198 GetPixelBlue(q)));
6199 SetPixelOpacity(q,ScaleQuantumToShort(
6200 GetPixelOpacity(q)));
cristy3ed852e2009-09-05 21:47:34 +00006201 q++;
6202 }
glennrp47b9dd52010-11-24 18:12:06 +00006203
cristy3ed852e2009-09-05 21:47:34 +00006204 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6205 break;
6206 }
6207 }
6208#else
6209#define QM Quantum
6210#endif
6211
6212 if (image->matte != MagickFalse)
6213 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006214
cristy3ed852e2009-09-05 21:47:34 +00006215 else
6216 {
6217 large_image->background_color.opacity=OpaqueOpacity;
6218 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006219
cristy3ed852e2009-09-05 21:47:34 +00006220 if (magn_methx == 4)
6221 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006222
cristy3ed852e2009-09-05 21:47:34 +00006223 if (magn_methx == 5)
6224 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006225
cristy3ed852e2009-09-05 21:47:34 +00006226 if (magn_methy == 4)
6227 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006228
cristy3ed852e2009-09-05 21:47:34 +00006229 if (magn_methy == 5)
6230 magn_methy=3;
6231 }
6232
6233 /* magnify the rows into the right side of the large image */
6234
6235 if (logging != MagickFalse)
6236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006237 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006238 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006239 yy=0;
6240 length=(size_t) image->columns;
6241 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
6242 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006243
cristy3ed852e2009-09-05 21:47:34 +00006244 if ((prev == (PixelPacket *) NULL) ||
6245 (next == (PixelPacket *) NULL))
6246 {
6247 image=DestroyImageList(image);
6248 MngInfoFreeStruct(mng_info,&have_mng_structure);
6249 ThrowReaderException(ResourceLimitError,
6250 "MemoryAllocationFailed");
6251 }
glennrp47b9dd52010-11-24 18:12:06 +00006252
cristy3ed852e2009-09-05 21:47:34 +00006253 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6254 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006255
cristybb503372010-05-27 20:51:26 +00006256 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006257 {
6258 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006259 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006260
cristybb503372010-05-27 20:51:26 +00006261 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6262 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006263
cristybb503372010-05-27 20:51:26 +00006264 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6265 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006266
cristybb503372010-05-27 20:51:26 +00006267 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006268 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006269
cristy3ed852e2009-09-05 21:47:34 +00006270 else
cristybb503372010-05-27 20:51:26 +00006271 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006272
cristy3ed852e2009-09-05 21:47:34 +00006273 n=prev;
6274 prev=next;
6275 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006276
cristybb503372010-05-27 20:51:26 +00006277 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006278 {
6279 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6280 exception);
6281 (void) CopyMagickMemory(next,n,length);
6282 }
glennrp47b9dd52010-11-24 18:12:06 +00006283
cristy3ed852e2009-09-05 21:47:34 +00006284 for (i=0; i < m; i++, yy++)
6285 {
6286 register PixelPacket
6287 *pixels;
6288
cristybb503372010-05-27 20:51:26 +00006289 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006290 pixels=prev;
6291 n=next;
6292 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006293 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00006294 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00006295
cristybb503372010-05-27 20:51:26 +00006296 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006297 {
glennrpfd05d622011-02-25 04:10:33 +00006298 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006299 /*
6300 if (image->storage_class == PseudoClass)
6301 {
6302 }
6303 */
6304
6305 if (magn_methy <= 1)
6306 {
glennrpbb4f99d2011-05-22 11:13:17 +00006307 /* replicate previous */
cristyef618312011-06-25 12:26:44 +00006308 SetPixelRGBO(q,(pixels));
cristy3ed852e2009-09-05 21:47:34 +00006309 }
glennrp47b9dd52010-11-24 18:12:06 +00006310
cristy3ed852e2009-09-05 21:47:34 +00006311 else if (magn_methy == 2 || magn_methy == 4)
6312 {
6313 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006314 {
cristyef618312011-06-25 12:26:44 +00006315 SetPixelRGBO(q,(pixels));
glennrpbb4f99d2011-05-22 11:13:17 +00006316 }
glennrp47b9dd52010-11-24 18:12:06 +00006317
cristy3ed852e2009-09-05 21:47:34 +00006318 else
6319 {
6320 /* Interpolate */
cristyef618312011-06-25 12:26:44 +00006321 SetPixelRed(q,
glennrpbb4f99d2011-05-22 11:13:17 +00006322 ((QM) (((ssize_t)
cristyef618312011-06-25 12:26:44 +00006323 (2*i*(GetPixelRed(n)
6324 -GetPixelRed(pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006325 ((ssize_t) (m*2))
cristyef618312011-06-25 12:26:44 +00006326 +GetPixelRed(pixels)))));
6327 SetPixelGreen(q,
glennrpbb4f99d2011-05-22 11:13:17 +00006328 ((QM) (((ssize_t)
cristyef618312011-06-25 12:26:44 +00006329 (2*i*(GetPixelGreen(n)
6330 -GetPixelGreen(pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006331 ((ssize_t) (m*2))
cristyef618312011-06-25 12:26:44 +00006332 +GetPixelGreen(pixels)))));
6333 SetPixelBlue(q,
glennrpbb4f99d2011-05-22 11:13:17 +00006334 ((QM) (((ssize_t)
cristyef618312011-06-25 12:26:44 +00006335 (2*i*(GetPixelBlue(n)
6336 -GetPixelBlue(pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006337 ((ssize_t) (m*2))
cristyef618312011-06-25 12:26:44 +00006338 +GetPixelBlue(pixels)))));
glennrp47b9dd52010-11-24 18:12:06 +00006339
cristy3ed852e2009-09-05 21:47:34 +00006340 if (image->matte != MagickFalse)
cristyef618312011-06-25 12:26:44 +00006341 SetPixelOpacity(q,
glennrpbb4f99d2011-05-22 11:13:17 +00006342 ((QM) (((ssize_t)
cristyef618312011-06-25 12:26:44 +00006343 (2*i*(GetPixelOpacity(n)
6344 -GetPixelOpacity(pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006345 /((ssize_t) (m*2))+
cristyef618312011-06-25 12:26:44 +00006346 GetPixelOpacity(pixels)))));
cristy3ed852e2009-09-05 21:47:34 +00006347 }
glennrp47b9dd52010-11-24 18:12:06 +00006348
cristy3ed852e2009-09-05 21:47:34 +00006349 if (magn_methy == 4)
6350 {
6351 /* Replicate nearest */
6352 if (i <= ((m+1) << 1))
cristyef618312011-06-25 12:26:44 +00006353 SetPixelOpacity(q,
glennrpbb4f99d2011-05-22 11:13:17 +00006354 (*pixels).opacity+0);
cristy3ed852e2009-09-05 21:47:34 +00006355 else
cristyef618312011-06-25 12:26:44 +00006356 SetPixelOpacity(q,
glennrpbb4f99d2011-05-22 11:13:17 +00006357 (*n).opacity+0);
cristy3ed852e2009-09-05 21:47:34 +00006358 }
6359 }
glennrp47b9dd52010-11-24 18:12:06 +00006360
cristy3ed852e2009-09-05 21:47:34 +00006361 else /* if (magn_methy == 3 || magn_methy == 5) */
6362 {
6363 /* Replicate nearest */
6364 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006365 {
cristyef618312011-06-25 12:26:44 +00006366 SetPixelRGBO(q,(pixels));
glennrpbb4f99d2011-05-22 11:13:17 +00006367 }
glennrp47b9dd52010-11-24 18:12:06 +00006368
cristy3ed852e2009-09-05 21:47:34 +00006369 else
glennrpbb4f99d2011-05-22 11:13:17 +00006370 {
cristyef618312011-06-25 12:26:44 +00006371 SetPixelRGBO(q,(n));
glennrpbb4f99d2011-05-22 11:13:17 +00006372 }
glennrp47b9dd52010-11-24 18:12:06 +00006373
cristy3ed852e2009-09-05 21:47:34 +00006374 if (magn_methy == 5)
6375 {
cristyef618312011-06-25 12:26:44 +00006376 SetPixelOpacity(q,
glennrpbb4f99d2011-05-22 11:13:17 +00006377 (QM) (((ssize_t) (2*i*
cristyef618312011-06-25 12:26:44 +00006378 (GetPixelOpacity(n)
6379 -GetPixelOpacity(pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006380 +m))/((ssize_t) (m*2))
cristyef618312011-06-25 12:26:44 +00006381 +GetPixelOpacity(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006382 }
6383 }
6384 n++;
6385 q++;
6386 pixels++;
6387 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006388
cristy3ed852e2009-09-05 21:47:34 +00006389 if (SyncAuthenticPixels(large_image,exception) == 0)
6390 break;
glennrp47b9dd52010-11-24 18:12:06 +00006391
cristy3ed852e2009-09-05 21:47:34 +00006392 } /* i */
6393 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006394
cristy3ed852e2009-09-05 21:47:34 +00006395 prev=(PixelPacket *) RelinquishMagickMemory(prev);
6396 next=(PixelPacket *) RelinquishMagickMemory(next);
6397
6398 length=image->columns;
6399
6400 if (logging != MagickFalse)
6401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6402 " Delete original image");
6403
6404 DeleteImageFromList(&image);
6405
6406 image=large_image;
6407
6408 mng_info->image=image;
6409
6410 /* magnify the columns */
6411 if (logging != MagickFalse)
6412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006413 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006414
cristybb503372010-05-27 20:51:26 +00006415 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006416 {
6417 register PixelPacket
6418 *pixels;
6419
6420 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6421 pixels=q+(image->columns-length);
6422 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00006423
cristybb503372010-05-27 20:51:26 +00006424 for (x=(ssize_t) (image->columns-length);
6425 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006426 {
glennrp7c7b3152011-04-26 04:01:27 +00006427 /* To do: Rewrite using Get/Set***PixelComponent() */
6428
cristybb503372010-05-27 20:51:26 +00006429 if (x == (ssize_t) (image->columns-length))
6430 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006431
cristybb503372010-05-27 20:51:26 +00006432 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6433 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006434
cristybb503372010-05-27 20:51:26 +00006435 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6436 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006437
cristybb503372010-05-27 20:51:26 +00006438 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006439 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006440
cristy3ed852e2009-09-05 21:47:34 +00006441 else
cristybb503372010-05-27 20:51:26 +00006442 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006443
cristy3ed852e2009-09-05 21:47:34 +00006444 for (i=0; i < m; i++)
6445 {
6446 if (magn_methx <= 1)
6447 {
6448 /* replicate previous */
cristyef618312011-06-25 12:26:44 +00006449 SetPixelRGBO(q,(pixels));
cristy3ed852e2009-09-05 21:47:34 +00006450 }
glennrp47b9dd52010-11-24 18:12:06 +00006451
cristy3ed852e2009-09-05 21:47:34 +00006452 else if (magn_methx == 2 || magn_methx == 4)
6453 {
6454 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006455 {
cristyef618312011-06-25 12:26:44 +00006456 SetPixelRGBO(q,(pixels));
glennrpbb4f99d2011-05-22 11:13:17 +00006457 }
glennrp47b9dd52010-11-24 18:12:06 +00006458
glennrpbb4f99d2011-05-22 11:13:17 +00006459 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00006460 else
6461 {
6462 /* Interpolate */
cristyef618312011-06-25 12:26:44 +00006463 SetPixelRed(q,
glennrpbb4f99d2011-05-22 11:13:17 +00006464 (QM) ((2*i*(
cristyef618312011-06-25 12:26:44 +00006465 GetPixelRed(n)
6466 -GetPixelRed(pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006467 /((ssize_t) (m*2))+
cristyef618312011-06-25 12:26:44 +00006468 GetPixelRed(pixels)));
glennrpbb4f99d2011-05-22 11:13:17 +00006469
cristyef618312011-06-25 12:26:44 +00006470 SetPixelGreen(q,
glennrpbb4f99d2011-05-22 11:13:17 +00006471 (QM) ((2*i*(
cristyef618312011-06-25 12:26:44 +00006472 GetPixelGreen(n)
6473 -GetPixelGreen(pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006474 /((ssize_t) (m*2))+
cristyef618312011-06-25 12:26:44 +00006475 GetPixelGreen(pixels)));
glennrpbb4f99d2011-05-22 11:13:17 +00006476
cristyef618312011-06-25 12:26:44 +00006477 SetPixelBlue(q,
glennrpbb4f99d2011-05-22 11:13:17 +00006478 (QM) ((2*i*(
cristyef618312011-06-25 12:26:44 +00006479 GetPixelBlue(n)
6480 -GetPixelBlue(pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006481 /((ssize_t) (m*2))+
cristyef618312011-06-25 12:26:44 +00006482 GetPixelBlue(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006483 if (image->matte != MagickFalse)
cristyef618312011-06-25 12:26:44 +00006484 SetPixelOpacity(q,
glennrpbb4f99d2011-05-22 11:13:17 +00006485 (QM) ((2*i*(
cristyef618312011-06-25 12:26:44 +00006486 GetPixelOpacity(n)
6487 -GetPixelOpacity(pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006488 /((ssize_t) (m*2))+
cristyef618312011-06-25 12:26:44 +00006489 GetPixelOpacity(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006490 }
glennrp47b9dd52010-11-24 18:12:06 +00006491
cristy3ed852e2009-09-05 21:47:34 +00006492 if (magn_methx == 4)
6493 {
6494 /* Replicate nearest */
6495 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006496 {
cristyef618312011-06-25 12:26:44 +00006497 SetPixelOpacity(q,
6498 GetPixelOpacity(pixels)+0);
glennrpbb4f99d2011-05-22 11:13:17 +00006499 }
cristy3ed852e2009-09-05 21:47:34 +00006500 else
glennrpbb4f99d2011-05-22 11:13:17 +00006501 {
cristyef618312011-06-25 12:26:44 +00006502 SetPixelOpacity(q,
6503 GetPixelOpacity(n)+0);
glennrpbb4f99d2011-05-22 11:13:17 +00006504 }
cristy3ed852e2009-09-05 21:47:34 +00006505 }
6506 }
glennrp47b9dd52010-11-24 18:12:06 +00006507
cristy3ed852e2009-09-05 21:47:34 +00006508 else /* if (magn_methx == 3 || magn_methx == 5) */
6509 {
6510 /* Replicate nearest */
6511 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006512 {
cristyef618312011-06-25 12:26:44 +00006513 SetPixelRGBO(q,(pixels));
glennrpbb4f99d2011-05-22 11:13:17 +00006514 }
glennrp47b9dd52010-11-24 18:12:06 +00006515
cristy3ed852e2009-09-05 21:47:34 +00006516 else
glennrpbb4f99d2011-05-22 11:13:17 +00006517 {
cristyef618312011-06-25 12:26:44 +00006518 SetPixelRGBO(q,(n));
glennrpbb4f99d2011-05-22 11:13:17 +00006519 }
glennrp47b9dd52010-11-24 18:12:06 +00006520
cristy3ed852e2009-09-05 21:47:34 +00006521 if (magn_methx == 5)
6522 {
6523 /* Interpolate */
cristyef618312011-06-25 12:26:44 +00006524 SetPixelOpacity(q,
6525 (QM) ((2*i*( GetPixelOpacity(n)
6526 -GetPixelOpacity(pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006527 ((ssize_t) (m*2))
cristyef618312011-06-25 12:26:44 +00006528 +GetPixelOpacity(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006529 }
6530 }
6531 q++;
6532 }
6533 n++;
6534 p++;
6535 }
glennrp47b9dd52010-11-24 18:12:06 +00006536
cristy3ed852e2009-09-05 21:47:34 +00006537 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6538 break;
6539 }
glennrp3faa9a32011-04-23 14:00:25 +00006540#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006541 if (magn_methx != 1 || magn_methy != 1)
6542 {
6543 /*
6544 Rescale pixels to Quantum
6545 */
cristybb503372010-05-27 20:51:26 +00006546 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006547 {
6548 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006549
cristybb503372010-05-27 20:51:26 +00006550 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006551 {
cristyef618312011-06-25 12:26:44 +00006552 SetPixelRed(q,ScaleShortToQuantum(
6553 GetPixelRed(q)));
6554 SetPixelGreen(q,ScaleShortToQuantum(
6555 GetPixelGreen(q)));
6556 SetPixelBlue(q,ScaleShortToQuantum(
6557 GetPixelBlue(q)));
6558 SetPixelOpacity(q,ScaleShortToQuantum(
6559 GetPixelOpacity(q)));
cristy3ed852e2009-09-05 21:47:34 +00006560 q++;
6561 }
glennrp47b9dd52010-11-24 18:12:06 +00006562
cristy3ed852e2009-09-05 21:47:34 +00006563 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6564 break;
6565 }
6566 }
6567#endif
6568 if (logging != MagickFalse)
6569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6570 " Finished MAGN processing");
6571 }
6572 }
6573
6574 /*
6575 Crop_box is with respect to the upper left corner of the MNG.
6576 */
6577 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6578 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6579 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6580 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6581 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6582 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6583 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6584 if ((crop_box.left != (mng_info->image_box.left
6585 +mng_info->x_off[object_id])) ||
6586 (crop_box.right != (mng_info->image_box.right
6587 +mng_info->x_off[object_id])) ||
6588 (crop_box.top != (mng_info->image_box.top
6589 +mng_info->y_off[object_id])) ||
6590 (crop_box.bottom != (mng_info->image_box.bottom
6591 +mng_info->y_off[object_id])))
6592 {
6593 if (logging != MagickFalse)
6594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6595 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006596
cristy3ed852e2009-09-05 21:47:34 +00006597 if ((crop_box.left < crop_box.right) &&
6598 (crop_box.top < crop_box.bottom))
6599 {
6600 Image
6601 *im;
6602
6603 RectangleInfo
6604 crop_info;
6605
6606 /*
6607 Crop_info is with respect to the upper left corner of
6608 the image.
6609 */
6610 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6611 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006612 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6613 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006614 image->page.width=image->columns;
6615 image->page.height=image->rows;
6616 image->page.x=0;
6617 image->page.y=0;
6618 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006619
cristy3ed852e2009-09-05 21:47:34 +00006620 if (im != (Image *) NULL)
6621 {
6622 image->columns=im->columns;
6623 image->rows=im->rows;
6624 im=DestroyImage(im);
6625 image->page.width=image->columns;
6626 image->page.height=image->rows;
6627 image->page.x=crop_box.left;
6628 image->page.y=crop_box.top;
6629 }
6630 }
glennrp47b9dd52010-11-24 18:12:06 +00006631
cristy3ed852e2009-09-05 21:47:34 +00006632 else
6633 {
6634 /*
6635 No pixels in crop area. The MNG spec still requires
6636 a layer, though, so make a single transparent pixel in
6637 the top left corner.
6638 */
6639 image->columns=1;
6640 image->rows=1;
6641 image->colors=2;
6642 (void) SetImageBackgroundColor(image);
6643 image->page.width=1;
6644 image->page.height=1;
6645 image->page.x=0;
6646 image->page.y=0;
6647 }
6648 }
6649#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6650 image=mng_info->image;
6651#endif
6652 }
6653
glennrp2b013e42010-11-24 16:55:50 +00006654#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6655 /* PNG does not handle depths greater than 16 so reduce it even
6656 * if lossy
6657 */
6658 if (image->depth > 16)
6659 image->depth=16;
6660#endif
6661
glennrp3faa9a32011-04-23 14:00:25 +00006662#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006663 if (LosslessReduceDepthOK(image) != MagickFalse)
6664 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006665#endif
glennrpd6afd542010-11-19 01:53:05 +00006666
cristy3ed852e2009-09-05 21:47:34 +00006667 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006668
cristy3ed852e2009-09-05 21:47:34 +00006669 if (image_info->number_scenes != 0)
6670 {
6671 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006672 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006673 break;
6674 }
glennrpd6afd542010-11-19 01:53:05 +00006675
cristy3ed852e2009-09-05 21:47:34 +00006676 if (logging != MagickFalse)
6677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6678 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006679
cristy3ed852e2009-09-05 21:47:34 +00006680 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006681
cristy3ed852e2009-09-05 21:47:34 +00006682 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006683
cristy3ed852e2009-09-05 21:47:34 +00006684 if (logging != MagickFalse)
6685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6686 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006687
cristy3ed852e2009-09-05 21:47:34 +00006688#if defined(MNG_INSERT_LAYERS)
6689 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6690 (mng_info->mng_height))
6691 {
6692 /*
6693 Insert a background layer if nothing else was found.
6694 */
6695 if (logging != MagickFalse)
6696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6697 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006698
cristy3ed852e2009-09-05 21:47:34 +00006699 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6700 {
6701 /*
6702 Allocate next image structure.
6703 */
6704 AcquireNextImage(image_info,image);
6705 if (GetNextImageInList(image) == (Image *) NULL)
6706 {
6707 image=DestroyImageList(image);
6708 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006709
cristy3ed852e2009-09-05 21:47:34 +00006710 if (logging != MagickFalse)
6711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6712 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006713
cristy3ed852e2009-09-05 21:47:34 +00006714 return((Image *) NULL);
6715 }
6716 image=SyncNextImageInList(image);
6717 }
6718 image->columns=mng_info->mng_width;
6719 image->rows=mng_info->mng_height;
6720 image->page.width=mng_info->mng_width;
6721 image->page.height=mng_info->mng_height;
6722 image->page.x=0;
6723 image->page.y=0;
6724 image->background_color=mng_background_color;
6725 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006726
cristy3ed852e2009-09-05 21:47:34 +00006727 if (image_info->ping == MagickFalse)
6728 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006729
cristy3ed852e2009-09-05 21:47:34 +00006730 mng_info->image_found++;
6731 }
6732#endif
6733 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006734
cristy3ed852e2009-09-05 21:47:34 +00006735 if (mng_iterations == 1)
6736 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006737
cristy3ed852e2009-09-05 21:47:34 +00006738 while (GetPreviousImageInList(image) != (Image *) NULL)
6739 {
6740 image_count++;
6741 if (image_count > 10*mng_info->image_found)
6742 {
6743 if (logging != MagickFalse)
6744 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006745
cristy3ed852e2009-09-05 21:47:34 +00006746 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6747 CoderError,"Linked list is corrupted, beginning of list not found",
6748 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006749
cristy3ed852e2009-09-05 21:47:34 +00006750 return((Image *) NULL);
6751 }
glennrp0fe50b42010-11-16 03:52:51 +00006752
cristy3ed852e2009-09-05 21:47:34 +00006753 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006754
cristy3ed852e2009-09-05 21:47:34 +00006755 if (GetNextImageInList(image) == (Image *) NULL)
6756 {
6757 if (logging != MagickFalse)
6758 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006759
cristy3ed852e2009-09-05 21:47:34 +00006760 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6761 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6762 image_info->filename);
6763 }
6764 }
glennrp47b9dd52010-11-24 18:12:06 +00006765
cristy3ed852e2009-09-05 21:47:34 +00006766 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6767 GetNextImageInList(image) ==
6768 (Image *) NULL)
6769 {
6770 if (logging != MagickFalse)
6771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6772 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006773
cristy3ed852e2009-09-05 21:47:34 +00006774 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6775 CoderError,"image->next for first image is NULL but shouldn't be.",
6776 "`%s'",image_info->filename);
6777 }
glennrp47b9dd52010-11-24 18:12:06 +00006778
cristy3ed852e2009-09-05 21:47:34 +00006779 if (mng_info->image_found == 0)
6780 {
6781 if (logging != MagickFalse)
6782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6783 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006784
cristy3ed852e2009-09-05 21:47:34 +00006785 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6786 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006787
cristy3ed852e2009-09-05 21:47:34 +00006788 if (image != (Image *) NULL)
6789 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006790
cristy3ed852e2009-09-05 21:47:34 +00006791 MngInfoFreeStruct(mng_info,&have_mng_structure);
6792 return((Image *) NULL);
6793 }
6794
6795 if (mng_info->ticks_per_second)
6796 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6797 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006798
cristy3ed852e2009-09-05 21:47:34 +00006799 else
6800 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006801
cristy3ed852e2009-09-05 21:47:34 +00006802 /* Find final nonzero image delay */
6803 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006804
cristy3ed852e2009-09-05 21:47:34 +00006805 while (GetNextImageInList(image) != (Image *) NULL)
6806 {
6807 if (image->delay)
6808 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006809
cristy3ed852e2009-09-05 21:47:34 +00006810 image=GetNextImageInList(image);
6811 }
glennrp0fe50b42010-11-16 03:52:51 +00006812
cristy3ed852e2009-09-05 21:47:34 +00006813 if (final_delay < final_image_delay)
6814 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006815
cristy3ed852e2009-09-05 21:47:34 +00006816 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006817
cristy3ed852e2009-09-05 21:47:34 +00006818 if (logging != MagickFalse)
6819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006820 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6821 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006822
cristy3ed852e2009-09-05 21:47:34 +00006823 if (logging != MagickFalse)
6824 {
6825 int
6826 scene;
6827
6828 scene=0;
6829 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006830
cristy3ed852e2009-09-05 21:47:34 +00006831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6832 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006833
cristy3ed852e2009-09-05 21:47:34 +00006834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006835 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006836
cristy3ed852e2009-09-05 21:47:34 +00006837 while (GetNextImageInList(image) != (Image *) NULL)
6838 {
6839 image=GetNextImageInList(image);
6840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006841 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006842 }
6843 }
6844
6845 image=GetFirstImageInList(image);
6846#ifdef MNG_COALESCE_LAYERS
6847 if (insert_layers)
6848 {
6849 Image
6850 *next_image,
6851 *next;
6852
cristybb503372010-05-27 20:51:26 +00006853 size_t
cristy3ed852e2009-09-05 21:47:34 +00006854 scene;
6855
6856 if (logging != MagickFalse)
6857 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006858
cristy3ed852e2009-09-05 21:47:34 +00006859 scene=image->scene;
6860 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006861
cristy3ed852e2009-09-05 21:47:34 +00006862 if (next_image == (Image *) NULL)
6863 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006864
cristy3ed852e2009-09-05 21:47:34 +00006865 image=DestroyImageList(image);
6866 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006867
cristy3ed852e2009-09-05 21:47:34 +00006868 for (next=image; next != (Image *) NULL; next=next_image)
6869 {
6870 next->page.width=mng_info->mng_width;
6871 next->page.height=mng_info->mng_height;
6872 next->page.x=0;
6873 next->page.y=0;
6874 next->scene=scene++;
6875 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006876
cristy3ed852e2009-09-05 21:47:34 +00006877 if (next_image == (Image *) NULL)
6878 break;
glennrp47b9dd52010-11-24 18:12:06 +00006879
cristy3ed852e2009-09-05 21:47:34 +00006880 if (next->delay == 0)
6881 {
6882 scene--;
6883 next_image->previous=GetPreviousImageInList(next);
6884 if (GetPreviousImageInList(next) == (Image *) NULL)
6885 image=next_image;
6886 else
6887 next->previous->next=next_image;
6888 next=DestroyImage(next);
6889 }
6890 }
6891 }
6892#endif
6893
6894 while (GetNextImageInList(image) != (Image *) NULL)
6895 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006896
cristy3ed852e2009-09-05 21:47:34 +00006897 image->dispose=BackgroundDispose;
6898
6899 if (logging != MagickFalse)
6900 {
6901 int
6902 scene;
6903
6904 scene=0;
6905 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006906
cristy3ed852e2009-09-05 21:47:34 +00006907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6908 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006909
cristy3ed852e2009-09-05 21:47:34 +00006910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006911 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6912 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006913
cristy3ed852e2009-09-05 21:47:34 +00006914 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006915 {
6916 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006917
cristyf2faecf2010-05-28 19:19:36 +00006918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006919 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6920 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006921 }
6922 }
glennrp47b9dd52010-11-24 18:12:06 +00006923
cristy3ed852e2009-09-05 21:47:34 +00006924 image=GetFirstImageInList(image);
6925 MngInfoFreeStruct(mng_info,&have_mng_structure);
6926 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006927
cristy3ed852e2009-09-05 21:47:34 +00006928 if (logging != MagickFalse)
6929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006930
cristy3ed852e2009-09-05 21:47:34 +00006931 return(GetFirstImageInList(image));
6932}
glennrp25c1e2b2010-03-25 01:39:56 +00006933#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006934static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6935{
6936 printf("Your PNG library is too old: You have libpng-%s\n",
6937 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006938
cristy3ed852e2009-09-05 21:47:34 +00006939 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6940 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006941
cristy3ed852e2009-09-05 21:47:34 +00006942 return(Image *) NULL;
6943}
glennrp47b9dd52010-11-24 18:12:06 +00006944
cristy3ed852e2009-09-05 21:47:34 +00006945static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6946{
6947 return(ReadPNGImage(image_info,exception));
6948}
glennrp25c1e2b2010-03-25 01:39:56 +00006949#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006950#endif
6951
6952/*
6953%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6954% %
6955% %
6956% %
6957% R e g i s t e r P N G I m a g e %
6958% %
6959% %
6960% %
6961%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6962%
6963% RegisterPNGImage() adds properties for the PNG image format to
6964% the list of supported formats. The properties include the image format
6965% tag, a method to read and/or write the format, whether the format
6966% supports the saving of more than one frame to the same file or blob,
6967% whether the format supports native in-memory I/O, and a brief
6968% description of the format.
6969%
6970% The format of the RegisterPNGImage method is:
6971%
cristybb503372010-05-27 20:51:26 +00006972% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006973%
6974*/
cristybb503372010-05-27 20:51:26 +00006975ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006976{
6977 char
6978 version[MaxTextExtent];
6979
6980 MagickInfo
6981 *entry;
6982
6983 static const char
6984 *PNGNote=
6985 {
6986 "See http://www.libpng.org/ for details about the PNG format."
6987 },
glennrp47b9dd52010-11-24 18:12:06 +00006988
cristy3ed852e2009-09-05 21:47:34 +00006989 *JNGNote=
6990 {
6991 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
6992 "format."
6993 },
glennrp47b9dd52010-11-24 18:12:06 +00006994
cristy3ed852e2009-09-05 21:47:34 +00006995 *MNGNote=
6996 {
6997 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
6998 "format."
6999 };
7000
7001 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007002
cristy3ed852e2009-09-05 21:47:34 +00007003#if defined(PNG_LIBPNG_VER_STRING)
7004 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7005 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007006
cristy3ed852e2009-09-05 21:47:34 +00007007 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7008 {
7009 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7010 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7011 MaxTextExtent);
7012 }
7013#endif
glennrp47b9dd52010-11-24 18:12:06 +00007014
cristy3ed852e2009-09-05 21:47:34 +00007015 entry=SetMagickInfo("MNG");
7016 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007017
cristy3ed852e2009-09-05 21:47:34 +00007018#if defined(MAGICKCORE_PNG_DELEGATE)
7019 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7020 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7021#endif
glennrp47b9dd52010-11-24 18:12:06 +00007022
cristy3ed852e2009-09-05 21:47:34 +00007023 entry->magick=(IsImageFormatHandler *) IsMNG;
7024 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007025
cristy3ed852e2009-09-05 21:47:34 +00007026 if (*version != '\0')
7027 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007028
cristy3ed852e2009-09-05 21:47:34 +00007029 entry->module=ConstantString("PNG");
7030 entry->note=ConstantString(MNGNote);
7031 (void) RegisterMagickInfo(entry);
7032
7033 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007034
cristy3ed852e2009-09-05 21:47:34 +00007035#if defined(MAGICKCORE_PNG_DELEGATE)
7036 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7037 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7038#endif
glennrp47b9dd52010-11-24 18:12:06 +00007039
cristy3ed852e2009-09-05 21:47:34 +00007040 entry->magick=(IsImageFormatHandler *) IsPNG;
7041 entry->adjoin=MagickFalse;
7042 entry->description=ConstantString("Portable Network Graphics");
7043 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007044
cristy3ed852e2009-09-05 21:47:34 +00007045 if (*version != '\0')
7046 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007047
cristy3ed852e2009-09-05 21:47:34 +00007048 entry->note=ConstantString(PNGNote);
7049 (void) RegisterMagickInfo(entry);
7050
7051 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007052
cristy3ed852e2009-09-05 21:47:34 +00007053#if defined(MAGICKCORE_PNG_DELEGATE)
7054 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7055 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7056#endif
glennrp47b9dd52010-11-24 18:12:06 +00007057
cristy3ed852e2009-09-05 21:47:34 +00007058 entry->magick=(IsImageFormatHandler *) IsPNG;
7059 entry->adjoin=MagickFalse;
7060 entry->description=ConstantString(
7061 "8-bit indexed with optional binary transparency");
7062 entry->module=ConstantString("PNG");
7063 (void) RegisterMagickInfo(entry);
7064
7065 entry=SetMagickInfo("PNG24");
7066 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007067
cristy3ed852e2009-09-05 21:47:34 +00007068#if defined(ZLIB_VERSION)
7069 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7070 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007071
cristy3ed852e2009-09-05 21:47:34 +00007072 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7073 {
7074 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7075 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7076 }
7077#endif
glennrp47b9dd52010-11-24 18:12:06 +00007078
cristy3ed852e2009-09-05 21:47:34 +00007079 if (*version != '\0')
7080 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007081
cristy3ed852e2009-09-05 21:47:34 +00007082#if defined(MAGICKCORE_PNG_DELEGATE)
7083 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7084 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7085#endif
glennrp47b9dd52010-11-24 18:12:06 +00007086
cristy3ed852e2009-09-05 21:47:34 +00007087 entry->magick=(IsImageFormatHandler *) IsPNG;
7088 entry->adjoin=MagickFalse;
7089 entry->description=ConstantString("opaque 24-bit RGB");
7090 entry->module=ConstantString("PNG");
7091 (void) RegisterMagickInfo(entry);
7092
7093 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007094
cristy3ed852e2009-09-05 21:47:34 +00007095#if defined(MAGICKCORE_PNG_DELEGATE)
7096 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7097 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7098#endif
glennrp47b9dd52010-11-24 18:12:06 +00007099
cristy3ed852e2009-09-05 21:47:34 +00007100 entry->magick=(IsImageFormatHandler *) IsPNG;
7101 entry->adjoin=MagickFalse;
7102 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7103 entry->module=ConstantString("PNG");
7104 (void) RegisterMagickInfo(entry);
7105
7106 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007107
cristy3ed852e2009-09-05 21:47:34 +00007108#if defined(JNG_SUPPORTED)
7109#if defined(MAGICKCORE_PNG_DELEGATE)
7110 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7111 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7112#endif
7113#endif
glennrp47b9dd52010-11-24 18:12:06 +00007114
cristy3ed852e2009-09-05 21:47:34 +00007115 entry->magick=(IsImageFormatHandler *) IsJNG;
7116 entry->adjoin=MagickFalse;
7117 entry->description=ConstantString("JPEG Network Graphics");
7118 entry->module=ConstantString("PNG");
7119 entry->note=ConstantString(JNGNote);
7120 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007121
cristy18b17442009-10-25 18:36:48 +00007122#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007123 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007124#endif
glennrp47b9dd52010-11-24 18:12:06 +00007125
cristy3ed852e2009-09-05 21:47:34 +00007126 return(MagickImageCoderSignature);
7127}
7128
7129/*
7130%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7131% %
7132% %
7133% %
7134% U n r e g i s t e r P N G I m a g e %
7135% %
7136% %
7137% %
7138%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7139%
7140% UnregisterPNGImage() removes format registrations made by the
7141% PNG module from the list of supported formats.
7142%
7143% The format of the UnregisterPNGImage method is:
7144%
7145% UnregisterPNGImage(void)
7146%
7147*/
7148ModuleExport void UnregisterPNGImage(void)
7149{
7150 (void) UnregisterMagickInfo("MNG");
7151 (void) UnregisterMagickInfo("PNG");
7152 (void) UnregisterMagickInfo("PNG8");
7153 (void) UnregisterMagickInfo("PNG24");
7154 (void) UnregisterMagickInfo("PNG32");
7155 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007156
cristy3ed852e2009-09-05 21:47:34 +00007157#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007158 if (ping_semaphore != (SemaphoreInfo *) NULL)
7159 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007160#endif
7161}
7162
7163#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007164#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007165/*
7166%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7167% %
7168% %
7169% %
7170% W r i t e M N G I m a g e %
7171% %
7172% %
7173% %
7174%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7175%
7176% WriteMNGImage() writes an image in the Portable Network Graphics
7177% Group's "Multiple-image Network Graphics" encoded image format.
7178%
7179% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7180%
7181% The format of the WriteMNGImage method is:
7182%
7183% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
7184%
7185% A description of each parameter follows.
7186%
7187% o image_info: the image info.
7188%
7189% o image: The image.
7190%
7191%
7192% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7193% "To do" under ReadPNGImage):
7194%
cristy3ed852e2009-09-05 21:47:34 +00007195% Preserve all unknown and not-yet-handled known chunks found in input
7196% PNG file and copy them into output PNG files according to the PNG
7197% copying rules.
7198%
7199% Write the iCCP chunk at MNG level when (icc profile length > 0)
7200%
7201% Improve selection of color type (use indexed-colour or indexed-colour
7202% with tRNS when 256 or fewer unique RGBA values are present).
7203%
7204% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7205% This will be complicated if we limit ourselves to generating MNG-LC
7206% files. For now we ignore disposal method 3 and simply overlay the next
7207% image on it.
7208%
7209% Check for identical PLTE's or PLTE/tRNS combinations and use a
7210% global MNG PLTE or PLTE/tRNS combination when appropriate.
7211% [mostly done 15 June 1999 but still need to take care of tRNS]
7212%
7213% Check for identical sRGB and replace with a global sRGB (and remove
7214% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7215% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7216% local gAMA/cHRM with local sRGB if appropriate).
7217%
7218% Check for identical sBIT chunks and write global ones.
7219%
7220% Provide option to skip writing the signature tEXt chunks.
7221%
7222% Use signatures to detect identical objects and reuse the first
7223% instance of such objects instead of writing duplicate objects.
7224%
7225% Use a smaller-than-32k value of compression window size when
7226% appropriate.
7227%
7228% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7229% ancillary text chunks and save profiles.
7230%
7231% Provide an option to force LC files (to ensure exact framing rate)
7232% instead of VLC.
7233%
7234% Provide an option to force VLC files instead of LC, even when offsets
7235% are present. This will involve expanding the embedded images with a
7236% transparent region at the top and/or left.
7237*/
7238
cristy3ed852e2009-09-05 21:47:34 +00007239static void
glennrpcf002022011-01-30 02:38:15 +00007240Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007241 png_info *ping_info, unsigned char *profile_type, unsigned char
7242 *profile_description, unsigned char *profile_data, png_uint_32 length)
7243{
cristy3ed852e2009-09-05 21:47:34 +00007244 png_textp
7245 text;
7246
cristybb503372010-05-27 20:51:26 +00007247 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007248 i;
7249
7250 unsigned char
7251 *sp;
7252
7253 png_charp
7254 dp;
7255
7256 png_uint_32
7257 allocated_length,
7258 description_length;
7259
7260 unsigned char
7261 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007262
cristy3ed852e2009-09-05 21:47:34 +00007263 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7264 return;
7265
7266 if (image_info->verbose)
7267 {
glennrp0fe50b42010-11-16 03:52:51 +00007268 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7269 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007270 }
glennrp0fe50b42010-11-16 03:52:51 +00007271
cristy3ed852e2009-09-05 21:47:34 +00007272 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7273 description_length=(png_uint_32) strlen((const char *) profile_description);
7274 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7275 + description_length);
7276 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7277 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7278 text[0].key[0]='\0';
7279 (void) ConcatenateMagickString(text[0].key,
7280 "Raw profile type ",MaxTextExtent);
7281 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7282 sp=profile_data;
7283 dp=text[0].text;
7284 *dp++='\n';
7285 (void) CopyMagickString(dp,(const char *) profile_description,
7286 allocated_length);
7287 dp+=description_length;
7288 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007289 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007290 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007291 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007292
cristybb503372010-05-27 20:51:26 +00007293 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007294 {
7295 if (i%36 == 0)
7296 *dp++='\n';
7297 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7298 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7299 }
glennrp47b9dd52010-11-24 18:12:06 +00007300
cristy3ed852e2009-09-05 21:47:34 +00007301 *dp++='\n';
7302 *dp='\0';
7303 text[0].text_length=(png_size_t) (dp-text[0].text);
7304 text[0].compression=image_info->compression == NoCompression ||
7305 (image_info->compression == UndefinedCompression &&
7306 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007307
cristy3ed852e2009-09-05 21:47:34 +00007308 if (text[0].text_length <= allocated_length)
7309 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007310
cristy3ed852e2009-09-05 21:47:34 +00007311 png_free(ping,text[0].text);
7312 png_free(ping,text[0].key);
7313 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007314}
7315
glennrpcf002022011-01-30 02:38:15 +00007316static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007317 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007318{
7319 char
7320 *name;
7321
7322 const StringInfo
7323 *profile;
7324
7325 unsigned char
7326 *data;
7327
7328 png_uint_32 length;
7329
7330 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007331
7332 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7333 {
cristy3ed852e2009-09-05 21:47:34 +00007334 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007335
cristy3ed852e2009-09-05 21:47:34 +00007336 if (profile != (const StringInfo *) NULL)
7337 {
7338 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007339 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007340
glennrp47b9dd52010-11-24 18:12:06 +00007341 if (LocaleNCompare(name,string,11) == 0)
7342 {
7343 if (logging != MagickFalse)
7344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7345 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007346
glennrpcf002022011-01-30 02:38:15 +00007347 ping_profile=CloneStringInfo(profile);
7348 data=GetStringInfoDatum(ping_profile),
7349 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007350 data[4]=data[3];
7351 data[3]=data[2];
7352 data[2]=data[1];
7353 data[1]=data[0];
7354 (void) WriteBlobMSBULong(image,length-5); /* data length */
7355 (void) WriteBlob(image,length-1,data+1);
7356 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007357 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007358 }
cristy3ed852e2009-09-05 21:47:34 +00007359 }
glennrp47b9dd52010-11-24 18:12:06 +00007360
cristy3ed852e2009-09-05 21:47:34 +00007361 name=GetNextImageProfile(image);
7362 }
glennrp47b9dd52010-11-24 18:12:06 +00007363
cristy3ed852e2009-09-05 21:47:34 +00007364 return(MagickTrue);
7365}
7366
glennrpb9cfe272010-12-21 15:08:06 +00007367
cristy3ed852e2009-09-05 21:47:34 +00007368/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007369static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7370 const ImageInfo *IMimage_info,Image *IMimage)
7371{
7372 Image
7373 *image;
7374
7375 ImageInfo
7376 *image_info;
7377
cristy3ed852e2009-09-05 21:47:34 +00007378 char
7379 s[2];
7380
7381 const char
7382 *name,
7383 *property,
7384 *value;
7385
7386 const StringInfo
7387 *profile;
7388
cristy3ed852e2009-09-05 21:47:34 +00007389 int
cristy3ed852e2009-09-05 21:47:34 +00007390 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007391 pass;
7392
glennrpe9c26dc2010-05-30 01:56:35 +00007393 png_byte
7394 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007395
glennrp39992b42010-11-14 00:03:43 +00007396 png_color
7397 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007398
glennrp5af765f2010-03-30 11:12:18 +00007399 png_color_16
7400 ping_background,
7401 ping_trans_color;
7402
cristy3ed852e2009-09-05 21:47:34 +00007403 png_info
7404 *ping_info;
7405
7406 png_struct
7407 *ping;
7408
glennrp5af765f2010-03-30 11:12:18 +00007409 png_uint_32
7410 ping_height,
7411 ping_width;
7412
cristybb503372010-05-27 20:51:26 +00007413 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007414 y;
7415
7416 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007417 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007418 logging,
glennrp58e01762011-01-07 15:28:54 +00007419 matte,
7420
glennrpda8f3a72011-02-27 23:54:12 +00007421 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007422 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007423 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007424 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007425 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007426 ping_have_bKGD,
7427 ping_have_pHYs,
7428 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007429
7430 ping_exclude_bKGD,
7431 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007432 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007433 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007434 ping_exclude_gAMA,
7435 ping_exclude_iCCP,
7436 /* ping_exclude_iTXt, */
7437 ping_exclude_oFFs,
7438 ping_exclude_pHYs,
7439 ping_exclude_sRGB,
7440 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007441 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007442 ping_exclude_vpAg,
7443 ping_exclude_zCCP, /* hex-encoded iCCP */
7444 ping_exclude_zTXt,
7445
glennrp8d3d6e52011-04-19 04:39:51 +00007446 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007447 ping_need_colortype_warning,
7448
glennrp82b3c532011-03-22 19:20:54 +00007449 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007450 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007451 tried_333,
7452 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007453
7454 QuantumInfo
7455 *quantum_info;
7456
cristybb503372010-05-27 20:51:26 +00007457 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007458 i,
7459 x;
7460
7461 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007462 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007463
glennrp5af765f2010-03-30 11:12:18 +00007464 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007465 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007466 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007467 ping_color_type,
7468 ping_interlace_method,
7469 ping_compression_method,
7470 ping_filter_method,
7471 ping_num_trans;
7472
cristybb503372010-05-27 20:51:26 +00007473 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007474 image_depth,
7475 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007476
cristybb503372010-05-27 20:51:26 +00007477 size_t
cristy3ed852e2009-09-05 21:47:34 +00007478 quality,
7479 rowbytes,
7480 save_image_depth;
7481
glennrpdfd70802010-11-14 01:23:35 +00007482 int
glennrpfd05d622011-02-25 04:10:33 +00007483 j,
glennrpf09bded2011-01-08 01:15:59 +00007484 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007485 number_opaque,
7486 number_semitransparent,
7487 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007488 ping_pHYs_unit_type;
7489
7490 png_uint_32
7491 ping_pHYs_x_resolution,
7492 ping_pHYs_y_resolution;
7493
cristy3ed852e2009-09-05 21:47:34 +00007494 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007495 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007496
glennrpb9cfe272010-12-21 15:08:06 +00007497 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7498 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007499 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007500 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007501
cristy3ed852e2009-09-05 21:47:34 +00007502#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007503 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007504#endif
7505
glennrp5af765f2010-03-30 11:12:18 +00007506 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007507 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007508 ping_color_type=0,
7509 ping_interlace_method=0,
7510 ping_compression_method=0,
7511 ping_filter_method=0,
7512 ping_num_trans = 0;
7513
7514 ping_background.red = 0;
7515 ping_background.green = 0;
7516 ping_background.blue = 0;
7517 ping_background.gray = 0;
7518 ping_background.index = 0;
7519
7520 ping_trans_color.red=0;
7521 ping_trans_color.green=0;
7522 ping_trans_color.blue=0;
7523 ping_trans_color.gray=0;
7524
glennrpdfd70802010-11-14 01:23:35 +00007525 ping_pHYs_unit_type = 0;
7526 ping_pHYs_x_resolution = 0;
7527 ping_pHYs_y_resolution = 0;
7528
glennrpda8f3a72011-02-27 23:54:12 +00007529 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007530 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007531 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007532 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007533 ping_have_bKGD=MagickFalse;
7534 ping_have_pHYs=MagickFalse;
7535 ping_have_tRNS=MagickFalse;
7536
glennrp0e8ea192010-12-24 18:00:33 +00007537 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7538 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007539 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007540 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007541 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007542 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7543 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7544 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7545 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7546 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7547 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007548 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007549 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7550 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7551 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7552
glennrp8d3d6e52011-04-19 04:39:51 +00007553 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007554 ping_need_colortype_warning = MagickFalse;
7555
glennrp8bb3a022010-12-13 20:40:04 +00007556 number_opaque = 0;
7557 number_semitransparent = 0;
7558 number_transparent = 0;
7559
glennrpfd05d622011-02-25 04:10:33 +00007560 if (logging != MagickFalse)
7561 {
7562 if (image->storage_class == UndefinedClass)
7563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7564 " storage_class=UndefinedClass");
7565 if (image->storage_class == DirectClass)
7566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7567 " storage_class=DirectClass");
7568 if (image->storage_class == PseudoClass)
7569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7570 " storage_class=PseudoClass");
7571 }
glennrp28af3712011-04-06 18:07:30 +00007572
glennrpc6c391a2011-04-27 02:23:56 +00007573 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007574 {
glennrpc6c391a2011-04-27 02:23:56 +00007575 if (image->storage_class != PseudoClass && image->colormap != NULL)
7576 {
7577 /* Free the bogus colormap; it can cause trouble later */
7578 if (logging != MagickFalse)
7579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7580 " Freeing bogus colormap");
7581 (void *) RelinquishMagickMemory(image->colormap);
7582 image->colormap=NULL;
7583 }
glennrp28af3712011-04-06 18:07:30 +00007584 }
glennrpbb4f99d2011-05-22 11:13:17 +00007585
cristy3ed852e2009-09-05 21:47:34 +00007586 if (image->colorspace != RGBColorspace)
7587 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007588
glennrp3241bd02010-12-12 04:36:28 +00007589 /*
7590 Sometimes we get PseudoClass images whose RGB values don't match
7591 the colors in the colormap. This code syncs the RGB values.
7592 */
7593 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7594 (void) SyncImage(image);
7595
glennrpa6a06632011-01-19 15:15:34 +00007596#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7597 if (image->depth > 8)
7598 {
7599 if (logging != MagickFalse)
7600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7601 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7602
7603 image->depth=8;
7604 }
7605#endif
7606
glennrp8e58efd2011-05-20 12:16:29 +00007607 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007608 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7609 {
glennrp8e58efd2011-05-20 12:16:29 +00007610 register PixelPacket
7611 *r;
7612
7613 ExceptionInfo
7614 *exception;
7615
7616 exception=(&image->exception);
7617
7618 if (image->depth > 8)
7619 {
7620#if MAGICKCORE_QUANTUM_DEPTH > 16
7621 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007622 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007623
7624 for (y=0; y < (ssize_t) image->rows; y++)
7625 {
7626 r=GetAuthenticPixels(image,0,y,image->columns,1,
7627 exception);
7628
7629 if (r == (PixelPacket *) NULL)
7630 break;
7631
7632 for (x=0; x < (ssize_t) image->columns; x++)
7633 {
cristyef618312011-06-25 12:26:44 +00007634 LBR16PixelRGBO(r);
glennrp8e58efd2011-05-20 12:16:29 +00007635 r++;
7636 }
glennrpbb4f99d2011-05-22 11:13:17 +00007637
glennrp8e58efd2011-05-20 12:16:29 +00007638 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7639 break;
7640 }
7641
7642 if (image->storage_class == PseudoClass && image->colormap != NULL)
7643 {
cristy3e08f112011-05-24 13:19:30 +00007644 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007645 {
glennrp91d99252011-06-25 14:30:13 +00007646 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007647 }
7648 }
7649#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7650 }
7651
7652 else if (image->depth > 4)
7653 {
7654#if MAGICKCORE_QUANTUM_DEPTH > 8
7655 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007656 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007657
7658 for (y=0; y < (ssize_t) image->rows; y++)
7659 {
7660 r=GetAuthenticPixels(image,0,y,image->columns,1,
7661 exception);
7662
7663 if (r == (PixelPacket *) NULL)
7664 break;
7665
7666 for (x=0; x < (ssize_t) image->columns; x++)
7667 {
cristyef618312011-06-25 12:26:44 +00007668 LBR08PixelRGBO(r);
glennrp8e58efd2011-05-20 12:16:29 +00007669 r++;
7670 }
glennrpbb4f99d2011-05-22 11:13:17 +00007671
glennrp8e58efd2011-05-20 12:16:29 +00007672 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7673 break;
7674 }
7675
7676 if (image->storage_class == PseudoClass && image->colormap != NULL)
7677 {
cristy3e08f112011-05-24 13:19:30 +00007678 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007679 {
glennrp91d99252011-06-25 14:30:13 +00007680 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007681 }
7682 }
7683#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7684 }
7685 else
7686 if (image->depth > 2)
7687 {
7688 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007689 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007690
7691 for (y=0; y < (ssize_t) image->rows; y++)
7692 {
7693 r=GetAuthenticPixels(image,0,y,image->columns,1,
7694 exception);
7695
7696 if (r == (PixelPacket *) NULL)
7697 break;
7698
7699 for (x=0; x < (ssize_t) image->columns; x++)
7700 {
cristyef618312011-06-25 12:26:44 +00007701 LBR04PixelRGBO(r);
glennrp8e58efd2011-05-20 12:16:29 +00007702 r++;
7703 }
glennrpbb4f99d2011-05-22 11:13:17 +00007704
glennrp8e58efd2011-05-20 12:16:29 +00007705 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7706 break;
7707 }
7708
7709 if (image->storage_class == PseudoClass && image->colormap != NULL)
7710 {
cristy3e08f112011-05-24 13:19:30 +00007711 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007712 {
glennrp91d99252011-06-25 14:30:13 +00007713 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007714 }
7715 }
7716 }
7717
7718 else if (image->depth > 1)
7719 {
7720 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007721 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007722
7723 for (y=0; y < (ssize_t) image->rows; y++)
7724 {
7725 r=GetAuthenticPixels(image,0,y,image->columns,1,
7726 exception);
7727
7728 if (r == (PixelPacket *) NULL)
7729 break;
7730
7731 for (x=0; x < (ssize_t) image->columns; x++)
7732 {
cristyef618312011-06-25 12:26:44 +00007733 LBR02PixelRGBO(r);
glennrp8e58efd2011-05-20 12:16:29 +00007734 r++;
7735 }
glennrpbb4f99d2011-05-22 11:13:17 +00007736
glennrp8e58efd2011-05-20 12:16:29 +00007737 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7738 break;
7739 }
7740
7741 if (image->storage_class == PseudoClass && image->colormap != NULL)
7742 {
cristy3e08f112011-05-24 13:19:30 +00007743 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007744 {
glennrp91d99252011-06-25 14:30:13 +00007745 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007746 }
7747 }
7748 }
7749 else
7750 {
7751 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007752 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007753
7754 for (y=0; y < (ssize_t) image->rows; y++)
7755 {
7756 r=GetAuthenticPixels(image,0,y,image->columns,1,
7757 exception);
7758
7759 if (r == (PixelPacket *) NULL)
7760 break;
7761
7762 for (x=0; x < (ssize_t) image->columns; x++)
7763 {
cristyef618312011-06-25 12:26:44 +00007764 LBR01PixelRGBO(r);
glennrp8e58efd2011-05-20 12:16:29 +00007765 r++;
7766 }
glennrpbb4f99d2011-05-22 11:13:17 +00007767
glennrp8e58efd2011-05-20 12:16:29 +00007768 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7769 break;
7770 }
7771
7772 if (image->storage_class == PseudoClass && image->colormap != NULL)
7773 {
cristy3e08f112011-05-24 13:19:30 +00007774 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007775 {
glennrp91d99252011-06-25 14:30:13 +00007776 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007777 }
7778 }
7779 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007780 }
7781
glennrp67b9c1a2011-04-22 18:47:36 +00007782 /* To do: set to next higher multiple of 8 */
7783 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007784 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007785
glennrp2b013e42010-11-24 16:55:50 +00007786#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7787 /* PNG does not handle depths greater than 16 so reduce it even
7788 * if lossy
7789 */
glennrp8e58efd2011-05-20 12:16:29 +00007790 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007791 image->depth=16;
7792#endif
7793
glennrp3faa9a32011-04-23 14:00:25 +00007794#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007795 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007796 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007797 image->depth = 8;
7798#endif
7799
glennrpc8c2f062011-02-25 19:00:33 +00007800 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007801 * we reduce the transparency to binary and run again, then if there
7802 * 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 +00007803 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7804 * palette. Then (To do) we take care of a final reduction that is only
7805 * needed if there are still 256 colors present and one of them has both
7806 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007807 */
glennrp82b3c532011-03-22 19:20:54 +00007808
glennrp8ca51ad2011-05-12 21:22:32 +00007809 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007810 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007811 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007812
glennrp8ca51ad2011-05-12 21:22:32 +00007813 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007814 {
7815 /* BUILD_PALETTE
7816 *
7817 * Sometimes we get DirectClass images that have 256 colors or fewer.
7818 * This code will build a colormap.
7819 *
7820 * Also, sometimes we get PseudoClass images with an out-of-date
7821 * colormap. This code will replace the colormap with a new one.
7822 * Sometimes we get PseudoClass images that have more than 256 colors.
7823 * This code will delete the colormap and change the image to
7824 * DirectClass.
7825 *
7826 * If image->matte is MagickFalse, we ignore the opacity channel
7827 * even though it sometimes contains left-over non-opaque values.
7828 *
7829 * Also we gather some information (number of opaque, transparent,
7830 * and semitransparent pixels, and whether the image has any non-gray
7831 * pixels or only black-and-white pixels) that we might need later.
7832 *
7833 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7834 * we need to check for bogus non-opaque values, at least.
7835 */
glennrp3c218112010-11-27 15:31:26 +00007836
glennrpd71e86a2011-02-24 01:28:37 +00007837 ExceptionInfo
7838 *exception;
glennrp3c218112010-11-27 15:31:26 +00007839
glennrpd71e86a2011-02-24 01:28:37 +00007840 int
7841 n;
glennrp3c218112010-11-27 15:31:26 +00007842
glennrpd71e86a2011-02-24 01:28:37 +00007843 PixelPacket
7844 opaque[260],
7845 semitransparent[260],
7846 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007847
glennrpd71e86a2011-02-24 01:28:37 +00007848 register IndexPacket
7849 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007850
glennrpd71e86a2011-02-24 01:28:37 +00007851 register const PixelPacket
7852 *s,
7853 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007854
glennrpfd05d622011-02-25 04:10:33 +00007855 register PixelPacket
7856 *r;
7857
glennrpd71e86a2011-02-24 01:28:37 +00007858 if (logging != MagickFalse)
7859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7860 " Enter BUILD_PALETTE:");
7861
7862 if (logging != MagickFalse)
7863 {
glennrp03812ae2010-12-24 01:31:34 +00007864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007865 " image->columns=%.20g",(double) image->columns);
7866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7867 " image->rows=%.20g",(double) image->rows);
7868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7869 " image->matte=%.20g",(double) image->matte);
7870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7871 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007872
glennrpfd05d622011-02-25 04:10:33 +00007873 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007874 {
7875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007876 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007878 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007879
glennrpd71e86a2011-02-24 01:28:37 +00007880 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007881 {
glennrpd71e86a2011-02-24 01:28:37 +00007882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7883 " %d (%d,%d,%d,%d)",
7884 (int) i,
7885 (int) image->colormap[i].red,
7886 (int) image->colormap[i].green,
7887 (int) image->colormap[i].blue,
7888 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007889 }
glennrp2cc891a2010-12-24 13:44:32 +00007890
glennrpd71e86a2011-02-24 01:28:37 +00007891 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7892 {
7893 if (i > 255)
7894 {
7895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7896 " %d (%d,%d,%d,%d)",
7897 (int) i,
7898 (int) image->colormap[i].red,
7899 (int) image->colormap[i].green,
7900 (int) image->colormap[i].blue,
7901 (int) image->colormap[i].opacity);
7902 }
7903 }
glennrp03812ae2010-12-24 01:31:34 +00007904 }
glennrp7ddcc222010-12-11 05:01:05 +00007905
glennrpd71e86a2011-02-24 01:28:37 +00007906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7907 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007908
glennrpd71e86a2011-02-24 01:28:37 +00007909 if (image->colors == 0)
7910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7911 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007912
glennrp8d3d6e52011-04-19 04:39:51 +00007913 if (ping_preserve_colormap == MagickFalse)
7914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7915 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007916 }
7917
7918 exception=(&image->exception);
7919
7920 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007921 number_opaque = 0;
7922 number_semitransparent = 0;
7923 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007924
7925 for (y=0; y < (ssize_t) image->rows; y++)
7926 {
7927 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7928
7929 if (q == (PixelPacket *) NULL)
7930 break;
7931
7932 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007933 {
glennrp4737d522011-04-29 03:33:42 +00007934 if (image->matte == MagickFalse ||
cristyef618312011-06-25 12:26:44 +00007935 GetPixelOpacity(q) == OpaqueOpacity)
glennrpd71e86a2011-02-24 01:28:37 +00007936 {
7937 if (number_opaque < 259)
7938 {
7939 if (number_opaque == 0)
7940 {
cristyef618312011-06-25 12:26:44 +00007941 GetPixelRGB(q, opaque);
glennrpd71e86a2011-02-24 01:28:37 +00007942 opaque[0].opacity=OpaqueOpacity;
7943 number_opaque=1;
7944 }
glennrp2cc891a2010-12-24 13:44:32 +00007945
glennrpd71e86a2011-02-24 01:28:37 +00007946 for (i=0; i< (ssize_t) number_opaque; i++)
7947 {
glennrp0e68fac2011-04-26 04:51:47 +00007948 if (IsColorEqual(q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00007949 break;
7950 }
glennrp7ddcc222010-12-11 05:01:05 +00007951
glennrpd71e86a2011-02-24 01:28:37 +00007952 if (i == (ssize_t) number_opaque &&
7953 number_opaque < 259)
7954 {
7955 number_opaque++;
cristyef618312011-06-25 12:26:44 +00007956 GetPixelRGB(q, opaque+i);
glennrpca7ad3a2011-04-26 04:44:54 +00007957 opaque[i].opacity=OpaqueOpacity;
glennrpd71e86a2011-02-24 01:28:37 +00007958 }
7959 }
7960 }
7961 else if (q->opacity == TransparentOpacity)
7962 {
7963 if (number_transparent < 259)
7964 {
7965 if (number_transparent == 0)
7966 {
cristyef618312011-06-25 12:26:44 +00007967 GetPixelRGBO(q, transparent);
glennrpa18d5bc2011-04-23 14:51:34 +00007968 ping_trans_color.red=
cristyef618312011-06-25 12:26:44 +00007969 (unsigned short) GetPixelRed(q);
glennrpa18d5bc2011-04-23 14:51:34 +00007970 ping_trans_color.green=
cristyef618312011-06-25 12:26:44 +00007971 (unsigned short) GetPixelGreen(q);
glennrpa18d5bc2011-04-23 14:51:34 +00007972 ping_trans_color.blue=
cristyef618312011-06-25 12:26:44 +00007973 (unsigned short) GetPixelBlue(q);
glennrpa18d5bc2011-04-23 14:51:34 +00007974 ping_trans_color.gray=
cristyef618312011-06-25 12:26:44 +00007975 (unsigned short) GetPixelRed(q);
glennrpd71e86a2011-02-24 01:28:37 +00007976 number_transparent = 1;
7977 }
7978
7979 for (i=0; i< (ssize_t) number_transparent; i++)
7980 {
glennrp0e68fac2011-04-26 04:51:47 +00007981 if (IsColorEqual(q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00007982 break;
7983 }
7984
7985 if (i == (ssize_t) number_transparent &&
7986 number_transparent < 259)
7987 {
7988 number_transparent++;
cristyef618312011-06-25 12:26:44 +00007989 GetPixelRGBO(q, transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00007990 }
7991 }
7992 }
7993 else
7994 {
7995 if (number_semitransparent < 259)
7996 {
7997 if (number_semitransparent == 0)
7998 {
cristyef618312011-06-25 12:26:44 +00007999 GetPixelRGBO(q, semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008000 number_semitransparent = 1;
8001 }
8002
8003 for (i=0; i< (ssize_t) number_semitransparent; i++)
8004 {
glennrp0e68fac2011-04-26 04:51:47 +00008005 if (IsColorEqual(q, semitransparent+i)
cristyef618312011-06-25 12:26:44 +00008006 && GetPixelOpacity(q) ==
glennrpca7ad3a2011-04-26 04:44:54 +00008007 semitransparent[i].opacity)
glennrpd71e86a2011-02-24 01:28:37 +00008008 break;
8009 }
8010
8011 if (i == (ssize_t) number_semitransparent &&
8012 number_semitransparent < 259)
8013 {
8014 number_semitransparent++;
cristyef618312011-06-25 12:26:44 +00008015 GetPixelRGBO(q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008016 }
8017 }
8018 }
8019 q++;
8020 }
8021 }
8022
8023 if (ping_exclude_bKGD == MagickFalse)
8024 {
8025 /* Add the background color to the palette, if it
8026 * isn't already there.
8027 */
glennrpc6c391a2011-04-27 02:23:56 +00008028 if (logging != MagickFalse)
8029 {
8030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8031 " Check colormap for background (%d,%d,%d)",
8032 (int) image->background_color.red,
8033 (int) image->background_color.green,
8034 (int) image->background_color.blue);
8035 }
glennrpd71e86a2011-02-24 01:28:37 +00008036 for (i=0; i<number_opaque; i++)
8037 {
glennrpca7ad3a2011-04-26 04:44:54 +00008038 if (opaque[i].red == image->background_color.red &&
8039 opaque[i].green == image->background_color.green &&
8040 opaque[i].blue == image->background_color.blue)
8041 break;
glennrpd71e86a2011-02-24 01:28:37 +00008042 }
glennrpd71e86a2011-02-24 01:28:37 +00008043 if (number_opaque < 259 && i == number_opaque)
8044 {
glennrp8e045c82011-04-27 16:40:27 +00008045 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008046 ping_background.index = i;
8047 if (logging != MagickFalse)
8048 {
8049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8050 " background_color index is %d",(int) i);
8051 }
8052
glennrpd71e86a2011-02-24 01:28:37 +00008053 }
glennrpa080bc32011-03-11 18:03:44 +00008054 else if (logging != MagickFalse)
8055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8056 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008057 }
8058
8059 image_colors=number_opaque+number_transparent+number_semitransparent;
8060
glennrpa080bc32011-03-11 18:03:44 +00008061 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8062 {
8063 /* No room for the background color; remove it. */
8064 number_opaque--;
8065 image_colors--;
8066 }
8067
glennrpd71e86a2011-02-24 01:28:37 +00008068 if (logging != MagickFalse)
8069 {
8070 if (image_colors > 256)
8071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8072 " image has more than 256 colors");
8073
8074 else
8075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8076 " image has %d colors",image_colors);
8077 }
8078
glennrp8d3d6e52011-04-19 04:39:51 +00008079 if (ping_preserve_colormap != MagickFalse)
8080 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008081
glennrpfd05d622011-02-25 04:10:33 +00008082 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008083 {
8084 ping_have_color=MagickFalse;
8085 ping_have_non_bw=MagickFalse;
8086
8087 if(image_colors > 256)
8088 {
8089 for (y=0; y < (ssize_t) image->rows; y++)
8090 {
8091 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8092
8093 if (q == (PixelPacket *) NULL)
8094 break;
8095
8096 /* Worst case is black-and-white; we are looking at every
8097 * pixel twice.
8098 */
8099
8100 if (ping_have_color == MagickFalse)
8101 {
8102 s=q;
8103 for (x=0; x < (ssize_t) image->columns; x++)
8104 {
cristyef618312011-06-25 12:26:44 +00008105 if (GetPixelRed(s) != GetPixelGreen(s)
8106 || GetPixelRed(s) != GetPixelBlue(s))
glennrpd71e86a2011-02-24 01:28:37 +00008107 {
8108 ping_have_color=MagickTrue;
8109 ping_have_non_bw=MagickTrue;
8110 break;
8111 }
8112 s++;
8113 }
8114 }
8115
8116 if (ping_have_non_bw == MagickFalse)
8117 {
8118 s=q;
8119 for (x=0; x < (ssize_t) image->columns; x++)
8120 {
cristyef618312011-06-25 12:26:44 +00008121 if (GetPixelRed(s) != 0 &&
8122 GetPixelRed(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008123 {
8124 ping_have_non_bw=MagickTrue;
8125 }
8126 s++;
8127 }
8128 }
8129 }
glennrpbb4f99d2011-05-22 11:13:17 +00008130 }
8131 }
glennrpd71e86a2011-02-24 01:28:37 +00008132
8133 if (image_colors < 257)
8134 {
8135 PixelPacket
8136 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008137
glennrpd71e86a2011-02-24 01:28:37 +00008138 /*
8139 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008140 */
8141
glennrpd71e86a2011-02-24 01:28:37 +00008142 if (logging != MagickFalse)
8143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8144 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008145
glennrpd71e86a2011-02-24 01:28:37 +00008146 /* Sort palette, transparent first */;
8147
8148 n = 0;
8149
8150 for (i=0; i<number_transparent; i++)
8151 colormap[n++] = transparent[i];
8152
8153 for (i=0; i<number_semitransparent; i++)
8154 colormap[n++] = semitransparent[i];
8155
8156 for (i=0; i<number_opaque; i++)
8157 colormap[n++] = opaque[i];
8158
glennrpc6c391a2011-04-27 02:23:56 +00008159 ping_background.index +=
8160 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008161
glennrpd71e86a2011-02-24 01:28:37 +00008162 /* image_colors < 257; search the colormap instead of the pixels
8163 * to get ping_have_color and ping_have_non_bw
8164 */
8165 for (i=0; i<n; i++)
8166 {
8167 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008168 {
glennrpd71e86a2011-02-24 01:28:37 +00008169 if (colormap[i].red != colormap[i].green ||
8170 colormap[i].red != colormap[i].blue)
8171 {
8172 ping_have_color=MagickTrue;
8173 ping_have_non_bw=MagickTrue;
8174 break;
8175 }
8176 }
8177
8178 if (ping_have_non_bw == MagickFalse)
8179 {
8180 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008181 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008182 }
glennrp8bb3a022010-12-13 20:40:04 +00008183 }
8184
glennrpd71e86a2011-02-24 01:28:37 +00008185 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8186 (number_transparent == 0 && number_semitransparent == 0)) &&
8187 (((mng_info->write_png_colortype-1) ==
8188 PNG_COLOR_TYPE_PALETTE) ||
8189 (mng_info->write_png_colortype == 0)))
8190 {
glennrp6185c532011-01-14 17:58:40 +00008191 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008192 {
glennrpd71e86a2011-02-24 01:28:37 +00008193 if (n != (ssize_t) image_colors)
8194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8195 " image_colors (%d) and n (%d) don't match",
8196 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008197
glennrpd71e86a2011-02-24 01:28:37 +00008198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8199 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008200 }
glennrp03812ae2010-12-24 01:31:34 +00008201
glennrpd71e86a2011-02-24 01:28:37 +00008202 image->colors = image_colors;
8203
8204 if (AcquireImageColormap(image,image_colors) ==
8205 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008206 ThrowWriterException(ResourceLimitError,
8207 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008208
8209 for (i=0; i< (ssize_t) image_colors; i++)
8210 image->colormap[i] = colormap[i];
8211
8212 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008213 {
glennrpd71e86a2011-02-24 01:28:37 +00008214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8215 " image->colors=%d (%d)",
8216 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008217
glennrpd71e86a2011-02-24 01:28:37 +00008218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8219 " Update the pixel indexes");
8220 }
glennrp6185c532011-01-14 17:58:40 +00008221
glennrpfd05d622011-02-25 04:10:33 +00008222 /* Sync the pixel indices with the new colormap */
8223
glennrpd71e86a2011-02-24 01:28:37 +00008224 for (y=0; y < (ssize_t) image->rows; y++)
8225 {
8226 q=GetAuthenticPixels(image,0,y,image->columns,1,
8227 exception);
glennrp6185c532011-01-14 17:58:40 +00008228
glennrpd71e86a2011-02-24 01:28:37 +00008229 if (q == (PixelPacket *) NULL)
8230 break;
glennrp6185c532011-01-14 17:58:40 +00008231
glennrpd71e86a2011-02-24 01:28:37 +00008232 indexes=GetAuthenticIndexQueue(image);
glennrpbb4f99d2011-05-22 11:13:17 +00008233
glennrpd71e86a2011-02-24 01:28:37 +00008234 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008235 {
glennrpd71e86a2011-02-24 01:28:37 +00008236 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008237 {
glennrpd71e86a2011-02-24 01:28:37 +00008238 if ((image->matte == MagickFalse ||
glennrpbb4f99d2011-05-22 11:13:17 +00008239 image->colormap[i].opacity ==
cristyef618312011-06-25 12:26:44 +00008240 GetPixelOpacity(q)) &&
glennrpbb4f99d2011-05-22 11:13:17 +00008241 image->colormap[i].red ==
cristyef618312011-06-25 12:26:44 +00008242 GetPixelRed(q) &&
glennrpbb4f99d2011-05-22 11:13:17 +00008243 image->colormap[i].green ==
cristyef618312011-06-25 12:26:44 +00008244 GetPixelGreen(q) &&
glennrpbb4f99d2011-05-22 11:13:17 +00008245 image->colormap[i].blue ==
cristyef618312011-06-25 12:26:44 +00008246 GetPixelBlue(q))
glennrp6185c532011-01-14 17:58:40 +00008247 {
cristyef618312011-06-25 12:26:44 +00008248 SetPixelIndex(indexes+x,i);
glennrpd71e86a2011-02-24 01:28:37 +00008249 break;
glennrp6185c532011-01-14 17:58:40 +00008250 }
glennrp6185c532011-01-14 17:58:40 +00008251 }
glennrpd71e86a2011-02-24 01:28:37 +00008252 q++;
8253 }
glennrp6185c532011-01-14 17:58:40 +00008254
glennrpd71e86a2011-02-24 01:28:37 +00008255 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8256 break;
8257 }
8258 }
8259 }
8260
8261 if (logging != MagickFalse)
8262 {
8263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8264 " image->colors=%d", (int) image->colors);
8265
8266 if (image->colormap != NULL)
8267 {
8268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8269 " i (red,green,blue,opacity)");
8270
8271 for (i=0; i < (ssize_t) image->colors; i++)
8272 {
cristy72988482011-03-29 16:34:38 +00008273 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008274 {
8275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8276 " %d (%d,%d,%d,%d)",
8277 (int) i,
8278 (int) image->colormap[i].red,
8279 (int) image->colormap[i].green,
8280 (int) image->colormap[i].blue,
8281 (int) image->colormap[i].opacity);
8282 }
glennrp6185c532011-01-14 17:58:40 +00008283 }
8284 }
glennrp03812ae2010-12-24 01:31:34 +00008285
glennrpd71e86a2011-02-24 01:28:37 +00008286 if (number_transparent < 257)
8287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8288 " number_transparent = %d",
8289 number_transparent);
8290 else
glennrp03812ae2010-12-24 01:31:34 +00008291
glennrpd71e86a2011-02-24 01:28:37 +00008292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8293 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008294
glennrpd71e86a2011-02-24 01:28:37 +00008295 if (number_opaque < 257)
8296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8297 " number_opaque = %d",
8298 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008299
glennrpd71e86a2011-02-24 01:28:37 +00008300 else
8301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8302 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008303
glennrpd71e86a2011-02-24 01:28:37 +00008304 if (number_semitransparent < 257)
8305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8306 " number_semitransparent = %d",
8307 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008308
glennrpd71e86a2011-02-24 01:28:37 +00008309 else
8310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8311 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008312
glennrpd71e86a2011-02-24 01:28:37 +00008313 if (ping_have_non_bw == MagickFalse)
8314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8315 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008316
glennrpd71e86a2011-02-24 01:28:37 +00008317 else if (ping_have_color == MagickFalse)
8318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8319 " All pixels and the background are gray");
8320
8321 else
8322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8323 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008324
glennrp03812ae2010-12-24 01:31:34 +00008325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8326 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008327 }
glennrpfd05d622011-02-25 04:10:33 +00008328
glennrpc8c2f062011-02-25 19:00:33 +00008329 if (mng_info->write_png8 == MagickFalse)
8330 break;
glennrpfd05d622011-02-25 04:10:33 +00008331
glennrpc8c2f062011-02-25 19:00:33 +00008332 /* Make any reductions necessary for the PNG8 format */
8333 if (image_colors <= 256 &&
8334 image_colors != 0 && image->colormap != NULL &&
8335 number_semitransparent == 0 &&
8336 number_transparent <= 1)
8337 break;
8338
8339 /* PNG8 can't have semitransparent colors so we threshold the
8340 * opacity to 0 or OpaqueOpacity
8341 */
8342 if (number_semitransparent != 0)
8343 {
8344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8345 " Thresholding the alpha channel to binary");
8346
8347 for (y=0; y < (ssize_t) image->rows; y++)
8348 {
8349 r=GetAuthenticPixels(image,0,y,image->columns,1,
8350 exception);
8351
8352 if (r == (PixelPacket *) NULL)
8353 break;
8354
8355 for (x=0; x < (ssize_t) image->columns; x++)
8356 {
cristyef618312011-06-25 12:26:44 +00008357 if (GetPixelOpacity(r) > TransparentOpacity/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008358 {
cristyef618312011-06-25 12:26:44 +00008359 SetPixelOpacity(r,TransparentOpacity);
8360 SetPixelRGB(r,&image->background_color);
glennrp8ca51ad2011-05-12 21:22:32 +00008361 }
8362 else
cristyef618312011-06-25 12:26:44 +00008363 SetPixelOpacity(r,OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00008364 r++;
8365 }
glennrpbb4f99d2011-05-22 11:13:17 +00008366
glennrpc8c2f062011-02-25 19:00:33 +00008367 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8368 break;
8369
8370 if (image_colors != 0 && image_colors <= 256 &&
8371 image->colormap != NULL)
8372 for (i=0; i<image_colors; i++)
8373 image->colormap[i].opacity =
glennrp77110c32011-05-03 05:25:16 +00008374 (image->colormap[i].opacity > TransparentOpacity/2 ?
8375 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00008376 }
8377 continue;
8378 }
8379
8380 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008381 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8382 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8383 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008384 */
glennrpd3371642011-03-22 19:42:23 +00008385 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8386 {
8387 if (logging != MagickFalse)
8388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8389 " Quantizing the background color to 4-4-4");
8390
8391 tried_444 = MagickTrue;
8392
glennrp91d99252011-06-25 14:30:13 +00008393 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008394
8395 if (logging != MagickFalse)
8396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8397 " Quantizing the pixel colors to 4-4-4");
8398
8399 if (image->colormap == NULL)
8400 {
8401 for (y=0; y < (ssize_t) image->rows; y++)
8402 {
8403 r=GetAuthenticPixels(image,0,y,image->columns,1,
8404 exception);
8405
8406 if (r == (PixelPacket *) NULL)
8407 break;
8408
8409 for (x=0; x < (ssize_t) image->columns; x++)
8410 {
cristyef618312011-06-25 12:26:44 +00008411 if (GetPixelOpacity(r) == OpaqueOpacity)
8412 LBR04PixelRGB(r);
glennrpd3371642011-03-22 19:42:23 +00008413 r++;
8414 }
glennrpbb4f99d2011-05-22 11:13:17 +00008415
glennrpd3371642011-03-22 19:42:23 +00008416 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8417 break;
8418 }
8419 }
8420
8421 else /* Should not reach this; colormap already exists and
8422 must be <= 256 */
8423 {
8424 if (logging != MagickFalse)
8425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8426 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008427
glennrpd3371642011-03-22 19:42:23 +00008428 for (i=0; i<image_colors; i++)
8429 {
glennrp91d99252011-06-25 14:30:13 +00008430 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008431 }
8432 }
8433 continue;
8434 }
8435
glennrp82b3c532011-03-22 19:20:54 +00008436 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8437 {
8438 if (logging != MagickFalse)
8439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8440 " Quantizing the background color to 3-3-3");
8441
8442 tried_333 = MagickTrue;
8443
glennrp91d99252011-06-25 14:30:13 +00008444 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008445
8446 if (logging != MagickFalse)
8447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008448 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008449
8450 if (image->colormap == NULL)
8451 {
8452 for (y=0; y < (ssize_t) image->rows; y++)
8453 {
8454 r=GetAuthenticPixels(image,0,y,image->columns,1,
8455 exception);
8456
8457 if (r == (PixelPacket *) NULL)
8458 break;
8459
8460 for (x=0; x < (ssize_t) image->columns; x++)
8461 {
cristyef618312011-06-25 12:26:44 +00008462 if (GetPixelOpacity(r) == OpaqueOpacity)
8463 LBR03PixelRGB(r);
glennrp82b3c532011-03-22 19:20:54 +00008464 r++;
8465 }
glennrpbb4f99d2011-05-22 11:13:17 +00008466
glennrp82b3c532011-03-22 19:20:54 +00008467 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8468 break;
8469 }
8470 }
8471
8472 else /* Should not reach this; colormap already exists and
8473 must be <= 256 */
8474 {
8475 if (logging != MagickFalse)
8476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008477 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008478 for (i=0; i<image_colors; i++)
8479 {
glennrp91d99252011-06-25 14:30:13 +00008480 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008481 }
glennrpd3371642011-03-22 19:42:23 +00008482 }
8483 continue;
glennrp82b3c532011-03-22 19:20:54 +00008484 }
glennrpc8c2f062011-02-25 19:00:33 +00008485
glennrp8ca51ad2011-05-12 21:22:32 +00008486 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008487 {
8488 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008490 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008491
glennrp8ca51ad2011-05-12 21:22:32 +00008492 tried_332 = MagickTrue;
8493
glennrp3faa9a32011-04-23 14:00:25 +00008494 /* Red and green were already done so we only quantize the blue
8495 * channel
8496 */
8497
glennrp91d99252011-06-25 14:30:13 +00008498 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008499
glennrpc8c2f062011-02-25 19:00:33 +00008500 if (logging != MagickFalse)
8501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008502 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008503
glennrpc8c2f062011-02-25 19:00:33 +00008504 if (image->colormap == NULL)
8505 {
8506 for (y=0; y < (ssize_t) image->rows; y++)
8507 {
8508 r=GetAuthenticPixels(image,0,y,image->columns,1,
8509 exception);
8510
8511 if (r == (PixelPacket *) NULL)
8512 break;
8513
8514 for (x=0; x < (ssize_t) image->columns; x++)
8515 {
cristyef618312011-06-25 12:26:44 +00008516 if (GetPixelOpacity(r) == OpaqueOpacity)
8517 LBR02PixelBlue(r);
glennrp52a479c2011-02-26 21:14:38 +00008518 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008519 }
glennrpbb4f99d2011-05-22 11:13:17 +00008520
glennrpc8c2f062011-02-25 19:00:33 +00008521 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8522 break;
8523 }
8524 }
glennrpfd05d622011-02-25 04:10:33 +00008525
glennrpc8c2f062011-02-25 19:00:33 +00008526 else /* Should not reach this; colormap already exists and
8527 must be <= 256 */
8528 {
8529 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008531 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008532 for (i=0; i<image_colors; i++)
8533 {
glennrp91d99252011-06-25 14:30:13 +00008534 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008535 }
8536 }
8537 continue;
8538 }
8539 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008540
8541 if (image_colors == 0 || image_colors > 256)
8542 {
8543 /* Take care of special case with 256 colors + 1 transparent
8544 * color. We don't need to quantize to 2-3-2-1; we only need to
8545 * eliminate one color, so we'll merge the two darkest red
8546 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8547 */
8548 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8549 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8550 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8551 {
8552 image->background_color.red=ScaleCharToQuantum(0x24);
8553 }
glennrpbb4f99d2011-05-22 11:13:17 +00008554
glennrp8ca51ad2011-05-12 21:22:32 +00008555 if (image->colormap == NULL)
8556 {
8557 for (y=0; y < (ssize_t) image->rows; y++)
8558 {
8559 r=GetAuthenticPixels(image,0,y,image->columns,1,
8560 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008561
glennrp8ca51ad2011-05-12 21:22:32 +00008562 if (r == (PixelPacket *) NULL)
8563 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008564
glennrp8ca51ad2011-05-12 21:22:32 +00008565 for (x=0; x < (ssize_t) image->columns; x++)
8566 {
cristyef618312011-06-25 12:26:44 +00008567 if (ScaleQuantumToChar(GetPixelRed(r)) == 0x49 &&
8568 ScaleQuantumToChar(GetPixelGreen(r)) == 0x00 &&
8569 ScaleQuantumToChar(GetPixelBlue(r)) == 0x00 &&
8570 GetPixelOpacity(r) == OpaqueOpacity)
glennrp8ca51ad2011-05-12 21:22:32 +00008571 {
cristyef618312011-06-25 12:26:44 +00008572 SetPixelRed(r,ScaleCharToQuantum(0x24));
glennrp8ca51ad2011-05-12 21:22:32 +00008573 }
8574 r++;
8575 }
glennrpbb4f99d2011-05-22 11:13:17 +00008576
glennrp8ca51ad2011-05-12 21:22:32 +00008577 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8578 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008579
glennrp8ca51ad2011-05-12 21:22:32 +00008580 }
8581 }
8582
8583 else
8584 {
8585 for (i=0; i<image_colors; i++)
8586 {
8587 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8588 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8589 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8590 {
8591 image->colormap[i].red=ScaleCharToQuantum(0x24);
8592 }
8593 }
8594 }
8595 }
glennrpd71e86a2011-02-24 01:28:37 +00008596 }
glennrpfd05d622011-02-25 04:10:33 +00008597 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008598
glennrpfd05d622011-02-25 04:10:33 +00008599 /* If we are excluding the tRNS chunk and there is transparency,
8600 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8601 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008602 */
glennrp0e8ea192010-12-24 18:00:33 +00008603 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8604 (number_transparent != 0 || number_semitransparent != 0))
8605 {
glennrpd17915c2011-04-29 14:24:22 +00008606 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008607
8608 if (ping_have_color == MagickFalse)
8609 mng_info->write_png_colortype = 5;
8610
8611 else
8612 mng_info->write_png_colortype = 7;
8613
glennrp8d579662011-02-23 02:05:02 +00008614 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008615 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008616 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008617
glennrp0e8ea192010-12-24 18:00:33 +00008618 }
8619
glennrpfd05d622011-02-25 04:10:33 +00008620 /* See if cheap transparency is possible. It is only possible
8621 * when there is a single transparent color, no semitransparent
8622 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008623 * as the transparent color. We only need this information if
8624 * we are writing a PNG with colortype 0 or 2, and we have not
8625 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008626 */
glennrp5a39f372011-02-25 04:52:16 +00008627 if (number_transparent == 1 &&
8628 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008629 {
8630 ping_have_cheap_transparency = MagickTrue;
8631
8632 if (number_semitransparent != 0)
8633 ping_have_cheap_transparency = MagickFalse;
8634
8635 else if (image_colors == 0 || image_colors > 256 ||
8636 image->colormap == NULL)
8637 {
8638 ExceptionInfo
8639 *exception;
8640
8641 register const PixelPacket
8642 *q;
8643
8644 exception=(&image->exception);
8645
8646 for (y=0; y < (ssize_t) image->rows; y++)
8647 {
8648 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8649
8650 if (q == (PixelPacket *) NULL)
8651 break;
8652
8653 for (x=0; x < (ssize_t) image->columns; x++)
8654 {
8655 if (q->opacity != TransparentOpacity &&
cristyef618312011-06-25 12:26:44 +00008656 (unsigned short) GetPixelRed(q) ==
glennrp7c7b3152011-04-26 04:01:27 +00008657 ping_trans_color.red &&
cristyef618312011-06-25 12:26:44 +00008658 (unsigned short) GetPixelGreen(q) ==
glennrp7c7b3152011-04-26 04:01:27 +00008659 ping_trans_color.green &&
cristyef618312011-06-25 12:26:44 +00008660 (unsigned short) GetPixelBlue(q) ==
glennrp7c7b3152011-04-26 04:01:27 +00008661 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008662 {
8663 ping_have_cheap_transparency = MagickFalse;
8664 break;
8665 }
8666
8667 q++;
8668 }
glennrpbb4f99d2011-05-22 11:13:17 +00008669
glennrpfd05d622011-02-25 04:10:33 +00008670 if (ping_have_cheap_transparency == MagickFalse)
8671 break;
8672 }
8673 }
8674 else
8675 {
glennrp67b9c1a2011-04-22 18:47:36 +00008676 /* Assuming that image->colormap[0] is the one transparent color
8677 * and that all others are opaque.
8678 */
glennrpfd05d622011-02-25 04:10:33 +00008679 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008680 for (i=1; i<image_colors; i++)
8681 if (image->colormap[i].red == image->colormap[0].red &&
8682 image->colormap[i].green == image->colormap[0].green &&
8683 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008684 {
glennrp67b9c1a2011-04-22 18:47:36 +00008685 ping_have_cheap_transparency = MagickFalse;
8686 break;
glennrpfd05d622011-02-25 04:10:33 +00008687 }
8688 }
glennrpbb4f99d2011-05-22 11:13:17 +00008689
glennrpfd05d622011-02-25 04:10:33 +00008690 if (logging != MagickFalse)
8691 {
8692 if (ping_have_cheap_transparency == MagickFalse)
8693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8694 " Cheap transparency is not possible.");
8695
8696 else
8697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8698 " Cheap transparency is possible.");
8699 }
8700 }
8701 else
8702 ping_have_cheap_transparency = MagickFalse;
8703
glennrp8640fb52010-11-23 15:48:26 +00008704 image_depth=image->depth;
8705
glennrp26c990a2010-11-23 02:23:20 +00008706 quantum_info = (QuantumInfo *) NULL;
8707 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008708 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008709 image_matte=image->matte;
8710
glennrp0fe50b42010-11-16 03:52:51 +00008711 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008712 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008713
glennrp52a479c2011-02-26 21:14:38 +00008714 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8715 (image->colors == 0 || image->colormap == NULL))
8716 {
glennrp52a479c2011-02-26 21:14:38 +00008717 image_info=DestroyImageInfo(image_info);
8718 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008719 (void) ThrowMagickException(&IMimage->exception,
8720 GetMagickModule(),CoderError,
8721 "Cannot write PNG8 or color-type 3; colormap is NULL",
8722 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008723#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8724 UnlockSemaphoreInfo(ping_semaphore);
8725#endif
8726 return(MagickFalse);
8727 }
8728
cristy3ed852e2009-09-05 21:47:34 +00008729 /*
8730 Allocate the PNG structures
8731 */
8732#ifdef PNG_USER_MEM_SUPPORTED
8733 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008734 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8735 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008736
cristy3ed852e2009-09-05 21:47:34 +00008737#else
8738 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008739 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008740
cristy3ed852e2009-09-05 21:47:34 +00008741#endif
8742 if (ping == (png_struct *) NULL)
8743 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008744
cristy3ed852e2009-09-05 21:47:34 +00008745 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008746
cristy3ed852e2009-09-05 21:47:34 +00008747 if (ping_info == (png_info *) NULL)
8748 {
8749 png_destroy_write_struct(&ping,(png_info **) NULL);
8750 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8751 }
glennrp0fe50b42010-11-16 03:52:51 +00008752
cristy3ed852e2009-09-05 21:47:34 +00008753 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008754 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008755
glennrp5af765f2010-03-30 11:12:18 +00008756 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008757 {
8758 /*
8759 PNG write failed.
8760 */
8761#ifdef PNG_DEBUG
8762 if (image_info->verbose)
8763 (void) printf("PNG write has failed.\n");
8764#endif
8765 png_destroy_write_struct(&ping,&ping_info);
8766#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008767 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008768#endif
glennrpda8f3a72011-02-27 23:54:12 +00008769 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008770 (void) CloseBlob(image);
8771 image_info=DestroyImageInfo(image_info);
8772 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008773 return(MagickFalse);
8774 }
8775 /*
8776 Prepare PNG for writing.
8777 */
8778#if defined(PNG_MNG_FEATURES_SUPPORTED)
8779 if (mng_info->write_mng)
8780 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008781
cristy3ed852e2009-09-05 21:47:34 +00008782#else
8783# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8784 if (mng_info->write_mng)
8785 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008786
cristy3ed852e2009-09-05 21:47:34 +00008787# endif
8788#endif
glennrp2b013e42010-11-24 16:55:50 +00008789
cristy3ed852e2009-09-05 21:47:34 +00008790 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008791
cristy4e5bc842010-06-09 13:56:01 +00008792 ping_width=(png_uint_32) image->columns;
8793 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008794
cristy3ed852e2009-09-05 21:47:34 +00008795 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8796 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008797
cristy3ed852e2009-09-05 21:47:34 +00008798 if (mng_info->write_png_depth != 0)
8799 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008800
cristy3ed852e2009-09-05 21:47:34 +00008801 /* Adjust requested depth to next higher valid depth if necessary */
8802 if (image_depth > 8)
8803 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008804
cristy3ed852e2009-09-05 21:47:34 +00008805 if ((image_depth > 4) && (image_depth < 8))
8806 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008807
cristy3ed852e2009-09-05 21:47:34 +00008808 if (image_depth == 3)
8809 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008810
cristy3ed852e2009-09-05 21:47:34 +00008811 if (logging != MagickFalse)
8812 {
8813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008814 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008816 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008818 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008820 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008822 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008823 }
glennrp8640fb52010-11-23 15:48:26 +00008824
cristy3ed852e2009-09-05 21:47:34 +00008825 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008826 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008827
glennrp26f37912010-12-23 16:22:42 +00008828
cristy3ed852e2009-09-05 21:47:34 +00008829#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008830 if (ping_exclude_pHYs == MagickFalse)
8831 {
cristy3ed852e2009-09-05 21:47:34 +00008832 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8833 (!mng_info->write_mng || !mng_info->equal_physs))
8834 {
glennrp0fe50b42010-11-16 03:52:51 +00008835 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8837 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008838
8839 if (image->units == PixelsPerInchResolution)
8840 {
glennrpdfd70802010-11-14 01:23:35 +00008841 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008842 ping_pHYs_x_resolution=
8843 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8844 ping_pHYs_y_resolution=
8845 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008846 }
glennrpdfd70802010-11-14 01:23:35 +00008847
cristy3ed852e2009-09-05 21:47:34 +00008848 else if (image->units == PixelsPerCentimeterResolution)
8849 {
glennrpdfd70802010-11-14 01:23:35 +00008850 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008851 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8852 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008853 }
glennrp991d11d2010-11-12 21:55:28 +00008854
cristy3ed852e2009-09-05 21:47:34 +00008855 else
8856 {
glennrpdfd70802010-11-14 01:23:35 +00008857 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8858 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8859 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008860 }
glennrp991d11d2010-11-12 21:55:28 +00008861
glennrp823b55c2011-03-14 18:46:46 +00008862 if (logging != MagickFalse)
8863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8864 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8865 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8866 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008867 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008868 }
glennrp26f37912010-12-23 16:22:42 +00008869 }
cristy3ed852e2009-09-05 21:47:34 +00008870#endif
glennrpa521b2f2010-10-29 04:11:03 +00008871
glennrp26f37912010-12-23 16:22:42 +00008872 if (ping_exclude_bKGD == MagickFalse)
8873 {
glennrpa521b2f2010-10-29 04:11:03 +00008874 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008875 {
glennrpa521b2f2010-10-29 04:11:03 +00008876 unsigned int
8877 mask;
cristy3ed852e2009-09-05 21:47:34 +00008878
glennrpa521b2f2010-10-29 04:11:03 +00008879 mask=0xffff;
8880 if (ping_bit_depth == 8)
8881 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008882
glennrpa521b2f2010-10-29 04:11:03 +00008883 if (ping_bit_depth == 4)
8884 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008885
glennrpa521b2f2010-10-29 04:11:03 +00008886 if (ping_bit_depth == 2)
8887 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008888
glennrpa521b2f2010-10-29 04:11:03 +00008889 if (ping_bit_depth == 1)
8890 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008891
glennrpa521b2f2010-10-29 04:11:03 +00008892 ping_background.red=(png_uint_16)
8893 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008894
glennrpa521b2f2010-10-29 04:11:03 +00008895 ping_background.green=(png_uint_16)
8896 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008897
glennrpa521b2f2010-10-29 04:11:03 +00008898 ping_background.blue=(png_uint_16)
8899 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00008900
8901 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00008902 }
cristy3ed852e2009-09-05 21:47:34 +00008903
glennrp0fe50b42010-11-16 03:52:51 +00008904 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008905 {
8906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8907 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00008908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8909 " background_color index is %d",
8910 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008911
8912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8913 " ping_bit_depth=%d",ping_bit_depth);
8914 }
glennrp0fe50b42010-11-16 03:52:51 +00008915
8916 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008917 }
glennrp0fe50b42010-11-16 03:52:51 +00008918
cristy3ed852e2009-09-05 21:47:34 +00008919 /*
8920 Select the color type.
8921 */
8922 matte=image_matte;
8923 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008924
glennrp1273f7b2011-02-24 03:20:30 +00008925 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008926 {
glennrp0fe50b42010-11-16 03:52:51 +00008927
glennrpfd05d622011-02-25 04:10:33 +00008928 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008929 for reducing the sample depth from 8. */
8930
glennrp0fe50b42010-11-16 03:52:51 +00008931 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008932
glennrp8bb3a022010-12-13 20:40:04 +00008933 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008934
8935 /*
8936 Set image palette.
8937 */
8938 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8939
glennrp0fe50b42010-11-16 03:52:51 +00008940 if (logging != MagickFalse)
8941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8942 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008943 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008944
8945 for (i=0; i < (ssize_t) number_colors; i++)
8946 {
8947 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8948 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8949 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8950 if (logging != MagickFalse)
8951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008952#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008953 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008954#else
8955 " %5ld (%5d,%5d,%5d)",
8956#endif
glennrp0fe50b42010-11-16 03:52:51 +00008957 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8958
8959 }
glennrp2b013e42010-11-24 16:55:50 +00008960
glennrp8bb3a022010-12-13 20:40:04 +00008961 ping_have_PLTE=MagickTrue;
8962 image_depth=ping_bit_depth;
8963 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008964
glennrp58e01762011-01-07 15:28:54 +00008965 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008966 {
glennrp0fe50b42010-11-16 03:52:51 +00008967 /*
8968 Identify which colormap entry is transparent.
8969 */
8970 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008971 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008972
glennrp8bb3a022010-12-13 20:40:04 +00008973 for (i=0; i < (ssize_t) number_transparent; i++)
8974 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008975
glennrp0fe50b42010-11-16 03:52:51 +00008976
glennrp2cc891a2010-12-24 13:44:32 +00008977 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008978 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008979
8980 if (ping_num_trans == 0)
8981 ping_have_tRNS=MagickFalse;
8982
glennrp8bb3a022010-12-13 20:40:04 +00008983 else
8984 ping_have_tRNS=MagickTrue;
8985 }
glennrp0fe50b42010-11-16 03:52:51 +00008986
glennrp1273f7b2011-02-24 03:20:30 +00008987 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008988 {
glennrp1273f7b2011-02-24 03:20:30 +00008989 /*
8990 * Identify which colormap entry is the background color.
8991 */
8992
glennrp4f25bd02011-01-01 18:51:28 +00008993 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8994 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8995 break;
glennrp0fe50b42010-11-16 03:52:51 +00008996
glennrp4f25bd02011-01-01 18:51:28 +00008997 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00008998
8999 if (logging != MagickFalse)
9000 {
9001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9002 " background_color index is %d",
9003 (int) ping_background.index);
9004 }
glennrp4f25bd02011-01-01 18:51:28 +00009005 }
cristy3ed852e2009-09-05 21:47:34 +00009006 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009007
cristy3ed852e2009-09-05 21:47:34 +00009008 else if (mng_info->write_png24)
9009 {
9010 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009011 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009012 }
glennrp0fe50b42010-11-16 03:52:51 +00009013
cristy3ed852e2009-09-05 21:47:34 +00009014 else if (mng_info->write_png32)
9015 {
9016 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009017 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009018 }
glennrp0fe50b42010-11-16 03:52:51 +00009019
glennrp8bb3a022010-12-13 20:40:04 +00009020 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009021 {
glennrp5af765f2010-03-30 11:12:18 +00009022 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009023
glennrp8bb3a022010-12-13 20:40:04 +00009024 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009025 {
glennrp5af765f2010-03-30 11:12:18 +00009026 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009027
glennrp5af765f2010-03-30 11:12:18 +00009028 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9029 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009030 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009031
glennrp8bb3a022010-12-13 20:40:04 +00009032 else
9033 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009034
9035 if (logging != MagickFalse)
9036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9037 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009038 }
glennrp0fe50b42010-11-16 03:52:51 +00009039
glennrp7c4c9e62011-03-21 20:23:32 +00009040 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009041 {
9042 if (logging != MagickFalse)
9043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009044 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009045
glennrpd6bf1612010-12-17 17:28:54 +00009046 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009047 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009048
glennrpd6bf1612010-12-17 17:28:54 +00009049 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009050 {
glennrp5af765f2010-03-30 11:12:18 +00009051 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009052 image_matte=MagickFalse;
9053 }
glennrp0fe50b42010-11-16 03:52:51 +00009054
glennrpd6bf1612010-12-17 17:28:54 +00009055 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009056 {
glennrp5af765f2010-03-30 11:12:18 +00009057 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009058 image_matte=MagickTrue;
9059 }
glennrp0fe50b42010-11-16 03:52:51 +00009060
glennrp5aa37f62011-01-02 03:07:57 +00009061 if (image_info->type == PaletteType ||
9062 image_info->type == PaletteMatteType)
9063 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9064
glennrp7c4c9e62011-03-21 20:23:32 +00009065 if (mng_info->write_png_colortype == 0 &&
9066 (image_info->type == UndefinedType ||
9067 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009068 {
glennrp5aa37f62011-01-02 03:07:57 +00009069 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009070 {
glennrp5aa37f62011-01-02 03:07:57 +00009071 if (image_matte == MagickFalse)
9072 {
9073 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9074 image_matte=MagickFalse;
9075 }
glennrp0fe50b42010-11-16 03:52:51 +00009076
glennrp0b206f52011-01-07 04:55:32 +00009077 else
glennrp5aa37f62011-01-02 03:07:57 +00009078 {
9079 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9080 image_matte=MagickTrue;
9081 }
9082 }
9083 else
glennrp8bb3a022010-12-13 20:40:04 +00009084 {
glennrp5aa37f62011-01-02 03:07:57 +00009085 if (image_matte == MagickFalse)
9086 {
9087 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9088 image_matte=MagickFalse;
9089 }
glennrp8bb3a022010-12-13 20:40:04 +00009090
glennrp0b206f52011-01-07 04:55:32 +00009091 else
glennrp5aa37f62011-01-02 03:07:57 +00009092 {
9093 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9094 image_matte=MagickTrue;
9095 }
9096 }
glennrp0fe50b42010-11-16 03:52:51 +00009097 }
glennrp5aa37f62011-01-02 03:07:57 +00009098
cristy3ed852e2009-09-05 21:47:34 +00009099 }
glennrp0fe50b42010-11-16 03:52:51 +00009100
cristy3ed852e2009-09-05 21:47:34 +00009101 if (logging != MagickFalse)
9102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009103 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009104
glennrp5af765f2010-03-30 11:12:18 +00009105 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009106 {
9107 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9108 ping_color_type == PNG_COLOR_TYPE_RGB ||
9109 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9110 ping_bit_depth=8;
9111 }
cristy3ed852e2009-09-05 21:47:34 +00009112
glennrpd6bf1612010-12-17 17:28:54 +00009113 old_bit_depth=ping_bit_depth;
9114
glennrp5af765f2010-03-30 11:12:18 +00009115 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009116 {
glennrp8d579662011-02-23 02:05:02 +00009117 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9118 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009119 }
glennrp8640fb52010-11-23 15:48:26 +00009120
glennrp5af765f2010-03-30 11:12:18 +00009121 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009122 {
cristy35ef8242010-06-03 16:24:13 +00009123 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009124 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009125
9126 if (image->colors == 0)
9127 {
glennrp0fe50b42010-11-16 03:52:51 +00009128 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00009129 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00009130 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009131 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009132 }
9133
cristy35ef8242010-06-03 16:24:13 +00009134 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009135 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009136 }
glennrp2b013e42010-11-24 16:55:50 +00009137
glennrpd6bf1612010-12-17 17:28:54 +00009138 if (logging != MagickFalse)
9139 {
9140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9141 " Number of colors: %.20g",(double) image_colors);
9142
9143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9144 " Tentative PNG bit depth: %d",ping_bit_depth);
9145 }
9146
9147 if (ping_bit_depth < (int) mng_info->write_png_depth)
9148 ping_bit_depth = mng_info->write_png_depth;
9149 }
glennrp2cc891a2010-12-24 13:44:32 +00009150
glennrp5af765f2010-03-30 11:12:18 +00009151 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009152
cristy3ed852e2009-09-05 21:47:34 +00009153 if (logging != MagickFalse)
9154 {
9155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009156 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009157
cristy3ed852e2009-09-05 21:47:34 +00009158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009159 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009160
cristy3ed852e2009-09-05 21:47:34 +00009161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009162 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009163
cristy3ed852e2009-09-05 21:47:34 +00009164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009165
glennrp8640fb52010-11-23 15:48:26 +00009166 " image->depth: %.20g",(double) image->depth);
9167
9168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009169 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009170 }
9171
glennrp58e01762011-01-07 15:28:54 +00009172 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009173 {
glennrp4f25bd02011-01-01 18:51:28 +00009174 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009175 {
glennrp7c4c9e62011-03-21 20:23:32 +00009176 if (mng_info->write_png_colortype == 0)
9177 {
9178 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009179
glennrp7c4c9e62011-03-21 20:23:32 +00009180 if (ping_have_color != MagickFalse)
9181 ping_color_type=PNG_COLOR_TYPE_RGBA;
9182 }
glennrp4f25bd02011-01-01 18:51:28 +00009183
9184 /*
9185 * Determine if there is any transparent color.
9186 */
9187 if (number_transparent + number_semitransparent == 0)
9188 {
9189 /*
9190 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9191 */
glennrpa6a06632011-01-19 15:15:34 +00009192
glennrp4f25bd02011-01-01 18:51:28 +00009193 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009194
9195 if (mng_info->write_png_colortype == 0)
9196 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009197 }
9198
9199 else
9200 {
9201 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009202 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009203
9204 mask=0xffff;
9205
9206 if (ping_bit_depth == 8)
9207 mask=0x00ff;
9208
9209 if (ping_bit_depth == 4)
9210 mask=0x000f;
9211
9212 if (ping_bit_depth == 2)
9213 mask=0x0003;
9214
9215 if (ping_bit_depth == 1)
9216 mask=0x0001;
9217
9218 ping_trans_color.red=(png_uint_16)
9219 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9220
9221 ping_trans_color.green=(png_uint_16)
9222 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9223
9224 ping_trans_color.blue=(png_uint_16)
9225 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9226
9227 ping_trans_color.gray=(png_uint_16)
9228 (ScaleQuantumToShort(PixelIntensityToQuantum(
9229 image->colormap)) & mask);
9230
9231 ping_trans_color.index=(png_byte) 0;
9232
9233 ping_have_tRNS=MagickTrue;
9234 }
9235
9236 if (ping_have_tRNS != MagickFalse)
9237 {
9238 /*
glennrpfd05d622011-02-25 04:10:33 +00009239 * Determine if there is one and only one transparent color
9240 * and if so if it is fully transparent.
9241 */
9242 if (ping_have_cheap_transparency == MagickFalse)
9243 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009244 }
9245
9246 if (ping_have_tRNS != MagickFalse)
9247 {
glennrp7c4c9e62011-03-21 20:23:32 +00009248 if (mng_info->write_png_colortype == 0)
9249 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009250
9251 if (image_depth == 8)
9252 {
9253 ping_trans_color.red&=0xff;
9254 ping_trans_color.green&=0xff;
9255 ping_trans_color.blue&=0xff;
9256 ping_trans_color.gray&=0xff;
9257 }
9258 }
9259 }
cristy3ed852e2009-09-05 21:47:34 +00009260 else
9261 {
cristy3ed852e2009-09-05 21:47:34 +00009262 if (image_depth == 8)
9263 {
glennrp5af765f2010-03-30 11:12:18 +00009264 ping_trans_color.red&=0xff;
9265 ping_trans_color.green&=0xff;
9266 ping_trans_color.blue&=0xff;
9267 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009268 }
9269 }
9270 }
glennrp8640fb52010-11-23 15:48:26 +00009271
cristy3ed852e2009-09-05 21:47:34 +00009272 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009273
glennrp2e09f552010-11-14 00:38:48 +00009274 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009275 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009276
glennrp39992b42010-11-14 00:03:43 +00009277 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009278 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009279 ping_have_color == MagickFalse &&
9280 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009281 {
cristy35ef8242010-06-03 16:24:13 +00009282 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009283
cristy3ed852e2009-09-05 21:47:34 +00009284 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009285 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009286
glennrp7c4c9e62011-03-21 20:23:32 +00009287 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009288 {
glennrp5af765f2010-03-30 11:12:18 +00009289 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009290
cristy3ed852e2009-09-05 21:47:34 +00009291 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009292 {
9293 if (logging != MagickFalse)
9294 {
9295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9296 " Scaling ping_trans_color (0)");
9297 }
9298 ping_trans_color.gray*=0x0101;
9299 }
cristy3ed852e2009-09-05 21:47:34 +00009300 }
glennrp0fe50b42010-11-16 03:52:51 +00009301
cristy3ed852e2009-09-05 21:47:34 +00009302 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9303 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009304
glennrp136ee3a2011-04-27 15:47:45 +00009305 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009306 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009307 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009308
cristy3ed852e2009-09-05 21:47:34 +00009309 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009310 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009311
cristy3ed852e2009-09-05 21:47:34 +00009312 else
9313 {
glennrp5af765f2010-03-30 11:12:18 +00009314 ping_bit_depth=8;
9315 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009316 {
9317 if(!mng_info->write_png_depth)
9318 {
glennrp5af765f2010-03-30 11:12:18 +00009319 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009320
cristy35ef8242010-06-03 16:24:13 +00009321 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009322 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009323 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009324 }
9325 }
glennrp2b013e42010-11-24 16:55:50 +00009326
glennrp0fe50b42010-11-16 03:52:51 +00009327 else if (ping_color_type ==
9328 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009329 mng_info->IsPalette)
9330 {
cristy3ed852e2009-09-05 21:47:34 +00009331 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009332
cristy3ed852e2009-09-05 21:47:34 +00009333 int
9334 depth_4_ok=MagickTrue,
9335 depth_2_ok=MagickTrue,
9336 depth_1_ok=MagickTrue;
9337
cristybb503372010-05-27 20:51:26 +00009338 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009339 {
9340 unsigned char
9341 intensity;
9342
9343 intensity=ScaleQuantumToChar(image->colormap[i].red);
9344
9345 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9346 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9347 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9348 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009349 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009350 depth_1_ok=MagickFalse;
9351 }
glennrp2b013e42010-11-24 16:55:50 +00009352
cristy3ed852e2009-09-05 21:47:34 +00009353 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009354 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009355
cristy3ed852e2009-09-05 21:47:34 +00009356 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009357 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009358
cristy3ed852e2009-09-05 21:47:34 +00009359 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009360 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009361 }
9362 }
glennrp2b013e42010-11-24 16:55:50 +00009363
glennrp5af765f2010-03-30 11:12:18 +00009364 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009365 }
glennrp0fe50b42010-11-16 03:52:51 +00009366
cristy3ed852e2009-09-05 21:47:34 +00009367 else
glennrp0fe50b42010-11-16 03:52:51 +00009368
cristy3ed852e2009-09-05 21:47:34 +00009369 if (mng_info->IsPalette)
9370 {
glennrp17a14852010-05-10 03:01:59 +00009371 number_colors=image_colors;
9372
cristy3ed852e2009-09-05 21:47:34 +00009373 if (image_depth <= 8)
9374 {
cristy3ed852e2009-09-05 21:47:34 +00009375 /*
9376 Set image palette.
9377 */
glennrp5af765f2010-03-30 11:12:18 +00009378 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009379
glennrp58e01762011-01-07 15:28:54 +00009380 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009381 {
glennrp9c1eb072010-06-06 22:19:15 +00009382 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009383
glennrp3b51f0e2010-11-27 18:14:08 +00009384 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9386 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009387 }
glennrp0fe50b42010-11-16 03:52:51 +00009388
cristy3ed852e2009-09-05 21:47:34 +00009389 else
9390 {
cristybb503372010-05-27 20:51:26 +00009391 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009392 {
9393 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9394 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9395 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9396 }
glennrp0fe50b42010-11-16 03:52:51 +00009397
glennrp3b51f0e2010-11-27 18:14:08 +00009398 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009400 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009401 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009402
glennrp39992b42010-11-14 00:03:43 +00009403 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009404 }
glennrp0fe50b42010-11-16 03:52:51 +00009405
cristy3ed852e2009-09-05 21:47:34 +00009406 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009407 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009408 {
cristybefe4d22010-06-07 01:18:58 +00009409 size_t
9410 one;
9411
glennrp5af765f2010-03-30 11:12:18 +00009412 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009413 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009414
glennrpd17915c2011-04-29 14:24:22 +00009415 while ((one << ping_bit_depth) < (ssize_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009416 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009417 }
glennrp0fe50b42010-11-16 03:52:51 +00009418
glennrp5af765f2010-03-30 11:12:18 +00009419 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009420
glennrp58e01762011-01-07 15:28:54 +00009421 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009422 {
glennrp0fe50b42010-11-16 03:52:51 +00009423 /*
glennrpd6bf1612010-12-17 17:28:54 +00009424 * Set up trans_colors array.
9425 */
glennrp0fe50b42010-11-16 03:52:51 +00009426 assert(number_colors <= 256);
9427
glennrpd6bf1612010-12-17 17:28:54 +00009428 ping_num_trans=(unsigned short) (number_transparent +
9429 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009430
9431 if (ping_num_trans == 0)
9432 ping_have_tRNS=MagickFalse;
9433
glennrpd6bf1612010-12-17 17:28:54 +00009434 else
glennrp0fe50b42010-11-16 03:52:51 +00009435 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009436 if (logging != MagickFalse)
9437 {
9438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9439 " Scaling ping_trans_color (1)");
9440 }
glennrpd6bf1612010-12-17 17:28:54 +00009441 ping_have_tRNS=MagickTrue;
9442
9443 for (i=0; i < ping_num_trans; i++)
9444 {
9445 ping_trans_alpha[i]= (png_byte) (255-
9446 ScaleQuantumToChar(image->colormap[i].opacity));
9447 }
glennrp0fe50b42010-11-16 03:52:51 +00009448 }
9449 }
cristy3ed852e2009-09-05 21:47:34 +00009450 }
9451 }
glennrp0fe50b42010-11-16 03:52:51 +00009452
cristy3ed852e2009-09-05 21:47:34 +00009453 else
9454 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009455
cristy3ed852e2009-09-05 21:47:34 +00009456 if (image_depth < 8)
9457 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009458
cristy3ed852e2009-09-05 21:47:34 +00009459 if ((save_image_depth == 16) && (image_depth == 8))
9460 {
glennrp4f25bd02011-01-01 18:51:28 +00009461 if (logging != MagickFalse)
9462 {
9463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9464 " Scaling ping_trans_color from (%d,%d,%d)",
9465 (int) ping_trans_color.red,
9466 (int) ping_trans_color.green,
9467 (int) ping_trans_color.blue);
9468 }
9469
glennrp5af765f2010-03-30 11:12:18 +00009470 ping_trans_color.red*=0x0101;
9471 ping_trans_color.green*=0x0101;
9472 ping_trans_color.blue*=0x0101;
9473 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009474
9475 if (logging != MagickFalse)
9476 {
9477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9478 " to (%d,%d,%d)",
9479 (int) ping_trans_color.red,
9480 (int) ping_trans_color.green,
9481 (int) ping_trans_color.blue);
9482 }
cristy3ed852e2009-09-05 21:47:34 +00009483 }
9484 }
9485
cristy4383ec82011-01-05 15:42:32 +00009486 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9487 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009488
cristy3ed852e2009-09-05 21:47:34 +00009489 /*
9490 Adjust background and transparency samples in sub-8-bit grayscale files.
9491 */
glennrp5af765f2010-03-30 11:12:18 +00009492 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009493 PNG_COLOR_TYPE_GRAY)
9494 {
9495 png_uint_16
9496 maxval;
9497
cristy35ef8242010-06-03 16:24:13 +00009498 size_t
9499 one=1;
9500
cristy22ffd972010-06-03 16:51:47 +00009501 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009502
glennrp4f25bd02011-01-01 18:51:28 +00009503 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009504 {
cristy3ed852e2009-09-05 21:47:34 +00009505
glennrpa521b2f2010-10-29 04:11:03 +00009506 ping_background.gray=(png_uint_16)
glennrp9be9b1c2011-06-09 12:21:45 +00009507 ((maxval/255.)*((PixelIntensity(&image->background_color)))+.5);
cristy3ed852e2009-09-05 21:47:34 +00009508
9509 if (logging != MagickFalse)
9510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009511 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9513 " background_color index is %d",
9514 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009515
glennrp991d11d2010-11-12 21:55:28 +00009516 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009517 }
cristy3ed852e2009-09-05 21:47:34 +00009518
glennrp3e3e20f2011-06-09 04:21:43 +00009519 if (logging != MagickFalse)
9520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9521 " Scaling ping_trans_color.gray from %d",
9522 (int)ping_trans_color.gray);
9523
glennrp9be9b1c2011-06-09 12:21:45 +00009524 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009525 ping_trans_color.gray)+.5);
9526
9527 if (logging != MagickFalse)
9528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9529 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009530 }
glennrp17a14852010-05-10 03:01:59 +00009531
glennrp26f37912010-12-23 16:22:42 +00009532 if (ping_exclude_bKGD == MagickFalse)
9533 {
glennrp1273f7b2011-02-24 03:20:30 +00009534 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009535 {
9536 /*
9537 Identify which colormap entry is the background color.
9538 */
9539
glennrp17a14852010-05-10 03:01:59 +00009540 number_colors=image_colors;
9541
glennrpa521b2f2010-10-29 04:11:03 +00009542 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9543 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009544 break;
9545
9546 ping_background.index=(png_byte) i;
9547
glennrp3b51f0e2010-11-27 18:14:08 +00009548 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009549 {
9550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009551 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009552 }
glennrp0fe50b42010-11-16 03:52:51 +00009553
cristy13d07042010-11-21 20:56:18 +00009554 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009555 {
9556 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009557
9558 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009559 {
9560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9561 " background =(%d,%d,%d)",
9562 (int) ping_background.red,
9563 (int) ping_background.green,
9564 (int) ping_background.blue);
9565 }
9566 }
glennrpa521b2f2010-10-29 04:11:03 +00009567
glennrpd6bf1612010-12-17 17:28:54 +00009568 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009569 {
glennrp3b51f0e2010-11-27 18:14:08 +00009570 if (logging != MagickFalse)
9571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9572 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009573 ping_have_bKGD = MagickFalse;
9574 }
glennrp17a14852010-05-10 03:01:59 +00009575 }
glennrp26f37912010-12-23 16:22:42 +00009576 }
glennrp17a14852010-05-10 03:01:59 +00009577
cristy3ed852e2009-09-05 21:47:34 +00009578 if (logging != MagickFalse)
9579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009580 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009581 /*
9582 Initialize compression level and filtering.
9583 */
9584 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009585 {
9586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9587 " Setting up deflate compression");
9588
9589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9590 " Compression buffer size: 32768");
9591 }
9592
cristy3ed852e2009-09-05 21:47:34 +00009593 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009594
cristy3ed852e2009-09-05 21:47:34 +00009595 if (logging != MagickFalse)
9596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9597 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009598
cristy3ed852e2009-09-05 21:47:34 +00009599 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00009600
glennrp10d739e2011-06-29 18:00:52 +00009601 /* Untangle the "-quality" setting:
9602
9603 Undefined is 0; the default is used.
9604 Default is 75
9605
9606 10's digit:
9607
9608 0: Use Z_HUFFMAN_ONLY strategy with the
9609 zlib default compression level
9610
9611 1-9: the zlib compression level
9612
9613 1's digit:
9614
9615 0-4: the PNG filter method
9616
9617 5: libpng adaptive filtering if compression level > 5
9618 libpng filter type "none" if compression level <= 5
9619 or if image is grayscale or palette
9620
9621 6: libpng adaptive filtering
9622
9623 7: "LOCO" filtering (intrapixel differing) if writing
9624 a MNG, othewise "none". Did not work in IM-6.7.0-9
9625 and earlier because of a missing "else".
9626
9627 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009628 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009629
9630 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009631 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009632
9633 Note that using the -quality option, not all combinations of
9634 PNG filter type, zlib compression level, and zlib compression
9635 strategy are possible. This will be addressed soon in a
9636 release that accomodates "-define PNG:compression-strategy",
9637 etc.
9638
9639 */
9640
cristy3ed852e2009-09-05 21:47:34 +00009641 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9642 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009643
glennrp18682582011-06-30 18:11:47 +00009644 if (quality <= 9)
9645 {
9646 if (mng_info->write_png_compression_strategy == 0)
9647 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9648 }
9649
9650 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009651 {
9652 int
9653 level;
9654
cristybb503372010-05-27 20:51:26 +00009655 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009656
glennrp18682582011-06-30 18:11:47 +00009657 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009658 }
glennrp0fe50b42010-11-16 03:52:51 +00009659
glennrp18682582011-06-30 18:11:47 +00009660 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009661 {
glennrp18682582011-06-30 18:11:47 +00009662 if ((quality %10) == 8 || (quality %10) == 9)
9663 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009664 }
glennrp0fe50b42010-11-16 03:52:51 +00009665
glennrp18682582011-06-30 18:11:47 +00009666 if (mng_info->write_png_compression_filter == 0)
9667 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9668
cristy3ed852e2009-09-05 21:47:34 +00009669 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009670 {
glennrp18682582011-06-30 18:11:47 +00009671 if (mng_info->write_png_compression_level)
9672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9673 " Compression level: %d",
9674 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009675
glennrp18682582011-06-30 18:11:47 +00009676 if (mng_info->write_png_compression_strategy)
9677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9678 " Compression strategy: %d",
9679 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009680
glennrp18682582011-06-30 18:11:47 +00009681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9682 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009683
glennrp18682582011-06-30 18:11:47 +00009684 if (mng_info->write_png_compression_filter == PNG_ALL_FILTERS+1)
cristy3ed852e2009-09-05 21:47:34 +00009685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9686 " Base filter method: ADAPTIVE");
glennrp18682582011-06-30 18:11:47 +00009687 else if (mng_info->write_png_compression_filter == PNG_NO_FILTERS+1)
cristy3ed852e2009-09-05 21:47:34 +00009688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9689 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009690 else
9691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9692 " Base filter method: %d",
9693 (int) mng_info->write_png_compression_filter-1);
9694 }
glennrp2b013e42010-11-24 16:55:50 +00009695
glennrp18682582011-06-30 18:11:47 +00009696 if (mng_info->write_png_compression_level != 0)
9697 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9698
9699 if (mng_info->write_png_compression_filter == 6)
9700 {
9701 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9702 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9703 (quality < 50))
9704 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9705 else
9706 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9707 }
9708
9709 if (mng_info->write_png_compression_filter == 7 ||
9710 mng_info->write_png_compression_filter == 10)
9711 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9712
9713 else if (mng_info->write_png_compression_filter == 8)
9714 {
9715#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9716 if (mng_info->write_mng)
9717 {
9718 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9719 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9720 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9721 }
9722#endif
9723 png_set_filter(ping,PNG_FILTER_TYPE_BASE,0);
9724 }
9725
9726 else if (mng_info->write_png_compression_filter == 9)
9727 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9728
9729 else if (mng_info->write_png_compression_filter != 0)
9730 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9731 mng_info->write_png_compression_filter-1);
9732
9733 if (mng_info->write_png_compression_strategy != 0)
9734 png_set_compression_strategy(ping,
9735 mng_info->write_png_compression_strategy-1);
9736
cristy3ed852e2009-09-05 21:47:34 +00009737
glennrp823b55c2011-03-14 18:46:46 +00009738 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9739 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009740 {
9741 ResetImageProfileIterator(image);
9742 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009743 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009744 profile=GetImageProfile(image,name);
9745
9746 if (profile != (StringInfo *) NULL)
9747 {
glennrp5af765f2010-03-30 11:12:18 +00009748#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009749 if ((LocaleCompare(name,"ICC") == 0) ||
9750 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009751 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009752
9753 if (ping_exclude_iCCP == MagickFalse)
9754 {
9755 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009756#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009757 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009758#else
9759 (png_const_bytep) GetStringInfoDatum(profile),
9760#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009761 (png_uint_32) GetStringInfoLength(profile));
9762 }
glennrp26f37912010-12-23 16:22:42 +00009763 }
glennrp0fe50b42010-11-16 03:52:51 +00009764
glennrpc8cbc5d2011-01-01 00:12:34 +00009765 else
cristy3ed852e2009-09-05 21:47:34 +00009766#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009767 if (ping_exclude_zCCP == MagickFalse)
9768 {
glennrpcf002022011-01-30 02:38:15 +00009769 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009770 (unsigned char *) name,(unsigned char *) name,
9771 GetStringInfoDatum(profile),
9772 (png_uint_32) GetStringInfoLength(profile));
9773 }
9774 }
glennrp0b206f52011-01-07 04:55:32 +00009775
glennrpc8cbc5d2011-01-01 00:12:34 +00009776 if (logging != MagickFalse)
9777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9778 " Setting up text chunk with %s profile",name);
9779
9780 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009781 }
cristy3ed852e2009-09-05 21:47:34 +00009782 }
9783
9784#if defined(PNG_WRITE_sRGB_SUPPORTED)
9785 if ((mng_info->have_write_global_srgb == 0) &&
9786 ((image->rendering_intent != UndefinedIntent) ||
9787 (image->colorspace == sRGBColorspace)))
9788 {
glennrp26f37912010-12-23 16:22:42 +00009789 if (ping_exclude_sRGB == MagickFalse)
9790 {
9791 /*
9792 Note image rendering intent.
9793 */
9794 if (logging != MagickFalse)
9795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9796 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009797
glennrp26f37912010-12-23 16:22:42 +00009798 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009799 Magick_RenderingIntent_to_PNG_RenderingIntent(
9800 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009801
glennrp26f37912010-12-23 16:22:42 +00009802 if (ping_exclude_gAMA == MagickFalse)
9803 png_set_gAMA(ping,ping_info,0.45455);
9804 }
cristy3ed852e2009-09-05 21:47:34 +00009805 }
glennrp26f37912010-12-23 16:22:42 +00009806
glennrp5af765f2010-03-30 11:12:18 +00009807 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009808#endif
9809 {
glennrp2cc891a2010-12-24 13:44:32 +00009810 if (ping_exclude_gAMA == MagickFalse &&
9811 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009812 (image->gamma < .45 || image->gamma > .46)))
9813 {
cristy3ed852e2009-09-05 21:47:34 +00009814 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9815 {
9816 /*
9817 Note image gamma.
9818 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9819 */
9820 if (logging != MagickFalse)
9821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9822 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009823
cristy3ed852e2009-09-05 21:47:34 +00009824 png_set_gAMA(ping,ping_info,image->gamma);
9825 }
glennrp26f37912010-12-23 16:22:42 +00009826 }
glennrp2b013e42010-11-24 16:55:50 +00009827
glennrp26f37912010-12-23 16:22:42 +00009828 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009829 {
glennrp26f37912010-12-23 16:22:42 +00009830 if ((mng_info->have_write_global_chrm == 0) &&
9831 (image->chromaticity.red_primary.x != 0.0))
9832 {
9833 /*
9834 Note image chromaticity.
9835 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9836 */
9837 PrimaryInfo
9838 bp,
9839 gp,
9840 rp,
9841 wp;
cristy3ed852e2009-09-05 21:47:34 +00009842
glennrp26f37912010-12-23 16:22:42 +00009843 wp=image->chromaticity.white_point;
9844 rp=image->chromaticity.red_primary;
9845 gp=image->chromaticity.green_primary;
9846 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009847
glennrp26f37912010-12-23 16:22:42 +00009848 if (logging != MagickFalse)
9849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9850 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009851
glennrp26f37912010-12-23 16:22:42 +00009852 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9853 bp.x,bp.y);
9854 }
9855 }
cristy3ed852e2009-09-05 21:47:34 +00009856 }
glennrpdfd70802010-11-14 01:23:35 +00009857
glennrp5af765f2010-03-30 11:12:18 +00009858 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009859
9860 if (mng_info->write_mng)
9861 png_set_sig_bytes(ping,8);
9862
9863 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9864
glennrpd6bf1612010-12-17 17:28:54 +00009865 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009866 {
9867 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009868 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009869 {
glennrp5af765f2010-03-30 11:12:18 +00009870 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009871
glennrp5af765f2010-03-30 11:12:18 +00009872 if (ping_bit_depth < 8)
9873 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009874 }
glennrp0fe50b42010-11-16 03:52:51 +00009875
cristy3ed852e2009-09-05 21:47:34 +00009876 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009877 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009878 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009879 }
9880
glennrp0e8ea192010-12-24 18:00:33 +00009881 if (ping_need_colortype_warning != MagickFalse ||
9882 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009883 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009884 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009885 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009886 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009887 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009888 {
9889 if (logging != MagickFalse)
9890 {
glennrp0e8ea192010-12-24 18:00:33 +00009891 if (ping_need_colortype_warning != MagickFalse)
9892 {
9893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9894 " Image has transparency but tRNS chunk was excluded");
9895 }
9896
cristy3ed852e2009-09-05 21:47:34 +00009897 if (mng_info->write_png_depth)
9898 {
9899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9900 " Defined PNG:bit-depth=%u, Computed depth=%u",
9901 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009902 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009903 }
glennrp0e8ea192010-12-24 18:00:33 +00009904
cristy3ed852e2009-09-05 21:47:34 +00009905 if (mng_info->write_png_colortype)
9906 {
9907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9908 " Defined PNG:color-type=%u, Computed color type=%u",
9909 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009910 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009911 }
9912 }
glennrp0e8ea192010-12-24 18:00:33 +00009913
glennrp3bd2e412010-08-10 13:34:52 +00009914 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009915 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9916 }
9917
glennrp58e01762011-01-07 15:28:54 +00009918 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009919 {
9920 /* Add an opaque matte channel */
9921 image->matte = MagickTrue;
9922 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009923
glennrpb4a13412010-05-05 12:47:19 +00009924 if (logging != MagickFalse)
9925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9926 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009927 }
9928
glennrp0e319732011-01-25 21:53:13 +00009929 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009930 {
glennrp991d11d2010-11-12 21:55:28 +00009931 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009932 {
glennrp991d11d2010-11-12 21:55:28 +00009933 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009934 if (logging != MagickFalse)
9935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9936 " Setting ping_have_tRNS=MagickTrue.");
9937 }
glennrpe9c26dc2010-05-30 01:56:35 +00009938 }
9939
cristy3ed852e2009-09-05 21:47:34 +00009940 if (logging != MagickFalse)
9941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9942 " Writing PNG header chunks");
9943
glennrp5af765f2010-03-30 11:12:18 +00009944 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9945 ping_bit_depth,ping_color_type,
9946 ping_interlace_method,ping_compression_method,
9947 ping_filter_method);
9948
glennrp39992b42010-11-14 00:03:43 +00009949 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9950 {
glennrpf09bded2011-01-08 01:15:59 +00009951 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009952
glennrp3b51f0e2010-11-27 18:14:08 +00009953 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009954 {
glennrp8640fb52010-11-23 15:48:26 +00009955 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009956 {
glennrpd6bf1612010-12-17 17:28:54 +00009957 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009959 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9960 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009961 (int) palette[i].red,
9962 (int) palette[i].green,
9963 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009964 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009965 (int) ping_trans_alpha[i]);
9966 else
9967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009968 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009969 (int) i,
9970 (int) palette[i].red,
9971 (int) palette[i].green,
9972 (int) palette[i].blue);
9973 }
glennrp39992b42010-11-14 00:03:43 +00009974 }
glennrp39992b42010-11-14 00:03:43 +00009975 }
9976
glennrp26f37912010-12-23 16:22:42 +00009977 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009978 {
glennrp26f37912010-12-23 16:22:42 +00009979 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +00009980 {
glennrp26f37912010-12-23 16:22:42 +00009981 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +00009982 if (logging)
9983 {
9984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9985 " Setting up bKGD chunk");
9986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9987 " background color = (%d,%d,%d)",
9988 (int) ping_background.red,
9989 (int) ping_background.green,
9990 (int) ping_background.blue);
9991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9992 " index = %d, gray=%d",
9993 (int) ping_background.index,
9994 (int) ping_background.gray);
9995 }
9996 }
glennrp26f37912010-12-23 16:22:42 +00009997 }
9998
9999 if (ping_exclude_pHYs == MagickFalse)
10000 {
10001 if (ping_have_pHYs != MagickFalse)
10002 {
10003 png_set_pHYs(ping,ping_info,
10004 ping_pHYs_x_resolution,
10005 ping_pHYs_y_resolution,
10006 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010007
10008 if (logging)
10009 {
10010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10011 " Setting up pHYs chunk");
10012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10013 " x_resolution=%lu",
10014 (unsigned long) ping_pHYs_x_resolution);
10015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10016 " y_resolution=%lu",
10017 (unsigned long) ping_pHYs_y_resolution);
10018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10019 " unit_type=%lu",
10020 (unsigned long) ping_pHYs_unit_type);
10021 }
glennrp26f37912010-12-23 16:22:42 +000010022 }
glennrpdfd70802010-11-14 01:23:35 +000010023 }
10024
10025#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010026 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010027 {
glennrp26f37912010-12-23 16:22:42 +000010028 if (image->page.x || image->page.y)
10029 {
10030 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10031 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010032
glennrp26f37912010-12-23 16:22:42 +000010033 if (logging != MagickFalse)
10034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10035 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10036 (int) image->page.x, (int) image->page.y);
10037 }
glennrpdfd70802010-11-14 01:23:35 +000010038 }
10039#endif
10040
glennrpda8f3a72011-02-27 23:54:12 +000010041 if (mng_info->need_blob != MagickFalse)
10042 {
10043 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10044 MagickFalse)
10045 png_error(ping,"WriteBlob Failed");
10046
10047 ping_have_blob=MagickTrue;
10048 }
10049
cristy3ed852e2009-09-05 21:47:34 +000010050 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010051
glennrp39992b42010-11-14 00:03:43 +000010052 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010053 {
glennrp3b51f0e2010-11-27 18:14:08 +000010054 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010055 {
10056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10057 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10058 }
10059
10060 if (ping_color_type == 3)
10061 (void) png_set_tRNS(ping, ping_info,
10062 ping_trans_alpha,
10063 ping_num_trans,
10064 NULL);
10065
10066 else
10067 {
10068 (void) png_set_tRNS(ping, ping_info,
10069 NULL,
10070 0,
10071 &ping_trans_color);
10072
glennrp3b51f0e2010-11-27 18:14:08 +000010073 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010074 {
10075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010076 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010077 (int) ping_trans_color.red,
10078 (int) ping_trans_color.green,
10079 (int) ping_trans_color.blue);
10080 }
10081 }
glennrp991d11d2010-11-12 21:55:28 +000010082 }
10083
cristy3ed852e2009-09-05 21:47:34 +000010084 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010085 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010086
cristy3ed852e2009-09-05 21:47:34 +000010087 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010088
cristy3ed852e2009-09-05 21:47:34 +000010089 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010090 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010091
glennrp26f37912010-12-23 16:22:42 +000010092 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010093 {
glennrp4f25bd02011-01-01 18:51:28 +000010094 if ((image->page.width != 0 && image->page.width != image->columns) ||
10095 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010096 {
10097 unsigned char
10098 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010099
glennrp26f37912010-12-23 16:22:42 +000010100 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10101 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010102 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010103 PNGLong(chunk+4,(png_uint_32) image->page.width);
10104 PNGLong(chunk+8,(png_uint_32) image->page.height);
10105 chunk[12]=0; /* unit = pixels */
10106 (void) WriteBlob(image,13,chunk);
10107 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10108 }
cristy3ed852e2009-09-05 21:47:34 +000010109 }
10110
10111#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010112 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010113#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010114 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010115#undef PNG_HAVE_IDAT
10116#endif
10117
10118 png_set_packing(ping);
10119 /*
10120 Allocate memory.
10121 */
10122 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010123 if (image_depth > 8)
10124 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010125 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010126 {
glennrpb4a13412010-05-05 12:47:19 +000010127 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010128 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010129 break;
glennrp0fe50b42010-11-16 03:52:51 +000010130
glennrpb4a13412010-05-05 12:47:19 +000010131 case PNG_COLOR_TYPE_GRAY_ALPHA:
10132 rowbytes*=2;
10133 break;
glennrp0fe50b42010-11-16 03:52:51 +000010134
glennrpb4a13412010-05-05 12:47:19 +000010135 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010136 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010137 break;
glennrp0fe50b42010-11-16 03:52:51 +000010138
glennrpb4a13412010-05-05 12:47:19 +000010139 default:
10140 break;
cristy3ed852e2009-09-05 21:47:34 +000010141 }
glennrp3b51f0e2010-11-27 18:14:08 +000010142
10143 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010144 {
10145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10146 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010147
glennrpb4a13412010-05-05 12:47:19 +000010148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010149 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010150 }
glennrpcf002022011-01-30 02:38:15 +000010151 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10152 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010153
glennrpcf002022011-01-30 02:38:15 +000010154 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010155 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010156
cristy3ed852e2009-09-05 21:47:34 +000010157 /*
10158 Initialize image scanlines.
10159 */
glennrp5af765f2010-03-30 11:12:18 +000010160 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010161 {
10162 /*
10163 PNG write failed.
10164 */
10165#ifdef PNG_DEBUG
10166 if (image_info->verbose)
10167 (void) printf("PNG write has failed.\n");
10168#endif
10169 png_destroy_write_struct(&ping,&ping_info);
10170 if (quantum_info != (QuantumInfo *) NULL)
10171 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010172 if (ping_pixels != (unsigned char *) NULL)
10173 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010174#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010175 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010176#endif
glennrpda8f3a72011-02-27 23:54:12 +000010177 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010178 (void) CloseBlob(image);
10179 image_info=DestroyImageInfo(image_info);
10180 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010181 return(MagickFalse);
10182 }
cristyed552522009-10-16 14:04:35 +000010183 quantum_info=AcquireQuantumInfo(image_info,image);
10184 if (quantum_info == (QuantumInfo *) NULL)
10185 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010186 quantum_info->format=UndefinedQuantumFormat;
10187 quantum_info->depth=image_depth;
10188 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010189
cristy3ed852e2009-09-05 21:47:34 +000010190 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010191 !mng_info->write_png32) &&
10192 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010193 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010194 image_matte == MagickFalse &&
10195 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010196 {
glennrp8bb3a022010-12-13 20:40:04 +000010197 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010198 register const PixelPacket
10199 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010200
cristy3ed852e2009-09-05 21:47:34 +000010201 quantum_info->depth=8;
10202 for (pass=0; pass < num_passes; pass++)
10203 {
10204 /*
10205 Convert PseudoClass image to a PNG monochrome image.
10206 */
cristybb503372010-05-27 20:51:26 +000010207 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010208 {
glennrpd71e86a2011-02-24 01:28:37 +000010209 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10211 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010212
cristy3ed852e2009-09-05 21:47:34 +000010213 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010214
cristy3ed852e2009-09-05 21:47:34 +000010215 if (p == (const PixelPacket *) NULL)
10216 break;
glennrp0fe50b42010-11-16 03:52:51 +000010217
cristy3ed852e2009-09-05 21:47:34 +000010218 if (mng_info->IsPalette)
10219 {
10220 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010221 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010222 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10223 mng_info->write_png_depth &&
10224 mng_info->write_png_depth != old_bit_depth)
10225 {
10226 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010227 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010228 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010229 >> (8-old_bit_depth));
10230 }
10231 }
glennrp0fe50b42010-11-16 03:52:51 +000010232
cristy3ed852e2009-09-05 21:47:34 +000010233 else
10234 {
10235 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010236 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010237 }
glennrp0fe50b42010-11-16 03:52:51 +000010238
cristy3ed852e2009-09-05 21:47:34 +000010239 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010240 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010241 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010242 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010243
glennrp3b51f0e2010-11-27 18:14:08 +000010244 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10246 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010247
glennrpcf002022011-01-30 02:38:15 +000010248 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010249 }
10250 if (image->previous == (Image *) NULL)
10251 {
10252 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10253 if (status == MagickFalse)
10254 break;
10255 }
10256 }
10257 }
glennrp0fe50b42010-11-16 03:52:51 +000010258
glennrp8bb3a022010-12-13 20:40:04 +000010259 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010260 {
glennrp0fe50b42010-11-16 03:52:51 +000010261 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010262 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010263 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010264 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010265 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010266 {
glennrp8bb3a022010-12-13 20:40:04 +000010267 register const PixelPacket
10268 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010269
glennrp8bb3a022010-12-13 20:40:04 +000010270 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010271 {
glennrp8bb3a022010-12-13 20:40:04 +000010272
cristybb503372010-05-27 20:51:26 +000010273 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010274 {
10275 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010276
cristy3ed852e2009-09-05 21:47:34 +000010277 if (p == (const PixelPacket *) NULL)
10278 break;
glennrp2cc891a2010-12-24 13:44:32 +000010279
glennrp5af765f2010-03-30 11:12:18 +000010280 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010281 {
glennrp8bb3a022010-12-13 20:40:04 +000010282 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +000010283 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010284 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010285
glennrp8bb3a022010-12-13 20:40:04 +000010286 else
10287 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010288 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010289
glennrp3b51f0e2010-11-27 18:14:08 +000010290 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010292 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010293 }
glennrp2cc891a2010-12-24 13:44:32 +000010294
glennrp8bb3a022010-12-13 20:40:04 +000010295 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10296 {
10297 if (logging != MagickFalse && y == 0)
10298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10299 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010300
glennrp8bb3a022010-12-13 20:40:04 +000010301 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010302 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010303 }
glennrp2cc891a2010-12-24 13:44:32 +000010304
glennrp3b51f0e2010-11-27 18:14:08 +000010305 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010307 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010308
glennrpcf002022011-01-30 02:38:15 +000010309 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010310 }
glennrp2cc891a2010-12-24 13:44:32 +000010311
glennrp8bb3a022010-12-13 20:40:04 +000010312 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010313 {
glennrp8bb3a022010-12-13 20:40:04 +000010314 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10315 if (status == MagickFalse)
10316 break;
cristy3ed852e2009-09-05 21:47:34 +000010317 }
cristy3ed852e2009-09-05 21:47:34 +000010318 }
10319 }
glennrp8bb3a022010-12-13 20:40:04 +000010320
10321 else
10322 {
10323 register const PixelPacket
10324 *p;
10325
10326 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010327 {
glennrp8bb3a022010-12-13 20:40:04 +000010328 if ((image_depth > 8) || (mng_info->write_png24 ||
10329 mng_info->write_png32 ||
10330 (!mng_info->write_png8 && !mng_info->IsPalette)))
10331 {
10332 for (y=0; y < (ssize_t) image->rows; y++)
10333 {
10334 p=GetVirtualPixels(image,0,y,image->columns,1,
10335 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010336
glennrp8bb3a022010-12-13 20:40:04 +000010337 if (p == (const PixelPacket *) NULL)
10338 break;
glennrp2cc891a2010-12-24 13:44:32 +000010339
glennrp8bb3a022010-12-13 20:40:04 +000010340 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10341 {
10342 if (image->storage_class == DirectClass)
10343 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010344 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010345
glennrp8bb3a022010-12-13 20:40:04 +000010346 else
10347 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010348 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010349 }
glennrp2cc891a2010-12-24 13:44:32 +000010350
glennrp8bb3a022010-12-13 20:40:04 +000010351 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10352 {
10353 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010354 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +000010355 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010356
glennrp8bb3a022010-12-13 20:40:04 +000010357 if (logging != MagickFalse && y == 0)
10358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10359 " Writing GRAY_ALPHA PNG pixels (3)");
10360 }
glennrp2cc891a2010-12-24 13:44:32 +000010361
glennrp8bb3a022010-12-13 20:40:04 +000010362 else if (image_matte != MagickFalse)
10363 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010364 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010365
glennrp8bb3a022010-12-13 20:40:04 +000010366 else
10367 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010368 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010369
glennrp8bb3a022010-12-13 20:40:04 +000010370 if (logging != MagickFalse && y == 0)
10371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10372 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010373
glennrpcf002022011-01-30 02:38:15 +000010374 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010375 }
10376 }
glennrp2cc891a2010-12-24 13:44:32 +000010377
glennrp8bb3a022010-12-13 20:40:04 +000010378 else
10379 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10380 mng_info->write_png32 ||
10381 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10382 {
10383 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10384 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10385 {
10386 if (logging != MagickFalse)
10387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10388 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010389
glennrp8bb3a022010-12-13 20:40:04 +000010390 quantum_info->depth=8;
10391 image_depth=8;
10392 }
glennrp2cc891a2010-12-24 13:44:32 +000010393
glennrp8bb3a022010-12-13 20:40:04 +000010394 for (y=0; y < (ssize_t) image->rows; y++)
10395 {
10396 if (logging != MagickFalse && y == 0)
10397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10398 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010399
glennrp770d1932011-03-06 22:11:17 +000010400 p=GetVirtualPixels(image,0,y,image->columns,1,
10401 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010402
glennrp8bb3a022010-12-13 20:40:04 +000010403 if (p == (const PixelPacket *) NULL)
10404 break;
glennrp2cc891a2010-12-24 13:44:32 +000010405
glennrp8bb3a022010-12-13 20:40:04 +000010406 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010407 {
glennrp4bf89732011-03-21 13:48:28 +000010408 quantum_info->depth=image->depth;
10409
glennrp44757ab2011-03-17 12:57:03 +000010410 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010411 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +000010412 }
glennrp2cc891a2010-12-24 13:44:32 +000010413
glennrp8bb3a022010-12-13 20:40:04 +000010414 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10415 {
10416 if (logging != MagickFalse && y == 0)
10417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10418 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010419
glennrp8bb3a022010-12-13 20:40:04 +000010420 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010421 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +000010422 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010423 }
glennrp2cc891a2010-12-24 13:44:32 +000010424
glennrp8bb3a022010-12-13 20:40:04 +000010425 else
glennrp8bb3a022010-12-13 20:40:04 +000010426 {
glennrp179d0752011-03-17 13:02:10 +000010427 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +000010428 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10429
10430 if (logging != MagickFalse && y <= 2)
10431 {
10432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010433 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010434
10435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10436 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10437 (int)ping_pixels[0],(int)ping_pixels[1]);
10438 }
glennrp8bb3a022010-12-13 20:40:04 +000010439 }
glennrpcf002022011-01-30 02:38:15 +000010440 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010441 }
10442 }
glennrp2cc891a2010-12-24 13:44:32 +000010443
glennrp8bb3a022010-12-13 20:40:04 +000010444 if (image->previous == (Image *) NULL)
10445 {
10446 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10447 if (status == MagickFalse)
10448 break;
10449 }
cristy3ed852e2009-09-05 21:47:34 +000010450 }
glennrp8bb3a022010-12-13 20:40:04 +000010451 }
10452 }
10453
cristyb32b90a2009-09-07 21:45:48 +000010454 if (quantum_info != (QuantumInfo *) NULL)
10455 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010456
10457 if (logging != MagickFalse)
10458 {
10459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010460 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010461
cristy3ed852e2009-09-05 21:47:34 +000010462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010463 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010464
cristy3ed852e2009-09-05 21:47:34 +000010465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010466 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010467
cristy3ed852e2009-09-05 21:47:34 +000010468 if (mng_info->write_png_depth)
10469 {
10470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10471 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10472 }
glennrp0fe50b42010-11-16 03:52:51 +000010473
cristy3ed852e2009-09-05 21:47:34 +000010474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010475 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010476
cristy3ed852e2009-09-05 21:47:34 +000010477 if (mng_info->write_png_colortype)
10478 {
10479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10480 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10481 }
glennrp0fe50b42010-11-16 03:52:51 +000010482
cristy3ed852e2009-09-05 21:47:34 +000010483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010484 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010485
cristy3ed852e2009-09-05 21:47:34 +000010486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010487 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010488 }
10489 /*
glennrpa0ed0092011-04-18 16:36:29 +000010490 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010491 */
glennrp823b55c2011-03-14 18:46:46 +000010492 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010493 {
glennrp26f37912010-12-23 16:22:42 +000010494 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010495 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010496 while (property != (const char *) NULL)
10497 {
10498 png_textp
10499 text;
glennrp2cc891a2010-12-24 13:44:32 +000010500
glennrp26f37912010-12-23 16:22:42 +000010501 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +000010502
10503 /* Don't write any "png:" properties; those are just for "identify" */
10504 if (LocaleNCompare(property,"png:",4) != 0 &&
10505
10506 /* Suppress density and units if we wrote a pHYs chunk */
10507 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010508 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010509 LocaleCompare(property,"units") != 0) &&
10510
10511 /* Suppress the IM-generated Date:create and Date:modify */
10512 (ping_exclude_date == MagickFalse ||
10513 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010514 {
glennrpc70af4a2011-03-07 00:08:23 +000010515 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010516 {
glennrpc70af4a2011-03-07 00:08:23 +000010517 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10518 text[0].key=(char *) property;
10519 text[0].text=(char *) value;
10520 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010521
glennrpc70af4a2011-03-07 00:08:23 +000010522 if (ping_exclude_tEXt != MagickFalse)
10523 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10524
10525 else if (ping_exclude_zTXt != MagickFalse)
10526 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10527
10528 else
glennrp26f37912010-12-23 16:22:42 +000010529 {
glennrpc70af4a2011-03-07 00:08:23 +000010530 text[0].compression=image_info->compression == NoCompression ||
10531 (image_info->compression == UndefinedCompression &&
10532 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10533 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010534 }
glennrp2cc891a2010-12-24 13:44:32 +000010535
glennrpc70af4a2011-03-07 00:08:23 +000010536 if (logging != MagickFalse)
10537 {
10538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10539 " Setting up text chunk");
10540
10541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10542 " keyword: %s",text[0].key);
10543 }
10544
10545 png_set_text(ping,ping_info,text,1);
10546 png_free(ping,text);
10547 }
glennrp26f37912010-12-23 16:22:42 +000010548 }
10549 property=GetNextImageProperty(image);
10550 }
cristy3ed852e2009-09-05 21:47:34 +000010551 }
10552
10553 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010554 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010555
10556 if (logging != MagickFalse)
10557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10558 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010559
cristy3ed852e2009-09-05 21:47:34 +000010560 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010561
cristy3ed852e2009-09-05 21:47:34 +000010562 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10563 {
10564 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010565 (ping_width != mng_info->page.width) ||
10566 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010567 {
10568 unsigned char
10569 chunk[32];
10570
10571 /*
10572 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10573 */
10574 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10575 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010576 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010577 chunk[4]=4;
10578 chunk[5]=0; /* frame name separator (no name) */
10579 chunk[6]=1; /* flag for changing delay, for next frame only */
10580 chunk[7]=0; /* flag for changing frame timeout */
10581 chunk[8]=1; /* flag for changing frame clipping for next frame */
10582 chunk[9]=0; /* flag for changing frame sync_id */
10583 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10584 chunk[14]=0; /* clipping boundaries delta type */
10585 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10586 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010587 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010588 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10589 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010590 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010591 (void) WriteBlob(image,31,chunk);
10592 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10593 mng_info->old_framing_mode=4;
10594 mng_info->framing_mode=1;
10595 }
glennrp0fe50b42010-11-16 03:52:51 +000010596
cristy3ed852e2009-09-05 21:47:34 +000010597 else
10598 mng_info->framing_mode=3;
10599 }
10600 if (mng_info->write_mng && !mng_info->need_fram &&
10601 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +000010602 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010603 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010604 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010605
cristy3ed852e2009-09-05 21:47:34 +000010606 /*
10607 Free PNG resources.
10608 */
glennrp5af765f2010-03-30 11:12:18 +000010609
cristy3ed852e2009-09-05 21:47:34 +000010610 png_destroy_write_struct(&ping,&ping_info);
10611
glennrpcf002022011-01-30 02:38:15 +000010612 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010613
10614#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010615 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010616#endif
10617
glennrpda8f3a72011-02-27 23:54:12 +000010618 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010619 (void) CloseBlob(image);
10620
10621 image_info=DestroyImageInfo(image_info);
10622 image=DestroyImage(image);
10623
10624 /* Store bit depth actually written */
10625 s[0]=(char) ping_bit_depth;
10626 s[1]='\0';
10627
10628 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10629
cristy3ed852e2009-09-05 21:47:34 +000010630 if (logging != MagickFalse)
10631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10632 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010633
cristy3ed852e2009-09-05 21:47:34 +000010634 return(MagickTrue);
10635/* End write one PNG image */
10636}
10637
10638/*
10639%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10640% %
10641% %
10642% %
10643% W r i t e P N G I m a g e %
10644% %
10645% %
10646% %
10647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10648%
10649% WritePNGImage() writes a Portable Network Graphics (PNG) or
10650% Multiple-image Network Graphics (MNG) image file.
10651%
10652% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10653%
10654% The format of the WritePNGImage method is:
10655%
10656% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10657%
10658% A description of each parameter follows:
10659%
10660% o image_info: the image info.
10661%
10662% o image: The image.
10663%
10664% Returns MagickTrue on success, MagickFalse on failure.
10665%
10666% Communicating with the PNG encoder:
10667%
10668% While the datastream written is always in PNG format and normally would
10669% be given the "png" file extension, this method also writes the following
10670% pseudo-formats which are subsets of PNG:
10671%
glennrp5a39f372011-02-25 04:52:16 +000010672% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10673% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010674% is present, the tRNS chunk must only have values 0 and 255
10675% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010676% transparent). If other values are present they will be
10677% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010678% colors are present, they will be quantized to the 4-4-4-1,
10679% 3-3-3-1, or 3-3-2-1 palette.
10680%
10681% If you want better quantization or dithering of the colors
10682% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010683% PNG encoder. The pixels contain 8-bit indices even if
10684% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010685% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010686% PNG grayscale type might be slightly more efficient. Please
10687% note that writing to the PNG8 format may result in loss
10688% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010689%
10690% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10691% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010692% one of the colors as transparent. The only loss incurred
10693% is reduction of sample depth to 8. If the image has more
10694% than one transparent color, has semitransparent pixels, or
10695% has an opaque pixel with the same RGB components as the
10696% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010697%
10698% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10699% transparency is permitted, i.e., the alpha sample for
10700% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010701% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010702% The only loss in data is the reduction of the sample depth
10703% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010704%
10705% o -define: For more precise control of the PNG output, you can use the
10706% Image options "png:bit-depth" and "png:color-type". These
10707% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010708% from the application programming interfaces. The options
10709% are case-independent and are converted to lowercase before
10710% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010711%
10712% png:color-type can be 0, 2, 3, 4, or 6.
10713%
10714% When png:color-type is 0 (Grayscale), png:bit-depth can
10715% be 1, 2, 4, 8, or 16.
10716%
10717% When png:color-type is 2 (RGB), png:bit-depth can
10718% be 8 or 16.
10719%
10720% When png:color-type is 3 (Indexed), png:bit-depth can
10721% be 1, 2, 4, or 8. This refers to the number of bits
10722% used to store the index. The color samples always have
10723% bit-depth 8 in indexed PNG files.
10724%
10725% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10726% png:bit-depth can be 8 or 16.
10727%
glennrp5a39f372011-02-25 04:52:16 +000010728% If the image cannot be written without loss with the requested bit-depth
10729% and color-type, a PNG file will not be written, and the encoder will
10730% return MagickFalse.
10731%
cristy3ed852e2009-09-05 21:47:34 +000010732% Since image encoders should not be responsible for the "heavy lifting",
10733% the user should make sure that ImageMagick has already reduced the
10734% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010735% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010736% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010737%
cristy3ed852e2009-09-05 21:47:34 +000010738% Note that another definition, "png:bit-depth-written" exists, but it
10739% is not intended for external use. It is only used internally by the
10740% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10741%
10742% It is possible to request that the PNG encoder write previously-formatted
10743% ancillary chunks in the output PNG file, using the "-profile" commandline
10744% option as shown below or by setting the profile via a programming
10745% interface:
10746%
10747% -profile PNG-chunk-x:<file>
10748%
10749% where x is a location flag and <file> is a file containing the chunk
10750% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010751% This encoder will compute the chunk length and CRC, so those must not
10752% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010753%
10754% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10755% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10756% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010757% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010758%
glennrpbb8a7332010-11-13 15:17:35 +000010759% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010760%
glennrp3241bd02010-12-12 04:36:28 +000010761% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010762%
glennrpd6afd542010-11-19 01:53:05 +000010763% o 32-bit depth is reduced to 16.
10764% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10765% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010766% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010767% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010768% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010769% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10770% this can be done without loss and a larger bit depth N was not
10771% requested via the "-define PNG:bit-depth=N" option.
10772% o If matte channel is present but only one transparent color is
10773% present, RGB+tRNS is written instead of RGBA
10774% o Opaque matte channel is removed (or added, if color-type 4 or 6
10775% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010776%
cristy3ed852e2009-09-05 21:47:34 +000010777%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10778*/
10779static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10780 Image *image)
10781{
10782 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010783 excluding,
10784 logging,
10785 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010786 status;
10787
10788 MngInfo
10789 *mng_info;
10790
10791 const char
10792 *value;
10793
10794 int
glennrp21f0e622011-01-07 16:20:57 +000010795 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010796 source;
10797
cristy3ed852e2009-09-05 21:47:34 +000010798 /*
10799 Open image file.
10800 */
10801 assert(image_info != (const ImageInfo *) NULL);
10802 assert(image_info->signature == MagickSignature);
10803 assert(image != (Image *) NULL);
10804 assert(image->signature == MagickSignature);
10805 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010806 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010807 /*
10808 Allocate a MngInfo structure.
10809 */
10810 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010811 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010812
cristy3ed852e2009-09-05 21:47:34 +000010813 if (mng_info == (MngInfo *) NULL)
10814 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010815
cristy3ed852e2009-09-05 21:47:34 +000010816 /*
10817 Initialize members of the MngInfo structure.
10818 */
10819 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10820 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010821 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010822 have_mng_structure=MagickTrue;
10823
10824 /* See if user has requested a specific PNG subformat */
10825
10826 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10827 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10828 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10829
10830 if (mng_info->write_png8)
10831 {
glennrp9c1eb072010-06-06 22:19:15 +000010832 mng_info->write_png_colortype = /* 3 */ 4;
10833 mng_info->write_png_depth = 8;
10834 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010835 }
10836
10837 if (mng_info->write_png24)
10838 {
glennrp9c1eb072010-06-06 22:19:15 +000010839 mng_info->write_png_colortype = /* 2 */ 3;
10840 mng_info->write_png_depth = 8;
10841 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010842
glennrp9c1eb072010-06-06 22:19:15 +000010843 if (image->matte == MagickTrue)
10844 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010845
glennrp9c1eb072010-06-06 22:19:15 +000010846 else
10847 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010848
glennrp9c1eb072010-06-06 22:19:15 +000010849 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010850 }
10851
10852 if (mng_info->write_png32)
10853 {
glennrp9c1eb072010-06-06 22:19:15 +000010854 mng_info->write_png_colortype = /* 6 */ 7;
10855 mng_info->write_png_depth = 8;
10856 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010857
glennrp9c1eb072010-06-06 22:19:15 +000010858 if (image->matte == MagickTrue)
10859 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010860
glennrp9c1eb072010-06-06 22:19:15 +000010861 else
10862 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010863
glennrp9c1eb072010-06-06 22:19:15 +000010864 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010865 }
10866
10867 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010868
cristy3ed852e2009-09-05 21:47:34 +000010869 if (value != (char *) NULL)
10870 {
10871 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010872 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010873
cristy3ed852e2009-09-05 21:47:34 +000010874 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010875 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010876
cristy3ed852e2009-09-05 21:47:34 +000010877 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010878 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010879
cristy3ed852e2009-09-05 21:47:34 +000010880 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010881 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010882
cristy3ed852e2009-09-05 21:47:34 +000010883 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010884 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010885
glennrpbb8a7332010-11-13 15:17:35 +000010886 else
10887 (void) ThrowMagickException(&image->exception,
10888 GetMagickModule(),CoderWarning,
10889 "ignoring invalid defined png:bit-depth",
10890 "=%s",value);
10891
cristy3ed852e2009-09-05 21:47:34 +000010892 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010894 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010895 }
glennrp0fe50b42010-11-16 03:52:51 +000010896
cristy3ed852e2009-09-05 21:47:34 +000010897 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010898
cristy3ed852e2009-09-05 21:47:34 +000010899 if (value != (char *) NULL)
10900 {
10901 /* We must store colortype+1 because 0 is a valid colortype */
10902 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010903 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010904
cristy3ed852e2009-09-05 21:47:34 +000010905 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010906 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010907
cristy3ed852e2009-09-05 21:47:34 +000010908 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010909 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010910
cristy3ed852e2009-09-05 21:47:34 +000010911 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010912 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010913
cristy3ed852e2009-09-05 21:47:34 +000010914 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010915 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010916
glennrpbb8a7332010-11-13 15:17:35 +000010917 else
10918 (void) ThrowMagickException(&image->exception,
10919 GetMagickModule(),CoderWarning,
10920 "ignoring invalid defined png:color-type",
10921 "=%s",value);
10922
cristy3ed852e2009-09-05 21:47:34 +000010923 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010925 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010926 }
10927
glennrp0e8ea192010-12-24 18:00:33 +000010928 /* Check for chunks to be excluded:
10929 *
glennrp0dff56c2011-01-29 19:10:02 +000010930 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010931 * listed in the "unused_chunks" array, above.
10932 *
10933 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10934 * define (in the image properties or in the image artifacts)
10935 * or via a mng_info member. For convenience, in addition
10936 * to or instead of a comma-separated list of chunks, the
10937 * "exclude-chunk" string can be simply "all" or "none".
10938 *
10939 * The exclude-chunk define takes priority over the mng_info.
10940 *
10941 * A "PNG:include-chunk" define takes priority over both the
10942 * mng_info and the "PNG:exclude-chunk" define. Like the
10943 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010944 * well as a comma-separated list. Chunks that are unknown to
10945 * ImageMagick are always excluded, regardless of their "copy-safe"
10946 * status according to the PNG specification, and even if they
10947 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010948 *
10949 * Finally, all chunks listed in the "unused_chunks" array are
10950 * automatically excluded, regardless of the other instructions
10951 * or lack thereof.
10952 *
10953 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10954 * will not be written and the gAMA chunk will only be written if it
10955 * is not between .45 and .46, or approximately (1.0/2.2).
10956 *
10957 * If you exclude tRNS and the image has transparency, the colortype
10958 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10959 *
10960 * The -strip option causes StripImage() to set the png:include-chunk
10961 * artifact to "none,gama".
10962 */
10963
glennrp26f37912010-12-23 16:22:42 +000010964 mng_info->ping_exclude_bKGD=MagickFalse;
10965 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010966 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010967 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10968 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010969 mng_info->ping_exclude_iCCP=MagickFalse;
10970 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10971 mng_info->ping_exclude_oFFs=MagickFalse;
10972 mng_info->ping_exclude_pHYs=MagickFalse;
10973 mng_info->ping_exclude_sRGB=MagickFalse;
10974 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010975 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010976 mng_info->ping_exclude_vpAg=MagickFalse;
10977 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10978 mng_info->ping_exclude_zTXt=MagickFalse;
10979
glennrp8d3d6e52011-04-19 04:39:51 +000010980 mng_info->ping_preserve_colormap=MagickFalse;
10981
10982 value=GetImageArtifact(image,"png:preserve-colormap");
10983 if (value == NULL)
10984 value=GetImageOption(image_info,"png:preserve-colormap");
10985 if (value != NULL)
10986 mng_info->ping_preserve_colormap=MagickTrue;
10987
glennrp18682582011-06-30 18:11:47 +000010988 /* Thes compression-level, compression-strategy, and compression-filter
10989 * defines take precedence over values from the -quality option.
10990 */
10991 value=GetImageArtifact(image,"png:compression-level");
10992 if (value == NULL)
10993 value=GetImageOption(image_info,"png:compression-level");
10994 if (value != NULL)
10995 {
glennrp18682582011-06-30 18:11:47 +000010996 /* We have to add 1 to everything because 0 is a valid input,
10997 * and we want to use 0 (the default) to mean undefined.
10998 */
10999 if (LocaleCompare(value,"0") == 0)
11000 mng_info->write_png_compression_level = 1;
11001
11002 if (LocaleCompare(value,"1") == 0)
11003 mng_info->write_png_compression_level = 2;
11004
11005 else if (LocaleCompare(value,"2") == 0)
11006 mng_info->write_png_compression_level = 3;
11007
11008 else if (LocaleCompare(value,"3") == 0)
11009 mng_info->write_png_compression_level = 4;
11010
11011 else if (LocaleCompare(value,"4") == 0)
11012 mng_info->write_png_compression_level = 5;
11013
11014 else if (LocaleCompare(value,"5") == 0)
11015 mng_info->write_png_compression_level = 6;
11016
11017 else if (LocaleCompare(value,"6") == 0)
11018 mng_info->write_png_compression_level = 7;
11019
11020 else if (LocaleCompare(value,"7") == 0)
11021 mng_info->write_png_compression_level = 8;
11022
11023 else if (LocaleCompare(value,"8") == 0)
11024 mng_info->write_png_compression_level = 9;
11025
11026 else if (LocaleCompare(value,"9") == 0)
11027 mng_info->write_png_compression_level = 10;
11028
11029 else
11030 (void) ThrowMagickException(&image->exception,
11031 GetMagickModule(),CoderWarning,
11032 "ignoring invalid defined png:compression-level",
11033 "=%s",value);
11034 }
11035
11036 value=GetImageArtifact(image,"png:compression-strategy");
11037 if (value == NULL)
11038 value=GetImageOption(image_info,"png:compression-strategy");
11039 if (value != NULL)
11040 {
11041
11042 if (LocaleCompare(value,"0") == 0)
11043 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11044
11045 else if (LocaleCompare(value,"1") == 0)
11046 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11047
11048 else if (LocaleCompare(value,"2") == 0)
11049 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11050
11051 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011052#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011053 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011054#else
11055 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11056#endif
glennrp18682582011-06-30 18:11:47 +000011057
11058 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011059#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011060 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011061#else
11062 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11063#endif
glennrp18682582011-06-30 18:11:47 +000011064
11065 else
11066 (void) ThrowMagickException(&image->exception,
11067 GetMagickModule(),CoderWarning,
11068 "ignoring invalid defined png:compression-strategy",
11069 "=%s",value);
11070 }
11071
11072 value=GetImageArtifact(image,"png:compression-filter");
11073 if (value == NULL)
11074 value=GetImageOption(image_info,"png:compression-filter");
11075 if (value != NULL)
11076 {
11077
11078 /* To do: combinations of filters allowed by libpng
11079 * masks 0x08 through 0xf8
11080 *
11081 * Implement this as a comma-separated list of 0,1,2,3,4,5
11082 * where 5 is a special case meaning PNG_ALL_FILTERS.
11083 */
11084
11085 if (LocaleCompare(value,"0") == 0)
11086 mng_info->write_png_compression_filter = 1;
11087
11088 if (LocaleCompare(value,"1") == 0)
11089 mng_info->write_png_compression_filter = 2;
11090
11091 else if (LocaleCompare(value,"2") == 0)
11092 mng_info->write_png_compression_filter = 3;
11093
11094 else if (LocaleCompare(value,"3") == 0)
11095 mng_info->write_png_compression_filter = 4;
11096
11097 else if (LocaleCompare(value,"4") == 0)
11098 mng_info->write_png_compression_filter = 5;
11099
11100 else if (LocaleCompare(value,"5") == 0)
11101 mng_info->write_png_compression_filter = 6;
11102
glennrp54dc0692011-07-01 19:02:13 +000011103 else if (LocaleCompare(value,"6") == 0)
11104 mng_info->write_png_compression_filter = 7;
11105
11106 else if (LocaleCompare(value,"7") == 0)
11107 mng_info->write_png_compression_filter = 8;
11108
11109 else if (LocaleCompare(value,"8") == 0)
11110 mng_info->write_png_compression_filter = 9;
11111
11112 else if (LocaleCompare(value,"9") == 0)
11113 mng_info->write_png_compression_filter = 10;
glennrp18682582011-06-30 18:11:47 +000011114
11115 else
11116 (void) ThrowMagickException(&image->exception,
11117 GetMagickModule(),CoderWarning,
11118 "ignoring invalid defined png:compression-filter",
11119 "=%s",value);
11120 }
11121
glennrp03812ae2010-12-24 01:31:34 +000011122 excluding=MagickFalse;
11123
glennrp5c7cf4e2010-12-24 00:30:00 +000011124 for (source=0; source<1; source++)
11125 {
11126 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011127 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011128 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011129
11130 if (value == NULL)
11131 value=GetImageArtifact(image,"png:exclude-chunks");
11132 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011133 else
glennrpacba0042010-12-24 14:27:26 +000011134 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011135 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011136
glennrpacba0042010-12-24 14:27:26 +000011137 if (value == NULL)
11138 value=GetImageOption(image_info,"png:exclude-chunks");
11139 }
11140
glennrp03812ae2010-12-24 01:31:34 +000011141 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011142 {
glennrp03812ae2010-12-24 01:31:34 +000011143
11144 size_t
11145 last;
11146
11147 excluding=MagickTrue;
11148
11149 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011150 {
11151 if (source == 0)
11152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11153 " png:exclude-chunk=%s found in image artifacts.\n", value);
11154 else
11155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11156 " png:exclude-chunk=%s found in image properties.\n", value);
11157 }
glennrp03812ae2010-12-24 01:31:34 +000011158
11159 last=strlen(value);
11160
11161 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011162 {
glennrp03812ae2010-12-24 01:31:34 +000011163
11164 if (LocaleNCompare(value+i,"all",3) == 0)
11165 {
11166 mng_info->ping_exclude_bKGD=MagickTrue;
11167 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011168 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011169 mng_info->ping_exclude_EXIF=MagickTrue;
11170 mng_info->ping_exclude_gAMA=MagickTrue;
11171 mng_info->ping_exclude_iCCP=MagickTrue;
11172 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11173 mng_info->ping_exclude_oFFs=MagickTrue;
11174 mng_info->ping_exclude_pHYs=MagickTrue;
11175 mng_info->ping_exclude_sRGB=MagickTrue;
11176 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011177 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011178 mng_info->ping_exclude_vpAg=MagickTrue;
11179 mng_info->ping_exclude_zCCP=MagickTrue;
11180 mng_info->ping_exclude_zTXt=MagickTrue;
11181 i--;
11182 }
glennrp2cc891a2010-12-24 13:44:32 +000011183
glennrp03812ae2010-12-24 01:31:34 +000011184 if (LocaleNCompare(value+i,"none",4) == 0)
11185 {
11186 mng_info->ping_exclude_bKGD=MagickFalse;
11187 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011188 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011189 mng_info->ping_exclude_EXIF=MagickFalse;
11190 mng_info->ping_exclude_gAMA=MagickFalse;
11191 mng_info->ping_exclude_iCCP=MagickFalse;
11192 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11193 mng_info->ping_exclude_oFFs=MagickFalse;
11194 mng_info->ping_exclude_pHYs=MagickFalse;
11195 mng_info->ping_exclude_sRGB=MagickFalse;
11196 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011197 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011198 mng_info->ping_exclude_vpAg=MagickFalse;
11199 mng_info->ping_exclude_zCCP=MagickFalse;
11200 mng_info->ping_exclude_zTXt=MagickFalse;
11201 }
glennrp2cc891a2010-12-24 13:44:32 +000011202
glennrp03812ae2010-12-24 01:31:34 +000011203 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11204 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011205
glennrp03812ae2010-12-24 01:31:34 +000011206 if (LocaleNCompare(value+i,"chrm",4) == 0)
11207 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011208
glennrpa0ed0092011-04-18 16:36:29 +000011209 if (LocaleNCompare(value+i,"date",4) == 0)
11210 mng_info->ping_exclude_date=MagickTrue;
11211
glennrp03812ae2010-12-24 01:31:34 +000011212 if (LocaleNCompare(value+i,"exif",4) == 0)
11213 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011214
glennrp03812ae2010-12-24 01:31:34 +000011215 if (LocaleNCompare(value+i,"gama",4) == 0)
11216 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011217
glennrp03812ae2010-12-24 01:31:34 +000011218 if (LocaleNCompare(value+i,"iccp",4) == 0)
11219 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011220
glennrp03812ae2010-12-24 01:31:34 +000011221 /*
11222 if (LocaleNCompare(value+i,"itxt",4) == 0)
11223 mng_info->ping_exclude_iTXt=MagickTrue;
11224 */
glennrp2cc891a2010-12-24 13:44:32 +000011225
glennrp03812ae2010-12-24 01:31:34 +000011226 if (LocaleNCompare(value+i,"gama",4) == 0)
11227 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011228
glennrp03812ae2010-12-24 01:31:34 +000011229 if (LocaleNCompare(value+i,"offs",4) == 0)
11230 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011231
glennrp03812ae2010-12-24 01:31:34 +000011232 if (LocaleNCompare(value+i,"phys",4) == 0)
11233 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011234
glennrpa1e3b7b2010-12-24 16:37:33 +000011235 if (LocaleNCompare(value+i,"srgb",4) == 0)
11236 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011237
glennrp03812ae2010-12-24 01:31:34 +000011238 if (LocaleNCompare(value+i,"text",4) == 0)
11239 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011240
glennrpa1e3b7b2010-12-24 16:37:33 +000011241 if (LocaleNCompare(value+i,"trns",4) == 0)
11242 mng_info->ping_exclude_tRNS=MagickTrue;
11243
glennrp03812ae2010-12-24 01:31:34 +000011244 if (LocaleNCompare(value+i,"vpag",4) == 0)
11245 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011246
glennrp03812ae2010-12-24 01:31:34 +000011247 if (LocaleNCompare(value+i,"zccp",4) == 0)
11248 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011249
glennrp03812ae2010-12-24 01:31:34 +000011250 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11251 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011252
glennrp03812ae2010-12-24 01:31:34 +000011253 }
glennrpce91ed52010-12-23 22:37:49 +000011254 }
glennrp26f37912010-12-23 16:22:42 +000011255 }
11256
glennrp5c7cf4e2010-12-24 00:30:00 +000011257 for (source=0; source<1; source++)
11258 {
11259 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011260 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011261 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011262
11263 if (value == NULL)
11264 value=GetImageArtifact(image,"png:include-chunks");
11265 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011266 else
glennrpacba0042010-12-24 14:27:26 +000011267 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011268 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011269
glennrpacba0042010-12-24 14:27:26 +000011270 if (value == NULL)
11271 value=GetImageOption(image_info,"png:include-chunks");
11272 }
11273
glennrp03812ae2010-12-24 01:31:34 +000011274 if (value != NULL)
11275 {
11276 size_t
11277 last;
glennrp26f37912010-12-23 16:22:42 +000011278
glennrp03812ae2010-12-24 01:31:34 +000011279 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011280
glennrp03812ae2010-12-24 01:31:34 +000011281 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011282 {
11283 if (source == 0)
11284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11285 " png:include-chunk=%s found in image artifacts.\n", value);
11286 else
11287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11288 " png:include-chunk=%s found in image properties.\n", value);
11289 }
glennrp03812ae2010-12-24 01:31:34 +000011290
11291 last=strlen(value);
11292
11293 for (i=0; i<(int) last; i+=5)
11294 {
11295 if (LocaleNCompare(value+i,"all",3) == 0)
11296 {
11297 mng_info->ping_exclude_bKGD=MagickFalse;
11298 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011299 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011300 mng_info->ping_exclude_EXIF=MagickFalse;
11301 mng_info->ping_exclude_gAMA=MagickFalse;
11302 mng_info->ping_exclude_iCCP=MagickFalse;
11303 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11304 mng_info->ping_exclude_oFFs=MagickFalse;
11305 mng_info->ping_exclude_pHYs=MagickFalse;
11306 mng_info->ping_exclude_sRGB=MagickFalse;
11307 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011308 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011309 mng_info->ping_exclude_vpAg=MagickFalse;
11310 mng_info->ping_exclude_zCCP=MagickFalse;
11311 mng_info->ping_exclude_zTXt=MagickFalse;
11312 i--;
11313 }
glennrp2cc891a2010-12-24 13:44:32 +000011314
glennrp03812ae2010-12-24 01:31:34 +000011315 if (LocaleNCompare(value+i,"none",4) == 0)
11316 {
11317 mng_info->ping_exclude_bKGD=MagickTrue;
11318 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011319 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011320 mng_info->ping_exclude_EXIF=MagickTrue;
11321 mng_info->ping_exclude_gAMA=MagickTrue;
11322 mng_info->ping_exclude_iCCP=MagickTrue;
11323 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11324 mng_info->ping_exclude_oFFs=MagickTrue;
11325 mng_info->ping_exclude_pHYs=MagickTrue;
11326 mng_info->ping_exclude_sRGB=MagickTrue;
11327 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011328 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011329 mng_info->ping_exclude_vpAg=MagickTrue;
11330 mng_info->ping_exclude_zCCP=MagickTrue;
11331 mng_info->ping_exclude_zTXt=MagickTrue;
11332 }
glennrp2cc891a2010-12-24 13:44:32 +000011333
glennrp03812ae2010-12-24 01:31:34 +000011334 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11335 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011336
glennrp03812ae2010-12-24 01:31:34 +000011337 if (LocaleNCompare(value+i,"chrm",4) == 0)
11338 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011339
glennrpa0ed0092011-04-18 16:36:29 +000011340 if (LocaleNCompare(value+i,"date",4) == 0)
11341 mng_info->ping_exclude_date=MagickFalse;
11342
glennrp03812ae2010-12-24 01:31:34 +000011343 if (LocaleNCompare(value+i,"exif",4) == 0)
11344 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011345
glennrp03812ae2010-12-24 01:31:34 +000011346 if (LocaleNCompare(value+i,"gama",4) == 0)
11347 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011348
glennrp03812ae2010-12-24 01:31:34 +000011349 if (LocaleNCompare(value+i,"iccp",4) == 0)
11350 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011351
glennrp03812ae2010-12-24 01:31:34 +000011352 /*
11353 if (LocaleNCompare(value+i,"itxt",4) == 0)
11354 mng_info->ping_exclude_iTXt=MagickFalse;
11355 */
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=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011359
glennrp03812ae2010-12-24 01:31:34 +000011360 if (LocaleNCompare(value+i,"offs",4) == 0)
11361 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011362
glennrp03812ae2010-12-24 01:31:34 +000011363 if (LocaleNCompare(value+i,"phys",4) == 0)
11364 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011365
glennrpa1e3b7b2010-12-24 16:37:33 +000011366 if (LocaleNCompare(value+i,"srgb",4) == 0)
11367 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011368
glennrp03812ae2010-12-24 01:31:34 +000011369 if (LocaleNCompare(value+i,"text",4) == 0)
11370 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011371
glennrpa1e3b7b2010-12-24 16:37:33 +000011372 if (LocaleNCompare(value+i,"trns",4) == 0)
11373 mng_info->ping_exclude_tRNS=MagickFalse;
11374
glennrp03812ae2010-12-24 01:31:34 +000011375 if (LocaleNCompare(value+i,"vpag",4) == 0)
11376 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011377
glennrp03812ae2010-12-24 01:31:34 +000011378 if (LocaleNCompare(value+i,"zccp",4) == 0)
11379 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011380
glennrp03812ae2010-12-24 01:31:34 +000011381 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11382 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011383
glennrp03812ae2010-12-24 01:31:34 +000011384 }
glennrpce91ed52010-12-23 22:37:49 +000011385 }
glennrp26f37912010-12-23 16:22:42 +000011386 }
11387
glennrp03812ae2010-12-24 01:31:34 +000011388 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011389 {
11390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11391 " Chunks to be excluded from the output PNG:");
11392 if (mng_info->ping_exclude_bKGD != MagickFalse)
11393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11394 " bKGD");
11395 if (mng_info->ping_exclude_cHRM != MagickFalse)
11396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11397 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011398 if (mng_info->ping_exclude_date != MagickFalse)
11399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11400 " date");
glennrp26f37912010-12-23 16:22:42 +000011401 if (mng_info->ping_exclude_EXIF != MagickFalse)
11402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11403 " EXIF");
11404 if (mng_info->ping_exclude_gAMA != MagickFalse)
11405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11406 " gAMA");
11407 if (mng_info->ping_exclude_iCCP != MagickFalse)
11408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11409 " iCCP");
11410/*
11411 if (mng_info->ping_exclude_iTXt != MagickFalse)
11412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11413 " iTXt");
11414*/
11415 if (mng_info->ping_exclude_oFFs != MagickFalse)
11416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11417 " oFFs");
11418 if (mng_info->ping_exclude_pHYs != MagickFalse)
11419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11420 " pHYs");
11421 if (mng_info->ping_exclude_sRGB != MagickFalse)
11422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11423 " sRGB");
11424 if (mng_info->ping_exclude_tEXt != MagickFalse)
11425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11426 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011427 if (mng_info->ping_exclude_tRNS != MagickFalse)
11428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11429 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011430 if (mng_info->ping_exclude_vpAg != MagickFalse)
11431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11432 " vpAg");
11433 if (mng_info->ping_exclude_zCCP != MagickFalse)
11434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11435 " zCCP");
11436 if (mng_info->ping_exclude_zTXt != MagickFalse)
11437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11438 " zTXt");
11439 }
11440
glennrpb9cfe272010-12-21 15:08:06 +000011441 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011442
glennrpb9cfe272010-12-21 15:08:06 +000011443 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000011444
11445 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011446
cristy3ed852e2009-09-05 21:47:34 +000011447 if (logging != MagickFalse)
11448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011449
cristy3ed852e2009-09-05 21:47:34 +000011450 return(status);
11451}
11452
11453#if defined(JNG_SUPPORTED)
11454
11455/* Write one JNG image */
11456static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11457 const ImageInfo *image_info,Image *image)
11458{
11459 Image
11460 *jpeg_image;
11461
11462 ImageInfo
11463 *jpeg_image_info;
11464
11465 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011466 logging,
cristy3ed852e2009-09-05 21:47:34 +000011467 status;
11468
11469 size_t
11470 length;
11471
11472 unsigned char
11473 *blob,
11474 chunk[80],
11475 *p;
11476
11477 unsigned int
11478 jng_alpha_compression_method,
11479 jng_alpha_sample_depth,
11480 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011481 transparent;
11482
cristybb503372010-05-27 20:51:26 +000011483 size_t
cristy3ed852e2009-09-05 21:47:34 +000011484 jng_quality;
11485
11486 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011487 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011488
11489 blob=(unsigned char *) NULL;
11490 jpeg_image=(Image *) NULL;
11491 jpeg_image_info=(ImageInfo *) NULL;
11492
11493 status=MagickTrue;
11494 transparent=image_info->type==GrayscaleMatteType ||
11495 image_info->type==TrueColorMatteType;
11496 jng_color_type=10;
11497 jng_alpha_sample_depth=0;
11498 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11499 jng_alpha_compression_method=0;
11500
11501 if (image->matte != MagickFalse)
11502 {
11503 /* if any pixels are transparent */
11504 transparent=MagickTrue;
11505 if (image_info->compression==JPEGCompression)
11506 jng_alpha_compression_method=8;
11507 }
11508
11509 if (transparent)
11510 {
11511 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011512
cristy3ed852e2009-09-05 21:47:34 +000011513 /* Create JPEG blob, image, and image_info */
11514 if (logging != MagickFalse)
11515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11516 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000011517
cristy3ed852e2009-09-05 21:47:34 +000011518 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011519
cristy3ed852e2009-09-05 21:47:34 +000011520 if (jpeg_image_info == (ImageInfo *) NULL)
11521 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011522
cristy3ed852e2009-09-05 21:47:34 +000011523 if (logging != MagickFalse)
11524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11525 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011526
cristy3ed852e2009-09-05 21:47:34 +000011527 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011528
cristy3ed852e2009-09-05 21:47:34 +000011529 if (jpeg_image == (Image *) NULL)
11530 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011531
cristy3ed852e2009-09-05 21:47:34 +000011532 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11533 status=SeparateImageChannel(jpeg_image,OpacityChannel);
11534 status=NegateImage(jpeg_image,MagickFalse);
11535 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011536
cristy3ed852e2009-09-05 21:47:34 +000011537 if (jng_quality >= 1000)
11538 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011539
cristy3ed852e2009-09-05 21:47:34 +000011540 else
11541 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011542
cristy3ed852e2009-09-05 21:47:34 +000011543 jpeg_image_info->type=GrayscaleType;
11544 (void) SetImageType(jpeg_image,GrayscaleType);
11545 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011546 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011547 "%s",jpeg_image->filename);
11548 }
11549
11550 /* To do: check bit depth of PNG alpha channel */
11551
11552 /* Check if image is grayscale. */
11553 if (image_info->type != TrueColorMatteType && image_info->type !=
11554 TrueColorType && ImageIsGray(image))
11555 jng_color_type-=2;
11556
11557 if (transparent)
11558 {
11559 if (jng_alpha_compression_method==0)
11560 {
11561 const char
11562 *value;
11563
11564 /* Encode opacity as a grayscale PNG blob */
11565 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11566 &image->exception);
11567 if (logging != MagickFalse)
11568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11569 " Creating PNG blob.");
11570 length=0;
11571
11572 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11573 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11574 jpeg_image_info->interlace=NoInterlace;
11575
11576 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11577 &image->exception);
11578
11579 /* Retrieve sample depth used */
11580 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11581 if (value != (char *) NULL)
11582 jng_alpha_sample_depth= (unsigned int) value[0];
11583 }
11584 else
11585 {
11586 /* Encode opacity as a grayscale JPEG blob */
11587
11588 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11589 &image->exception);
11590
11591 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11592 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11593 jpeg_image_info->interlace=NoInterlace;
11594 if (logging != MagickFalse)
11595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11596 " Creating blob.");
11597 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000011598 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000011599 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011600
cristy3ed852e2009-09-05 21:47:34 +000011601 if (logging != MagickFalse)
11602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011603 " Successfully read jpeg_image into a blob, length=%.20g.",
11604 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011605
11606 }
11607 /* Destroy JPEG image and image_info */
11608 jpeg_image=DestroyImage(jpeg_image);
11609 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11610 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11611 }
11612
11613 /* Write JHDR chunk */
11614 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11615 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011616 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011617 PNGLong(chunk+4,(png_uint_32) image->columns);
11618 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011619 chunk[12]=jng_color_type;
11620 chunk[13]=8; /* sample depth */
11621 chunk[14]=8; /*jng_image_compression_method */
11622 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11623 chunk[16]=jng_alpha_sample_depth;
11624 chunk[17]=jng_alpha_compression_method;
11625 chunk[18]=0; /*jng_alpha_filter_method */
11626 chunk[19]=0; /*jng_alpha_interlace_method */
11627 (void) WriteBlob(image,20,chunk);
11628 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11629 if (logging != MagickFalse)
11630 {
11631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011632 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011633
cristy3ed852e2009-09-05 21:47:34 +000011634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011635 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011636
cristy3ed852e2009-09-05 21:47:34 +000011637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11638 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011639
cristy3ed852e2009-09-05 21:47:34 +000011640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11641 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011642
cristy3ed852e2009-09-05 21:47:34 +000011643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11644 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011645
cristy3ed852e2009-09-05 21:47:34 +000011646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11647 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011648
cristy3ed852e2009-09-05 21:47:34 +000011649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11650 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011651
cristy3ed852e2009-09-05 21:47:34 +000011652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11653 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011654
cristy3ed852e2009-09-05 21:47:34 +000011655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11656 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011657
cristy3ed852e2009-09-05 21:47:34 +000011658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11659 " JNG alpha interlace:%5d",0);
11660 }
11661
glennrp0fe50b42010-11-16 03:52:51 +000011662 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011663 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011664
11665 /*
11666 Write leading ancillary chunks
11667 */
11668
11669 if (transparent)
11670 {
11671 /*
11672 Write JNG bKGD chunk
11673 */
11674
11675 unsigned char
11676 blue,
11677 green,
11678 red;
11679
cristybb503372010-05-27 20:51:26 +000011680 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011681 num_bytes;
11682
11683 if (jng_color_type == 8 || jng_color_type == 12)
11684 num_bytes=6L;
11685 else
11686 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011687 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011688 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011689 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011690 red=ScaleQuantumToChar(image->background_color.red);
11691 green=ScaleQuantumToChar(image->background_color.green);
11692 blue=ScaleQuantumToChar(image->background_color.blue);
11693 *(chunk+4)=0;
11694 *(chunk+5)=red;
11695 *(chunk+6)=0;
11696 *(chunk+7)=green;
11697 *(chunk+8)=0;
11698 *(chunk+9)=blue;
11699 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11700 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11701 }
11702
11703 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11704 {
11705 /*
11706 Write JNG sRGB chunk
11707 */
11708 (void) WriteBlobMSBULong(image,1L);
11709 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011710 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011711
cristy3ed852e2009-09-05 21:47:34 +000011712 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011713 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011714 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011715 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011716
cristy3ed852e2009-09-05 21:47:34 +000011717 else
glennrpe610a072010-08-05 17:08:46 +000011718 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011719 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011720 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011721
cristy3ed852e2009-09-05 21:47:34 +000011722 (void) WriteBlob(image,5,chunk);
11723 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11724 }
11725 else
11726 {
11727 if (image->gamma != 0.0)
11728 {
11729 /*
11730 Write JNG gAMA chunk
11731 */
11732 (void) WriteBlobMSBULong(image,4L);
11733 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011734 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011735 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011736 (void) WriteBlob(image,8,chunk);
11737 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11738 }
glennrp0fe50b42010-11-16 03:52:51 +000011739
cristy3ed852e2009-09-05 21:47:34 +000011740 if ((mng_info->equal_chrms == MagickFalse) &&
11741 (image->chromaticity.red_primary.x != 0.0))
11742 {
11743 PrimaryInfo
11744 primary;
11745
11746 /*
11747 Write JNG cHRM chunk
11748 */
11749 (void) WriteBlobMSBULong(image,32L);
11750 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011751 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011752 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011753 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11754 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011755 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011756 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11757 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011758 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011759 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11760 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011761 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011762 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11763 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011764 (void) WriteBlob(image,36,chunk);
11765 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11766 }
11767 }
glennrp0fe50b42010-11-16 03:52:51 +000011768
cristy3ed852e2009-09-05 21:47:34 +000011769 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11770 {
11771 /*
11772 Write JNG pHYs chunk
11773 */
11774 (void) WriteBlobMSBULong(image,9L);
11775 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011776 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011777 if (image->units == PixelsPerInchResolution)
11778 {
cristy35ef8242010-06-03 16:24:13 +000011779 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011780 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011781
cristy35ef8242010-06-03 16:24:13 +000011782 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011783 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011784
cristy3ed852e2009-09-05 21:47:34 +000011785 chunk[12]=1;
11786 }
glennrp0fe50b42010-11-16 03:52:51 +000011787
cristy3ed852e2009-09-05 21:47:34 +000011788 else
11789 {
11790 if (image->units == PixelsPerCentimeterResolution)
11791 {
cristy35ef8242010-06-03 16:24:13 +000011792 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011793 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011794
cristy35ef8242010-06-03 16:24:13 +000011795 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011796 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011797
cristy3ed852e2009-09-05 21:47:34 +000011798 chunk[12]=1;
11799 }
glennrp0fe50b42010-11-16 03:52:51 +000011800
cristy3ed852e2009-09-05 21:47:34 +000011801 else
11802 {
cristy35ef8242010-06-03 16:24:13 +000011803 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11804 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011805 chunk[12]=0;
11806 }
11807 }
11808 (void) WriteBlob(image,13,chunk);
11809 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11810 }
11811
11812 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11813 {
11814 /*
11815 Write JNG oFFs chunk
11816 */
11817 (void) WriteBlobMSBULong(image,9L);
11818 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011819 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011820 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11821 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011822 chunk[12]=0;
11823 (void) WriteBlob(image,13,chunk);
11824 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11825 }
11826 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11827 {
11828 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11829 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011830 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011831 PNGLong(chunk+4,(png_uint_32) image->page.width);
11832 PNGLong(chunk+8,(png_uint_32) image->page.height);
11833 chunk[12]=0; /* unit = pixels */
11834 (void) WriteBlob(image,13,chunk);
11835 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11836 }
11837
11838
11839 if (transparent)
11840 {
11841 if (jng_alpha_compression_method==0)
11842 {
cristybb503372010-05-27 20:51:26 +000011843 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011844 i;
11845
cristybb503372010-05-27 20:51:26 +000011846 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011847 len;
11848
11849 /* Write IDAT chunk header */
11850 if (logging != MagickFalse)
11851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011852 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011853 length);
cristy3ed852e2009-09-05 21:47:34 +000011854
11855 /* Copy IDAT chunks */
11856 len=0;
11857 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011858 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011859 {
11860 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11861 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011862
cristy3ed852e2009-09-05 21:47:34 +000011863 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11864 {
11865 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011866 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011867 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011868 (void) WriteBlob(image,(size_t) len+4,p);
11869 (void) WriteBlobMSBULong(image,
11870 crc32(0,p,(uInt) len+4));
11871 }
glennrp0fe50b42010-11-16 03:52:51 +000011872
cristy3ed852e2009-09-05 21:47:34 +000011873 else
11874 {
11875 if (logging != MagickFalse)
11876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011877 " Skipping %c%c%c%c chunk, length=%.20g.",
11878 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011879 }
11880 p+=(8+len);
11881 }
11882 }
11883 else
11884 {
11885 /* Write JDAA chunk header */
11886 if (logging != MagickFalse)
11887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011888 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011889 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011890 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011891 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011892 /* Write JDAT chunk(s) data */
11893 (void) WriteBlob(image,4,chunk);
11894 (void) WriteBlob(image,length,blob);
11895 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11896 (uInt) length));
11897 }
11898 blob=(unsigned char *) RelinquishMagickMemory(blob);
11899 }
11900
11901 /* Encode image as a JPEG blob */
11902 if (logging != MagickFalse)
11903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11904 " Creating jpeg_image_info.");
11905 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11906 if (jpeg_image_info == (ImageInfo *) NULL)
11907 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11908
11909 if (logging != MagickFalse)
11910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11911 " Creating jpeg_image.");
11912
11913 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11914 if (jpeg_image == (Image *) NULL)
11915 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11916 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11917
11918 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011919 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000011920 jpeg_image->filename);
11921
11922 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11923 &image->exception);
11924
11925 if (logging != MagickFalse)
11926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011927 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11928 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011929
11930 if (jng_color_type == 8 || jng_color_type == 12)
11931 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011932
cristy3ed852e2009-09-05 21:47:34 +000011933 jpeg_image_info->quality=jng_quality % 1000;
11934 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11935 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011936
cristy3ed852e2009-09-05 21:47:34 +000011937 if (logging != MagickFalse)
11938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11939 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011940
cristy3ed852e2009-09-05 21:47:34 +000011941 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011942
cristy3ed852e2009-09-05 21:47:34 +000011943 if (logging != MagickFalse)
11944 {
11945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011946 " Successfully read jpeg_image into a blob, length=%.20g.",
11947 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011948
11949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011950 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011951 }
glennrp0fe50b42010-11-16 03:52:51 +000011952
cristy3ed852e2009-09-05 21:47:34 +000011953 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011954 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011955 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011956 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011957 (void) WriteBlob(image,4,chunk);
11958 (void) WriteBlob(image,length,blob);
11959 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11960
11961 jpeg_image=DestroyImage(jpeg_image);
11962 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11963 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11964 blob=(unsigned char *) RelinquishMagickMemory(blob);
11965
11966 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011967 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011968
11969 /* Write IEND chunk */
11970 (void) WriteBlobMSBULong(image,0L);
11971 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011972 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011973 (void) WriteBlob(image,4,chunk);
11974 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11975
11976 if (logging != MagickFalse)
11977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11978 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011979
cristy3ed852e2009-09-05 21:47:34 +000011980 return(status);
11981}
11982
11983
11984/*
11985%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11986% %
11987% %
11988% %
11989% W r i t e J N G I m a g e %
11990% %
11991% %
11992% %
11993%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11994%
11995% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11996%
11997% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11998%
11999% The format of the WriteJNGImage method is:
12000%
12001% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
12002%
12003% A description of each parameter follows:
12004%
12005% o image_info: the image info.
12006%
12007% o image: The image.
12008%
12009%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12010*/
12011static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
12012{
12013 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012014 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012015 logging,
cristy3ed852e2009-09-05 21:47:34 +000012016 status;
12017
12018 MngInfo
12019 *mng_info;
12020
cristy3ed852e2009-09-05 21:47:34 +000012021 /*
12022 Open image file.
12023 */
12024 assert(image_info != (const ImageInfo *) NULL);
12025 assert(image_info->signature == MagickSignature);
12026 assert(image != (Image *) NULL);
12027 assert(image->signature == MagickSignature);
12028 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012029 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012030 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12031 if (status == MagickFalse)
12032 return(status);
12033
12034 /*
12035 Allocate a MngInfo structure.
12036 */
12037 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012038 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012039 if (mng_info == (MngInfo *) NULL)
12040 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12041 /*
12042 Initialize members of the MngInfo structure.
12043 */
12044 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12045 mng_info->image=image;
12046 have_mng_structure=MagickTrue;
12047
12048 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12049
12050 status=WriteOneJNGImage(mng_info,image_info,image);
12051 (void) CloseBlob(image);
12052
12053 (void) CatchImageException(image);
12054 MngInfoFreeStruct(mng_info,&have_mng_structure);
12055 if (logging != MagickFalse)
12056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12057 return(status);
12058}
12059#endif
12060
12061
12062
12063static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12064{
12065 const char
12066 *option;
12067
12068 Image
12069 *next_image;
12070
12071 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012072 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012073 status;
12074
glennrp03812ae2010-12-24 01:31:34 +000012075 volatile MagickBooleanType
12076 logging;
12077
cristy3ed852e2009-09-05 21:47:34 +000012078 MngInfo
12079 *mng_info;
12080
12081 int
cristy3ed852e2009-09-05 21:47:34 +000012082 image_count,
12083 need_iterations,
12084 need_matte;
12085
12086 volatile int
12087#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12088 defined(PNG_MNG_FEATURES_SUPPORTED)
12089 need_local_plte,
12090#endif
12091 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012092 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012093 use_global_plte;
12094
cristybb503372010-05-27 20:51:26 +000012095 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012096 i;
12097
12098 unsigned char
12099 chunk[800];
12100
12101 volatile unsigned int
12102 write_jng,
12103 write_mng;
12104
cristybb503372010-05-27 20:51:26 +000012105 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012106 scene;
12107
cristybb503372010-05-27 20:51:26 +000012108 size_t
cristy3ed852e2009-09-05 21:47:34 +000012109 final_delay=0,
12110 initial_delay;
12111
glennrpd5045b42010-03-24 12:40:35 +000012112#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012113 if (image_info->verbose)
12114 printf("Your PNG library (libpng-%s) is rather old.\n",
12115 PNG_LIBPNG_VER_STRING);
12116#endif
12117
12118 /*
12119 Open image file.
12120 */
12121 assert(image_info != (const ImageInfo *) NULL);
12122 assert(image_info->signature == MagickSignature);
12123 assert(image != (Image *) NULL);
12124 assert(image->signature == MagickSignature);
12125 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012126 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012127 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12128 if (status == MagickFalse)
12129 return(status);
12130
12131 /*
12132 Allocate a MngInfo structure.
12133 */
12134 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012135 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012136 if (mng_info == (MngInfo *) NULL)
12137 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12138 /*
12139 Initialize members of the MngInfo structure.
12140 */
12141 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12142 mng_info->image=image;
12143 have_mng_structure=MagickTrue;
12144 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12145
12146 /*
12147 * See if user has requested a specific PNG subformat to be used
12148 * for all of the PNGs in the MNG being written, e.g.,
12149 *
12150 * convert *.png png8:animation.mng
12151 *
12152 * To do: check -define png:bit_depth and png:color_type as well,
12153 * or perhaps use mng:bit_depth and mng:color_type instead for
12154 * global settings.
12155 */
12156
12157 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12158 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12159 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12160
12161 write_jng=MagickFalse;
12162 if (image_info->compression == JPEGCompression)
12163 write_jng=MagickTrue;
12164
12165 mng_info->adjoin=image_info->adjoin &&
12166 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12167
cristy3ed852e2009-09-05 21:47:34 +000012168 if (logging != MagickFalse)
12169 {
12170 /* Log some info about the input */
12171 Image
12172 *p;
12173
12174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12175 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012176
cristy3ed852e2009-09-05 21:47:34 +000012177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012178 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012179
cristy3ed852e2009-09-05 21:47:34 +000012180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12181 " Type: %d",image_info->type);
12182
12183 scene=0;
12184 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12185 {
12186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012187 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012188
cristy3ed852e2009-09-05 21:47:34 +000012189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012190 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012191
cristy3ed852e2009-09-05 21:47:34 +000012192 if (p->matte)
12193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12194 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012195
cristy3ed852e2009-09-05 21:47:34 +000012196 else
12197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12198 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012199
cristy3ed852e2009-09-05 21:47:34 +000012200 if (p->storage_class == PseudoClass)
12201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12202 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012203
cristy3ed852e2009-09-05 21:47:34 +000012204 else
12205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12206 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012207
cristy3ed852e2009-09-05 21:47:34 +000012208 if (p->colors)
12209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012210 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012211
cristy3ed852e2009-09-05 21:47:34 +000012212 else
12213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12214 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012215
cristy3ed852e2009-09-05 21:47:34 +000012216 if (mng_info->adjoin == MagickFalse)
12217 break;
12218 }
12219 }
12220
cristy3ed852e2009-09-05 21:47:34 +000012221 use_global_plte=MagickFalse;
12222 all_images_are_gray=MagickFalse;
12223#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12224 need_local_plte=MagickTrue;
12225#endif
12226 need_defi=MagickFalse;
12227 need_matte=MagickFalse;
12228 mng_info->framing_mode=1;
12229 mng_info->old_framing_mode=1;
12230
12231 if (write_mng)
12232 if (image_info->page != (char *) NULL)
12233 {
12234 /*
12235 Determine image bounding box.
12236 */
12237 SetGeometry(image,&mng_info->page);
12238 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12239 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12240 }
12241 if (write_mng)
12242 {
12243 unsigned int
12244 need_geom;
12245
12246 unsigned short
12247 red,
12248 green,
12249 blue;
12250
12251 mng_info->page=image->page;
12252 need_geom=MagickTrue;
12253 if (mng_info->page.width || mng_info->page.height)
12254 need_geom=MagickFalse;
12255 /*
12256 Check all the scenes.
12257 */
12258 initial_delay=image->delay;
12259 need_iterations=MagickFalse;
12260 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12261 mng_info->equal_physs=MagickTrue,
12262 mng_info->equal_gammas=MagickTrue;
12263 mng_info->equal_srgbs=MagickTrue;
12264 mng_info->equal_backgrounds=MagickTrue;
12265 image_count=0;
12266#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12267 defined(PNG_MNG_FEATURES_SUPPORTED)
12268 all_images_are_gray=MagickTrue;
12269 mng_info->equal_palettes=MagickFalse;
12270 need_local_plte=MagickFalse;
12271#endif
12272 for (next_image=image; next_image != (Image *) NULL; )
12273 {
12274 if (need_geom)
12275 {
12276 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12277 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012278
cristy3ed852e2009-09-05 21:47:34 +000012279 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12280 mng_info->page.height=next_image->rows+next_image->page.y;
12281 }
glennrp0fe50b42010-11-16 03:52:51 +000012282
cristy3ed852e2009-09-05 21:47:34 +000012283 if (next_image->page.x || next_image->page.y)
12284 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012285
cristy3ed852e2009-09-05 21:47:34 +000012286 if (next_image->matte)
12287 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012288
cristy3ed852e2009-09-05 21:47:34 +000012289 if ((int) next_image->dispose >= BackgroundDispose)
12290 if (next_image->matte || next_image->page.x || next_image->page.y ||
12291 ((next_image->columns < mng_info->page.width) &&
12292 (next_image->rows < mng_info->page.height)))
12293 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012294
cristy3ed852e2009-09-05 21:47:34 +000012295 if (next_image->iterations)
12296 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012297
cristy3ed852e2009-09-05 21:47:34 +000012298 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012299
cristy3ed852e2009-09-05 21:47:34 +000012300 if (final_delay != initial_delay || final_delay > 1UL*
12301 next_image->ticks_per_second)
12302 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012303
cristy3ed852e2009-09-05 21:47:34 +000012304#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12305 defined(PNG_MNG_FEATURES_SUPPORTED)
12306 /*
12307 check for global palette possibility.
12308 */
12309 if (image->matte != MagickFalse)
12310 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012311
cristy3ed852e2009-09-05 21:47:34 +000012312 if (need_local_plte == 0)
12313 {
12314 if (ImageIsGray(image) == MagickFalse)
12315 all_images_are_gray=MagickFalse;
12316 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12317 if (use_global_plte == 0)
12318 use_global_plte=mng_info->equal_palettes;
12319 need_local_plte=!mng_info->equal_palettes;
12320 }
12321#endif
12322 if (GetNextImageInList(next_image) != (Image *) NULL)
12323 {
12324 if (next_image->background_color.red !=
12325 next_image->next->background_color.red ||
12326 next_image->background_color.green !=
12327 next_image->next->background_color.green ||
12328 next_image->background_color.blue !=
12329 next_image->next->background_color.blue)
12330 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012331
cristy3ed852e2009-09-05 21:47:34 +000012332 if (next_image->gamma != next_image->next->gamma)
12333 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012334
cristy3ed852e2009-09-05 21:47:34 +000012335 if (next_image->rendering_intent !=
12336 next_image->next->rendering_intent)
12337 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012338
cristy3ed852e2009-09-05 21:47:34 +000012339 if ((next_image->units != next_image->next->units) ||
12340 (next_image->x_resolution != next_image->next->x_resolution) ||
12341 (next_image->y_resolution != next_image->next->y_resolution))
12342 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012343
cristy3ed852e2009-09-05 21:47:34 +000012344 if (mng_info->equal_chrms)
12345 {
12346 if (next_image->chromaticity.red_primary.x !=
12347 next_image->next->chromaticity.red_primary.x ||
12348 next_image->chromaticity.red_primary.y !=
12349 next_image->next->chromaticity.red_primary.y ||
12350 next_image->chromaticity.green_primary.x !=
12351 next_image->next->chromaticity.green_primary.x ||
12352 next_image->chromaticity.green_primary.y !=
12353 next_image->next->chromaticity.green_primary.y ||
12354 next_image->chromaticity.blue_primary.x !=
12355 next_image->next->chromaticity.blue_primary.x ||
12356 next_image->chromaticity.blue_primary.y !=
12357 next_image->next->chromaticity.blue_primary.y ||
12358 next_image->chromaticity.white_point.x !=
12359 next_image->next->chromaticity.white_point.x ||
12360 next_image->chromaticity.white_point.y !=
12361 next_image->next->chromaticity.white_point.y)
12362 mng_info->equal_chrms=MagickFalse;
12363 }
12364 }
12365 image_count++;
12366 next_image=GetNextImageInList(next_image);
12367 }
12368 if (image_count < 2)
12369 {
12370 mng_info->equal_backgrounds=MagickFalse;
12371 mng_info->equal_chrms=MagickFalse;
12372 mng_info->equal_gammas=MagickFalse;
12373 mng_info->equal_srgbs=MagickFalse;
12374 mng_info->equal_physs=MagickFalse;
12375 use_global_plte=MagickFalse;
12376#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12377 need_local_plte=MagickTrue;
12378#endif
12379 need_iterations=MagickFalse;
12380 }
glennrp0fe50b42010-11-16 03:52:51 +000012381
cristy3ed852e2009-09-05 21:47:34 +000012382 if (mng_info->need_fram == MagickFalse)
12383 {
12384 /*
12385 Only certain framing rates 100/n are exactly representable without
12386 the FRAM chunk but we'll allow some slop in VLC files
12387 */
12388 if (final_delay == 0)
12389 {
12390 if (need_iterations != MagickFalse)
12391 {
12392 /*
12393 It's probably a GIF with loop; don't run it *too* fast.
12394 */
glennrp02617122010-07-28 13:07:35 +000012395 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012396 {
12397 final_delay=10;
12398 (void) ThrowMagickException(&image->exception,
12399 GetMagickModule(),CoderWarning,
12400 "input has zero delay between all frames; assuming",
12401 " 10 cs `%s'","");
12402 }
cristy3ed852e2009-09-05 21:47:34 +000012403 }
12404 else
12405 mng_info->ticks_per_second=0;
12406 }
12407 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012408 mng_info->ticks_per_second=(png_uint_32)
12409 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012410 if (final_delay > 50)
12411 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012412
cristy3ed852e2009-09-05 21:47:34 +000012413 if (final_delay > 75)
12414 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012415
cristy3ed852e2009-09-05 21:47:34 +000012416 if (final_delay > 125)
12417 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012418
cristy3ed852e2009-09-05 21:47:34 +000012419 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12420 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12421 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12422 1UL*image->ticks_per_second))
12423 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12424 }
glennrp0fe50b42010-11-16 03:52:51 +000012425
cristy3ed852e2009-09-05 21:47:34 +000012426 if (mng_info->need_fram != MagickFalse)
12427 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12428 /*
12429 If pseudocolor, we should also check to see if all the
12430 palettes are identical and write a global PLTE if they are.
12431 ../glennrp Feb 99.
12432 */
12433 /*
12434 Write the MNG version 1.0 signature and MHDR chunk.
12435 */
12436 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12437 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12438 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012439 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012440 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12441 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012442 PNGLong(chunk+12,mng_info->ticks_per_second);
12443 PNGLong(chunk+16,0L); /* layer count=unknown */
12444 PNGLong(chunk+20,0L); /* frame count=unknown */
12445 PNGLong(chunk+24,0L); /* play time=unknown */
12446 if (write_jng)
12447 {
12448 if (need_matte)
12449 {
12450 if (need_defi || mng_info->need_fram || use_global_plte)
12451 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012452
cristy3ed852e2009-09-05 21:47:34 +000012453 else
12454 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12455 }
glennrp0fe50b42010-11-16 03:52:51 +000012456
cristy3ed852e2009-09-05 21:47:34 +000012457 else
12458 {
12459 if (need_defi || mng_info->need_fram || use_global_plte)
12460 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012461
cristy3ed852e2009-09-05 21:47:34 +000012462 else
12463 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12464 }
12465 }
glennrp0fe50b42010-11-16 03:52:51 +000012466
cristy3ed852e2009-09-05 21:47:34 +000012467 else
12468 {
12469 if (need_matte)
12470 {
12471 if (need_defi || mng_info->need_fram || use_global_plte)
12472 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012473
cristy3ed852e2009-09-05 21:47:34 +000012474 else
12475 PNGLong(chunk+28,9L); /* simplicity=VLC */
12476 }
glennrp0fe50b42010-11-16 03:52:51 +000012477
cristy3ed852e2009-09-05 21:47:34 +000012478 else
12479 {
12480 if (need_defi || mng_info->need_fram || use_global_plte)
12481 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012482
cristy3ed852e2009-09-05 21:47:34 +000012483 else
12484 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12485 }
12486 }
12487 (void) WriteBlob(image,32,chunk);
12488 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12489 option=GetImageOption(image_info,"mng:need-cacheoff");
12490 if (option != (const char *) NULL)
12491 {
12492 size_t
12493 length;
12494
12495 /*
12496 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12497 */
12498 PNGType(chunk,mng_nEED);
12499 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012500 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012501 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012502 length+=4;
12503 (void) WriteBlob(image,length,chunk);
12504 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12505 }
12506 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12507 (GetNextImageInList(image) != (Image *) NULL) &&
12508 (image->iterations != 1))
12509 {
12510 /*
12511 Write MNG TERM chunk
12512 */
12513 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12514 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012515 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012516 chunk[4]=3; /* repeat animation */
12517 chunk[5]=0; /* show last frame when done */
12518 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12519 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012520
cristy3ed852e2009-09-05 21:47:34 +000012521 if (image->iterations == 0)
12522 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012523
cristy3ed852e2009-09-05 21:47:34 +000012524 else
12525 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012526
cristy3ed852e2009-09-05 21:47:34 +000012527 if (logging != MagickFalse)
12528 {
12529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012530 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12531 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012532
cristy3ed852e2009-09-05 21:47:34 +000012533 if (image->iterations == 0)
12534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012535 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012536
cristy3ed852e2009-09-05 21:47:34 +000012537 else
12538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012539 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012540 }
12541 (void) WriteBlob(image,14,chunk);
12542 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12543 }
12544 /*
12545 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12546 */
12547 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12548 mng_info->equal_srgbs)
12549 {
12550 /*
12551 Write MNG sRGB chunk
12552 */
12553 (void) WriteBlobMSBULong(image,1L);
12554 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012555 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012556
cristy3ed852e2009-09-05 21:47:34 +000012557 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012558 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012559 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012560 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012561
cristy3ed852e2009-09-05 21:47:34 +000012562 else
glennrpe610a072010-08-05 17:08:46 +000012563 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012564 Magick_RenderingIntent_to_PNG_RenderingIntent(
12565 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012566
cristy3ed852e2009-09-05 21:47:34 +000012567 (void) WriteBlob(image,5,chunk);
12568 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12569 mng_info->have_write_global_srgb=MagickTrue;
12570 }
glennrp0fe50b42010-11-16 03:52:51 +000012571
cristy3ed852e2009-09-05 21:47:34 +000012572 else
12573 {
12574 if (image->gamma && mng_info->equal_gammas)
12575 {
12576 /*
12577 Write MNG gAMA chunk
12578 */
12579 (void) WriteBlobMSBULong(image,4L);
12580 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012581 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012582 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012583 (void) WriteBlob(image,8,chunk);
12584 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12585 mng_info->have_write_global_gama=MagickTrue;
12586 }
12587 if (mng_info->equal_chrms)
12588 {
12589 PrimaryInfo
12590 primary;
12591
12592 /*
12593 Write MNG cHRM chunk
12594 */
12595 (void) WriteBlobMSBULong(image,32L);
12596 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012597 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012598 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012599 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12600 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012601 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012602 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12603 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012604 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012605 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12606 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012607 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012608 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12609 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012610 (void) WriteBlob(image,36,chunk);
12611 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12612 mng_info->have_write_global_chrm=MagickTrue;
12613 }
12614 }
12615 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12616 {
12617 /*
12618 Write MNG pHYs chunk
12619 */
12620 (void) WriteBlobMSBULong(image,9L);
12621 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012622 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012623
cristy3ed852e2009-09-05 21:47:34 +000012624 if (image->units == PixelsPerInchResolution)
12625 {
cristy35ef8242010-06-03 16:24:13 +000012626 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012627 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012628
cristy35ef8242010-06-03 16:24:13 +000012629 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012630 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012631
cristy3ed852e2009-09-05 21:47:34 +000012632 chunk[12]=1;
12633 }
glennrp0fe50b42010-11-16 03:52:51 +000012634
cristy3ed852e2009-09-05 21:47:34 +000012635 else
12636 {
12637 if (image->units == PixelsPerCentimeterResolution)
12638 {
cristy35ef8242010-06-03 16:24:13 +000012639 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012640 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012641
cristy35ef8242010-06-03 16:24:13 +000012642 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012643 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012644
cristy3ed852e2009-09-05 21:47:34 +000012645 chunk[12]=1;
12646 }
glennrp0fe50b42010-11-16 03:52:51 +000012647
cristy3ed852e2009-09-05 21:47:34 +000012648 else
12649 {
cristy35ef8242010-06-03 16:24:13 +000012650 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12651 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012652 chunk[12]=0;
12653 }
12654 }
12655 (void) WriteBlob(image,13,chunk);
12656 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12657 }
12658 /*
12659 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12660 or does not cover the entire frame.
12661 */
12662 if (write_mng && (image->matte || image->page.x > 0 ||
12663 image->page.y > 0 || (image->page.width &&
12664 (image->page.width+image->page.x < mng_info->page.width))
12665 || (image->page.height && (image->page.height+image->page.y
12666 < mng_info->page.height))))
12667 {
12668 (void) WriteBlobMSBULong(image,6L);
12669 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012670 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012671 red=ScaleQuantumToShort(image->background_color.red);
12672 green=ScaleQuantumToShort(image->background_color.green);
12673 blue=ScaleQuantumToShort(image->background_color.blue);
12674 PNGShort(chunk+4,red);
12675 PNGShort(chunk+6,green);
12676 PNGShort(chunk+8,blue);
12677 (void) WriteBlob(image,10,chunk);
12678 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12679 if (mng_info->equal_backgrounds)
12680 {
12681 (void) WriteBlobMSBULong(image,6L);
12682 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012683 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012684 (void) WriteBlob(image,10,chunk);
12685 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12686 }
12687 }
12688
12689#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12690 if ((need_local_plte == MagickFalse) &&
12691 (image->storage_class == PseudoClass) &&
12692 (all_images_are_gray == MagickFalse))
12693 {
cristybb503372010-05-27 20:51:26 +000012694 size_t
cristy3ed852e2009-09-05 21:47:34 +000012695 data_length;
12696
12697 /*
12698 Write MNG PLTE chunk
12699 */
12700 data_length=3*image->colors;
12701 (void) WriteBlobMSBULong(image,data_length);
12702 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012703 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012704
cristybb503372010-05-27 20:51:26 +000012705 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012706 {
12707 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
12708 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
12709 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
12710 }
glennrp0fe50b42010-11-16 03:52:51 +000012711
cristy3ed852e2009-09-05 21:47:34 +000012712 (void) WriteBlob(image,data_length+4,chunk);
12713 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12714 mng_info->have_write_global_plte=MagickTrue;
12715 }
12716#endif
12717 }
12718 scene=0;
12719 mng_info->delay=0;
12720#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12721 defined(PNG_MNG_FEATURES_SUPPORTED)
12722 mng_info->equal_palettes=MagickFalse;
12723#endif
12724 do
12725 {
12726 if (mng_info->adjoin)
12727 {
12728#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12729 defined(PNG_MNG_FEATURES_SUPPORTED)
12730 /*
12731 If we aren't using a global palette for the entire MNG, check to
12732 see if we can use one for two or more consecutive images.
12733 */
12734 if (need_local_plte && use_global_plte && !all_images_are_gray)
12735 {
12736 if (mng_info->IsPalette)
12737 {
12738 /*
12739 When equal_palettes is true, this image has the same palette
12740 as the previous PseudoClass image
12741 */
12742 mng_info->have_write_global_plte=mng_info->equal_palettes;
12743 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12744 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12745 {
12746 /*
12747 Write MNG PLTE chunk
12748 */
cristybb503372010-05-27 20:51:26 +000012749 size_t
cristy3ed852e2009-09-05 21:47:34 +000012750 data_length;
12751
12752 data_length=3*image->colors;
12753 (void) WriteBlobMSBULong(image,data_length);
12754 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012755 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012756
cristybb503372010-05-27 20:51:26 +000012757 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012758 {
12759 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12760 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12761 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12762 }
glennrp0fe50b42010-11-16 03:52:51 +000012763
cristy3ed852e2009-09-05 21:47:34 +000012764 (void) WriteBlob(image,data_length+4,chunk);
12765 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12766 (uInt) (data_length+4)));
12767 mng_info->have_write_global_plte=MagickTrue;
12768 }
12769 }
12770 else
12771 mng_info->have_write_global_plte=MagickFalse;
12772 }
12773#endif
12774 if (need_defi)
12775 {
cristybb503372010-05-27 20:51:26 +000012776 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012777 previous_x,
12778 previous_y;
12779
12780 if (scene)
12781 {
12782 previous_x=mng_info->page.x;
12783 previous_y=mng_info->page.y;
12784 }
12785 else
12786 {
12787 previous_x=0;
12788 previous_y=0;
12789 }
12790 mng_info->page=image->page;
12791 if ((mng_info->page.x != previous_x) ||
12792 (mng_info->page.y != previous_y))
12793 {
12794 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12795 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012796 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012797 chunk[4]=0; /* object 0 MSB */
12798 chunk[5]=0; /* object 0 LSB */
12799 chunk[6]=0; /* visible */
12800 chunk[7]=0; /* abstract */
12801 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12802 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12803 (void) WriteBlob(image,16,chunk);
12804 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12805 }
12806 }
12807 }
12808
12809 mng_info->write_mng=write_mng;
12810
12811 if ((int) image->dispose >= 3)
12812 mng_info->framing_mode=3;
12813
12814 if (mng_info->need_fram && mng_info->adjoin &&
12815 ((image->delay != mng_info->delay) ||
12816 (mng_info->framing_mode != mng_info->old_framing_mode)))
12817 {
12818 if (image->delay == mng_info->delay)
12819 {
12820 /*
12821 Write a MNG FRAM chunk with the new framing mode.
12822 */
12823 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12824 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012825 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012826 chunk[4]=(unsigned char) mng_info->framing_mode;
12827 (void) WriteBlob(image,5,chunk);
12828 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12829 }
12830 else
12831 {
12832 /*
12833 Write a MNG FRAM chunk with the delay.
12834 */
12835 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12836 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012837 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012838 chunk[4]=(unsigned char) mng_info->framing_mode;
12839 chunk[5]=0; /* frame name separator (no name) */
12840 chunk[6]=2; /* flag for changing default delay */
12841 chunk[7]=0; /* flag for changing frame timeout */
12842 chunk[8]=0; /* flag for changing frame clipping */
12843 chunk[9]=0; /* flag for changing frame sync_id */
12844 PNGLong(chunk+10,(png_uint_32)
12845 ((mng_info->ticks_per_second*
12846 image->delay)/MagickMax(image->ticks_per_second,1)));
12847 (void) WriteBlob(image,14,chunk);
12848 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012849 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012850 }
12851 mng_info->old_framing_mode=mng_info->framing_mode;
12852 }
12853
12854#if defined(JNG_SUPPORTED)
12855 if (image_info->compression == JPEGCompression)
12856 {
12857 ImageInfo
12858 *write_info;
12859
12860 if (logging != MagickFalse)
12861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12862 " Writing JNG object.");
12863 /* To do: specify the desired alpha compression method. */
12864 write_info=CloneImageInfo(image_info);
12865 write_info->compression=UndefinedCompression;
12866 status=WriteOneJNGImage(mng_info,write_info,image);
12867 write_info=DestroyImageInfo(write_info);
12868 }
12869 else
12870#endif
12871 {
12872 if (logging != MagickFalse)
12873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12874 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012875
glennrpb9cfe272010-12-21 15:08:06 +000012876 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012877 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012878
12879 /* We don't want any ancillary chunks written */
12880 mng_info->ping_exclude_bKGD=MagickTrue;
12881 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012882 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012883 mng_info->ping_exclude_EXIF=MagickTrue;
12884 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012885 mng_info->ping_exclude_iCCP=MagickTrue;
12886 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12887 mng_info->ping_exclude_oFFs=MagickTrue;
12888 mng_info->ping_exclude_pHYs=MagickTrue;
12889 mng_info->ping_exclude_sRGB=MagickTrue;
12890 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012891 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012892 mng_info->ping_exclude_vpAg=MagickTrue;
12893 mng_info->ping_exclude_zCCP=MagickTrue;
12894 mng_info->ping_exclude_zTXt=MagickTrue;
12895
cristy3ed852e2009-09-05 21:47:34 +000012896 status=WriteOnePNGImage(mng_info,image_info,image);
12897 }
12898
12899 if (status == MagickFalse)
12900 {
12901 MngInfoFreeStruct(mng_info,&have_mng_structure);
12902 (void) CloseBlob(image);
12903 return(MagickFalse);
12904 }
12905 (void) CatchImageException(image);
12906 if (GetNextImageInList(image) == (Image *) NULL)
12907 break;
12908 image=SyncNextImageInList(image);
12909 status=SetImageProgress(image,SaveImagesTag,scene++,
12910 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012911
cristy3ed852e2009-09-05 21:47:34 +000012912 if (status == MagickFalse)
12913 break;
glennrp0fe50b42010-11-16 03:52:51 +000012914
cristy3ed852e2009-09-05 21:47:34 +000012915 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012916
cristy3ed852e2009-09-05 21:47:34 +000012917 if (write_mng)
12918 {
12919 while (GetPreviousImageInList(image) != (Image *) NULL)
12920 image=GetPreviousImageInList(image);
12921 /*
12922 Write the MEND chunk.
12923 */
12924 (void) WriteBlobMSBULong(image,0x00000000L);
12925 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012926 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012927 (void) WriteBlob(image,4,chunk);
12928 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12929 }
12930 /*
12931 Relinquish resources.
12932 */
12933 (void) CloseBlob(image);
12934 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012935
cristy3ed852e2009-09-05 21:47:34 +000012936 if (logging != MagickFalse)
12937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012938
cristy3ed852e2009-09-05 21:47:34 +000012939 return(MagickTrue);
12940}
glennrpd5045b42010-03-24 12:40:35 +000012941#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012942
cristy3ed852e2009-09-05 21:47:34 +000012943static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12944{
12945 image=image;
12946 printf("Your PNG library is too old: You have libpng-%s\n",
12947 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012948
cristy3ed852e2009-09-05 21:47:34 +000012949 ThrowBinaryException(CoderError,"PNG library is too old",
12950 image_info->filename);
12951}
glennrp39992b42010-11-14 00:03:43 +000012952
cristy3ed852e2009-09-05 21:47:34 +000012953static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12954{
12955 return(WritePNGImage(image_info,image));
12956}
glennrpd5045b42010-03-24 12:40:35 +000012957#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012958#endif