blob: 93a28d484ad79c3cc7c2a2ce459b13043001e142 [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
136#define LBR01RedPixelPacketComponent(pixelpacket) \
137 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
138 0 : QuantumRange);
139
140#define LBR01GreenPixelPacketComponent(pixelpacket) \
141 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
142 0 : QuantumRange);
143
144#define LBR01BluePixelPacketComponent(pixelpacket) \
145 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
146 0 : QuantumRange);
147
148#define LBR01OpacityPixelPacketComponent(pixelpacket) \
149 (pixelpacket).opacity=(ScaleQuantumToChar((pixelpacket).opacity) < 0x10 ? \
150 0 : QuantumRange);
151
152#define LBR01RGBPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000153 { \
glennrp8e58efd2011-05-20 12:16:29 +0000154 LBR01RedPixelPacketComponent((pixelpacket)); \
155 LBR01GreenPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000156 LBR01BluePixelPacketComponent((pixelpacket)); \
157 }
glennrp8e58efd2011-05-20 12:16:29 +0000158
159#define LBR01RGBOPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 { \
glennrp8e58efd2011-05-20 12:16:29 +0000161 LBR01RGBPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000162 LBR01OpacityPixelPacketComponent((pixelpacket)); \
163 }
glennrp8e58efd2011-05-20 12:16:29 +0000164
165#define LBR01RedPixelComponent(pixel) \
166 (ScaleQuantumToChar(GetRedPixelComponent((pixel))) < 0x10 ? \
167 0 : QuantumRange);
168
169#define LBR01GreenPixelComponent(pixel) \
170 (ScaleQuantumToChar(GetGreenPixelComponent((pixel))) < 0x10 ? \
171 0 : QuantumRange);
172
173#define LBR01BluePixelComponent(pixel) \
174 (ScaleQuantumToChar(GetBluePixelComponent((pixel))) < 0x10 ? \
175 0 : QuantumRange);
176
177#define LBR01OpacityPixelComponent(pixel) \
178 (ScaleQuantumToChar(GetOpacityPixelComponent((pixel))) < 0x10 ? \
179 0 : QuantumRange);
180
181#define LBR01RGBPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000182 { \
glennrp8e58efd2011-05-20 12:16:29 +0000183 LBR01RedPixelComponent((pixel)); \
184 LBR01GreenPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000185 LBR01BluePixelComponent((pixel)); \
186 }
glennrp8e58efd2011-05-20 12:16:29 +0000187
188#define LBR01RGBOPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000189 { \
glennrp8e58efd2011-05-20 12:16:29 +0000190 LBR01RGBPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000191 LBR01OpacityPixelComponent((pixel)); \
192 }
glennrp8e58efd2011-05-20 12:16:29 +0000193
194/* LBR02: Replicate top 2 bits */
195
196#define LBR02RedPixelPacketComponent(pixelpacket) \
197 { \
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 }
202#define LBR02GreenPixelPacketComponent(pixelpacket) \
203 { \
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 }
208#define LBR02BluePixelPacketComponent(pixelpacket) \
209 { \
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 }
214#define LBR02OpacityPixelPacketComponent(pixelpacket) \
215 { \
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
221#define LBR02RGBPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000222 { \
glennrp8e58efd2011-05-20 12:16:29 +0000223 LBR02RedPixelPacketComponent((pixelpacket)); \
224 LBR02GreenPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000225 LBR02BluePixelPacketComponent((pixelpacket)); \
226 }
glennrp8e58efd2011-05-20 12:16:29 +0000227
228#define LBR02RGBOPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000229 { \
glennrp8e58efd2011-05-20 12:16:29 +0000230 LBR02RGBPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000231 LBR02OpacityPixelPacketComponent((pixelpacket)); \
232 }
glennrp8e58efd2011-05-20 12:16:29 +0000233
234#define LBR02RedPixelComponent(pixel) \
235 { \
236 unsigned char lbr_bits=ScaleQuantumToChar(GetRedPixelComponent((pixel))) \
237 & 0xc0; \
238 SetRedPixelComponent((pixel), ScaleCharToQuantum( \
239 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6)))); \
240 }
241#define LBR02GreenPixelComponent(pixel) \
242 { \
243 unsigned char lbr_bits=ScaleQuantumToChar(GetGreenPixelComponent((pixel)))\
244 & 0xc0; \
245 SetGreenPixelComponent((pixel), ScaleCharToQuantum( \
246 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6)))); \
247 }
248#define LBR02BluePixelComponent(pixel) \
249 { \
250 unsigned char lbr_bits= \
251 ScaleQuantumToChar(GetBluePixelComponent((pixel))) & 0xc0; \
252 SetBluePixelComponent((pixel), ScaleCharToQuantum( \
253 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6)))); \
254 }
255#define LBR02OpacityPixelComponent(pixel) \
256 { \
257 unsigned char lbr_bits= \
258 ScaleQuantumToChar(GetOpacityPixelComponent((pixel))) & 0xc0; \
259 SetOpacityPixelComponent((pixel), ScaleCharToQuantum( \
260 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6)))); \
261 }
262
263#define LBR02RGBPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000264 { \
glennrp8e58efd2011-05-20 12:16:29 +0000265 LBR02RedPixelComponent((pixel)); \
266 LBR02GreenPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000267 LBR02BluePixelComponent((pixel)); \
268 }
glennrp8e58efd2011-05-20 12:16:29 +0000269
270#define LBR02RGBOPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000271 { \
glennrp8e58efd2011-05-20 12:16:29 +0000272 LBR02RGBPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000273 LBR02OpacityPixelComponent((pixel)); \
274 }
glennrp8e58efd2011-05-20 12:16:29 +0000275
276/* LBR03: Replicate top 3 bits (only used with opaque pixels during
277 PNG8 quantization) */
278
279#define LBR03RedPixelPacketComponent(pixelpacket) \
280 { \
281 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
282 (pixelpacket).red=ScaleCharToQuantum( \
283 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
284 }
285#define LBR03GreenPixelPacketComponent(pixelpacket) \
286 { \
287 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
288 (pixelpacket).green=ScaleCharToQuantum( \
289 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
290 }
291#define LBR03BluePixelPacketComponent(pixelpacket) \
292 { \
293 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
294 (pixelpacket).blue=ScaleCharToQuantum( \
295 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
296 }
297
298#define LBR03RGBPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000299 { \
glennrp8e58efd2011-05-20 12:16:29 +0000300 LBR03RedPixelPacketComponent((pixelpacket)); \
301 LBR03GreenPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000302 LBR03BluePixelPacketComponent((pixelpacket)); \
303 }
glennrp8e58efd2011-05-20 12:16:29 +0000304
305#define LBR03RedPixelComponent(pixel) \
306 { \
307 unsigned char lbr_bits=ScaleQuantumToChar(GetRedPixelComponent((pixel))) \
308 & 0xe0; \
309 SetRedPixelComponent((pixel), ScaleCharToQuantum( \
310 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6)))); \
311 }
312#define LBR03GreenPixelComponent(pixel) \
313 { \
314 unsigned char lbr_bits=ScaleQuantumToChar(GetGreenPixelComponent((pixel)))\
315 & 0xe0; \
316 SetGreenPixelComponent((pixel), ScaleCharToQuantum( \
317 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6)))); \
318 }
319#define LBR03BluePixelComponent(pixel) \
320 { \
321 unsigned char lbr_bits=ScaleQuantumToChar(GetBluePixelComponent((pixel))) \
322 & 0xe0; \
323 SetBluePixelComponent((pixel), ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6)))); \
325 }
326
327#define LBR03RGBPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000328 { \
glennrp8e58efd2011-05-20 12:16:29 +0000329 LBR03RedPixelComponent((pixel)); \
330 LBR03GreenPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000331 LBR03BluePixelComponent((pixel)); \
332 }
glennrp8e58efd2011-05-20 12:16:29 +0000333
334/* LBR04: Replicate top 4 bits */
335
336#define LBR04RedPixelPacketComponent(pixelpacket) \
337 { \
338 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
339 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
340 }
341#define LBR04GreenPixelPacketComponent(pixelpacket) \
342 { \
343 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
344 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
345 }
346#define LBR04BluePixelPacketComponent(pixelpacket) \
347 { \
348 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
349 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
350 }
351#define LBR04OpacityPixelPacketComponent(pixelpacket) \
352 { \
353 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).opacity) & 0xf0; \
354 (pixelpacket).opacity=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
355 }
356
357#define LBR04RGBPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000358 { \
glennrp8e58efd2011-05-20 12:16:29 +0000359 LBR04RedPixelPacketComponent((pixelpacket)); \
360 LBR04GreenPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000361 LBR04BluePixelPacketComponent((pixelpacket)); \
362 }
glennrp8e58efd2011-05-20 12:16:29 +0000363
364#define LBR04RGBOPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000365 { \
glennrp8e58efd2011-05-20 12:16:29 +0000366 LBR04RGBPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000367 LBR04OpacityPixelPacketComponent((pixelpacket)); \
368 }
glennrp8e58efd2011-05-20 12:16:29 +0000369
370#define LBR04RedPixelComponent(pixel) \
371 { \
372 unsigned char lbr_bits=ScaleQuantumToChar(GetRedPixelComponent((pixel))) \
373 & 0xf0; \
374 SetRedPixelComponent((pixel),\
375 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4)))); \
376 }
377#define LBR04GreenPixelComponent(pixel) \
378 { \
379 unsigned char lbr_bits=ScaleQuantumToChar(GetGreenPixelComponent((pixel)))\
380 & 0xf0; \
381 SetGreenPixelComponent((pixel),\
382 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4)))); \
383 }
384#define LBR04BluePixelComponent(pixel) \
385 { \
386 unsigned char lbr_bits= \
387 ScaleQuantumToChar(GetBluePixelComponent((pixel))) & 0xf0; \
388 SetBluePixelComponent((pixel),\
389 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4)))); \
390 }
391#define LBR04OpacityPixelComponent(pixel) \
392 { \
393 unsigned char lbr_bits= \
394 ScaleQuantumToChar(GetOpacityPixelComponent((pixel))) & 0xf0; \
395 SetOpacityPixelComponent((pixel),\
396 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4)))); \
397 }
398
399#define LBR04RGBPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000400 { \
glennrp8e58efd2011-05-20 12:16:29 +0000401 LBR04RedPixelComponent((pixel)); \
402 LBR04GreenPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000403 LBR04BluePixelComponent((pixel)); \
404 }
glennrp8e58efd2011-05-20 12:16:29 +0000405
406#define LBR04RGBOPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000407 { \
glennrp8e58efd2011-05-20 12:16:29 +0000408 LBR04RGBPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000409 LBR04OpacityPixelComponent((pixel)); \
410 }
glennrp8e58efd2011-05-20 12:16:29 +0000411
412
413/* LBR08: Replicate top 8 bits */
414
415#define LBR08RedPixelPacketComponent(pixelpacket) \
416 { \
417 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
418 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
419 }
420#define LBR08GreenPixelPacketComponent(pixelpacket) \
421 { \
422 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
423 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
424 }
425#define LBR08BluePixelPacketComponent(pixelpacket) \
426 { \
427 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
428 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
429 }
430#define LBR08OpacityPixelPacketComponent(pixelpacket) \
431 { \
432 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).opacity); \
433 (pixelpacket).opacity=ScaleCharToQuantum((lbr_bits)); \
434 }
435
436#define LBR08RGBPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000437 { \
glennrp8e58efd2011-05-20 12:16:29 +0000438 LBR08RedPixelPacketComponent((pixelpacket)); \
439 LBR08GreenPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000440 LBR08BluePixelPacketComponent((pixelpacket)); \
441 }
glennrp8e58efd2011-05-20 12:16:29 +0000442
443#define LBR08RGBOPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 { \
glennrp8e58efd2011-05-20 12:16:29 +0000445 LBR08RGBPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000446 LBR08OpacityPixelPacketComponent((pixelpacket)); \
447 }
glennrp8e58efd2011-05-20 12:16:29 +0000448
449#define LBR08RedPixelComponent(pixel) \
450 { \
451 unsigned char lbr_bits= \
452 ScaleQuantumToChar(GetRedPixelComponent((pixel))); \
453 SetRedPixelComponent((pixel),\
454 ScaleCharToQuantum((lbr_bits))); \
455 }
456#define LBR08GreenPixelComponent(pixel) \
457 { \
458 unsigned char lbr_bits= \
459 ScaleQuantumToChar(GetGreenPixelComponent((pixel))); \
460 SetGreenPixelComponent((pixel),\
461 ScaleCharToQuantum((lbr_bits))); \
462 }
463#define LBR08BluePixelComponent(pixel) \
464 { \
465 unsigned char lbr_bits= \
466 ScaleQuantumToChar(GetBluePixelComponent((pixel))); \
467 SetBluePixelComponent((pixel),\
468 ScaleCharToQuantum((lbr_bits))); \
469 }
470#define LBR08OpacityPixelComponent(pixel) \
471 { \
472 unsigned char lbr_bits= \
473 ScaleQuantumToChar(GetOpacityPixelComponent((pixel))); \
474 SetOpacityPixelComponent((pixel),\
475 ScaleCharToQuantum((lbr_bits))); \
476 }
477
478#define LBR08RGBPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000479 { \
glennrp8e58efd2011-05-20 12:16:29 +0000480 LBR08RedPixelComponent((pixel)); \
481 LBR08GreenPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000482 LBR08BluePixelComponent((pixel)); \
483 }
glennrp8e58efd2011-05-20 12:16:29 +0000484
485#define LBR08RGBOPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000486 { \
glennrp8e58efd2011-05-20 12:16:29 +0000487 LBR08RGBPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000488 LBR08OpacityPixelComponent((pixel)); \
489 }
glennrp8e58efd2011-05-20 12:16:29 +0000490
491
492/* LBR16: Replicate top 16 bits */
493
494#define LBR16RedPixelPacketComponent(pixelpacket) \
495 { \
496 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
497 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
498 }
499#define LBR16GreenPixelPacketComponent(pixelpacket) \
500 { \
501 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
502 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
503 }
504#define LBR16BluePixelPacketComponent(pixelpacket) \
505 { \
506 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
507 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
508 }
509#define LBR16OpacityPixelPacketComponent(pixelpacket) \
510 { \
511 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).opacity); \
512 (pixelpacket).opacity=ScaleShortToQuantum((lbr_bits)); \
513 }
514
515#define LBR16RGBPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000516 { \
glennrp8e58efd2011-05-20 12:16:29 +0000517 LBR16RedPixelPacketComponent((pixelpacket)); \
518 LBR16GreenPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000519 LBR16BluePixelPacketComponent((pixelpacket)); \
520 }
glennrp8e58efd2011-05-20 12:16:29 +0000521
522#define LBR16RGBOPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 { \
glennrp8e58efd2011-05-20 12:16:29 +0000524 LBR16RGBPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000525 LBR16OpacityPixelPacketComponent((pixelpacket)); \
526 }
glennrp8e58efd2011-05-20 12:16:29 +0000527
528#define LBR16RedPixelComponent(pixel) \
529 { \
530 unsigned short lbr_bits= \
531 ScaleQuantumToShort(GetRedPixelComponent((pixel))); \
532 SetRedPixelComponent((pixel),\
533 ScaleShortToQuantum((lbr_bits))); \
534 }
535#define LBR16GreenPixelComponent(pixel) \
536 { \
537 unsigned short lbr_bits= \
538 ScaleQuantumToShort(GetGreenPixelComponent((pixel))); \
539 SetGreenPixelComponent((pixel),\
540 ScaleShortToQuantum((lbr_bits))); \
541 }
542#define LBR16BluePixelComponent(pixel) \
543 { \
544 unsigned short lbr_bits= \
545 ScaleQuantumToShort(GetBluePixelComponent((pixel))); \
546 SetBluePixelComponent((pixel),\
547 ScaleShortToQuantum((lbr_bits))); \
548 }
549#define LBR16OpacityPixelComponent(pixel) \
550 { \
551 unsigned short lbr_bits= \
552 ScaleQuantumToShort(GetOpacityPixelComponent((pixel))); \
553 SetOpacityPixelComponent((pixel),\
554 ScaleShortToQuantum((lbr_bits))); \
555 }
556
557#define LBR16RGBPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000558 { \
glennrp8e58efd2011-05-20 12:16:29 +0000559 LBR16RedPixelComponent((pixel)); \
560 LBR16GreenPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000561 LBR16BluePixelComponent((pixel)); \
562 }
glennrp8e58efd2011-05-20 12:16:29 +0000563
564#define LBR16RGBOPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000565 { \
glennrp8e58efd2011-05-20 12:16:29 +0000566 LBR16RGBPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000567 LBR16OpacityPixelComponent((pixel)); \
568 }
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,
862 write_png8,
863 write_png24,
864 write_png32;
865
866#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000867 size_t
cristy3ed852e2009-09-05 21:47:34 +0000868 basi_width,
869 basi_height;
870
871 unsigned int
872 basi_depth,
873 basi_color_type,
874 basi_compression_method,
875 basi_filter_type,
876 basi_interlace_method,
877 basi_red,
878 basi_green,
879 basi_blue,
880 basi_alpha,
881 basi_viewable;
882#endif
883
884 png_uint_16
885 magn_first,
886 magn_last,
887 magn_mb,
888 magn_ml,
889 magn_mr,
890 magn_mt,
891 magn_mx,
892 magn_my,
893 magn_methx,
894 magn_methy;
895
896 PixelPacket
897 mng_global_bkgd;
898
glennrp26f37912010-12-23 16:22:42 +0000899 /* Added at version 6.6.6-7 */
900 MagickBooleanType
901 ping_exclude_bKGD,
902 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000903 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000904 ping_exclude_EXIF,
905 ping_exclude_gAMA,
906 ping_exclude_iCCP,
907 /* ping_exclude_iTXt, */
908 ping_exclude_oFFs,
909 ping_exclude_pHYs,
910 ping_exclude_sRGB,
911 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000912 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000913 ping_exclude_vpAg,
914 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000915 ping_exclude_zTXt,
916 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000917
cristy3ed852e2009-09-05 21:47:34 +0000918} MngInfo;
919#endif /* VER */
920
921/*
922 Forward declarations.
923*/
924static MagickBooleanType
925 WritePNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000926
cristy3ed852e2009-09-05 21:47:34 +0000927static MagickBooleanType
928 WriteMNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000929
cristy3ed852e2009-09-05 21:47:34 +0000930#if defined(JNG_SUPPORTED)
931static MagickBooleanType
932 WriteJNGImage(const ImageInfo *,Image *);
933#endif
934
glennrp0c3e06b2010-11-19 13:45:02 +0000935#if PNG_LIBPNG_VER > 10011
936
glennrpfd05d622011-02-25 04:10:33 +0000937
glennrp0c3e06b2010-11-19 13:45:02 +0000938#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
939static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000940LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000941{
glennrp67b9c1a2011-04-22 18:47:36 +0000942 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
943 *
944 * This is true if the high byte and the next highest byte of
945 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000946 * are equal to each other. We check this by seeing if the samples
947 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000948 *
949 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000950 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000951 */
952
glennrp3faa9a32011-04-23 14:00:25 +0000953#define QuantumToCharToQuantumEqQuantum(quantum) \
954 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
955
glennrp0c3e06b2010-11-19 13:45:02 +0000956 MagickBooleanType
957 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000958
glennrp03e11f62011-04-22 13:30:16 +0000959 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000960 {
961
962 const PixelPacket
963 *p;
964
965 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000966 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
967 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
968 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
969 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000970
971 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
972 {
973 int indx;
974
975 for (indx=0; indx < (ssize_t) image->colors; indx++)
976 {
glennrp3faa9a32011-04-23 14:00:25 +0000977 ok_to_reduce=(
978 QuantumToCharToQuantumEqQuantum(
979 image->colormap[indx].red) &&
980 QuantumToCharToQuantumEqQuantum(
981 image->colormap[indx].green) &&
982 QuantumToCharToQuantumEqQuantum(
983 image->colormap[indx].blue)) ?
984 MagickTrue : MagickFalse;
985
glennrp0c3e06b2010-11-19 13:45:02 +0000986 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000987 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000988 }
989 }
990
991 if ((ok_to_reduce != MagickFalse) &&
992 (image->storage_class != PseudoClass))
993 {
994 ssize_t
995 y;
996
997 register ssize_t
998 x;
999
1000 for (y=0; y < (ssize_t) image->rows; y++)
1001 {
1002 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1003
1004 if (p == (const PixelPacket *) NULL)
1005 {
1006 ok_to_reduce = MagickFalse;
1007 break;
1008 }
1009
1010 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1011 {
glennrp3faa9a32011-04-23 14:00:25 +00001012 ok_to_reduce=
1013 QuantumToCharToQuantumEqQuantum(GetRedPixelComponent(p)) &&
1014 QuantumToCharToQuantumEqQuantum(GetGreenPixelComponent(p)) &&
1015 QuantumToCharToQuantumEqQuantum(GetBluePixelComponent(p)) ?
1016 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001017
1018 if (ok_to_reduce == MagickFalse)
1019 break;
1020
1021 p++;
1022 }
glennrp8640fb52010-11-23 15:48:26 +00001023 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001024 break;
1025 }
1026 }
1027
1028 if (ok_to_reduce != MagickFalse)
1029 {
glennrp0c3e06b2010-11-19 13:45:02 +00001030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001031 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001032 }
glennrpa6a06632011-01-19 15:15:34 +00001033 else
1034 {
1035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001036 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001037 }
glennrp0c3e06b2010-11-19 13:45:02 +00001038 }
1039
1040 return ok_to_reduce;
1041}
1042#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1043
glennrpe610a072010-08-05 17:08:46 +00001044static int
glennrpcf002022011-01-30 02:38:15 +00001045Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001046{
glennrpe610a072010-08-05 17:08:46 +00001047 switch (intent)
1048 {
1049 case PerceptualIntent:
1050 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001051
glennrpe610a072010-08-05 17:08:46 +00001052 case RelativeIntent:
1053 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001054
glennrpe610a072010-08-05 17:08:46 +00001055 case SaturationIntent:
1056 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001057
glennrpe610a072010-08-05 17:08:46 +00001058 case AbsoluteIntent:
1059 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001060
glennrpe610a072010-08-05 17:08:46 +00001061 default:
1062 return -1;
1063 }
1064}
1065
1066static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001067Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001068{
glennrpcf002022011-01-30 02:38:15 +00001069 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001070 {
1071 case 0:
1072 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001073
glennrpe610a072010-08-05 17:08:46 +00001074 case 1:
1075 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001076
glennrpe610a072010-08-05 17:08:46 +00001077 case 2:
1078 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001079
glennrpe610a072010-08-05 17:08:46 +00001080 case 3:
1081 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001082
glennrpe610a072010-08-05 17:08:46 +00001083 default:
1084 return UndefinedIntent;
1085 }
1086}
1087
cristybb503372010-05-27 20:51:26 +00001088static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001089{
1090 if (x > y)
1091 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001092
cristy3ed852e2009-09-05 21:47:34 +00001093 return(y);
1094}
glennrp0c3e06b2010-11-19 13:45:02 +00001095
cristybb503372010-05-27 20:51:26 +00001096static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001097{
1098 if (x < y)
1099 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001100
cristy3ed852e2009-09-05 21:47:34 +00001101 return(y);
1102}
glennrp0c3e06b2010-11-19 13:45:02 +00001103
cristy3ed852e2009-09-05 21:47:34 +00001104
1105/*
1106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1107% %
1108% %
1109% %
1110% I m a g e I s G r a y %
1111% %
1112% %
1113% %
1114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1115% %
1116% Like IsGrayImage except does not change DirectClass to PseudoClass %
1117% %
1118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1119*/
1120static MagickBooleanType ImageIsGray(Image *image)
1121{
1122 register const PixelPacket
1123 *p;
1124
cristybb503372010-05-27 20:51:26 +00001125 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001126 i,
1127 x,
1128 y;
1129
1130 assert(image != (Image *) NULL);
1131 assert(image->signature == MagickSignature);
1132 if (image->debug != MagickFalse)
1133 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1134
1135 if (image->storage_class == PseudoClass)
1136 {
cristybb503372010-05-27 20:51:26 +00001137 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001138 if (IsGray(image->colormap+i) == MagickFalse)
1139 return(MagickFalse);
1140 return(MagickTrue);
1141 }
cristybb503372010-05-27 20:51:26 +00001142 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001143 {
1144 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1145 if (p == (const PixelPacket *) NULL)
1146 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001147 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001148 {
1149 if (IsGray(p) == MagickFalse)
1150 return(MagickFalse);
1151 p++;
1152 }
1153 }
1154 return(MagickTrue);
1155}
glennrpd5045b42010-03-24 12:40:35 +00001156#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001157#endif /* MAGICKCORE_PNG_DELEGATE */
1158
1159/*
1160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1161% %
1162% %
1163% %
1164% I s M N G %
1165% %
1166% %
1167% %
1168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1169%
1170% IsMNG() returns MagickTrue if the image format type, identified by the
1171% magick string, is MNG.
1172%
1173% The format of the IsMNG method is:
1174%
1175% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1176%
1177% A description of each parameter follows:
1178%
1179% o magick: compare image format pattern against these bytes.
1180%
1181% o length: Specifies the length of the magick string.
1182%
1183%
1184*/
1185static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1186{
1187 if (length < 8)
1188 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001189
cristy3ed852e2009-09-05 21:47:34 +00001190 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1191 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001192
cristy3ed852e2009-09-05 21:47:34 +00001193 return(MagickFalse);
1194}
1195
1196/*
1197%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1198% %
1199% %
1200% %
1201% I s J N G %
1202% %
1203% %
1204% %
1205%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1206%
1207% IsJNG() returns MagickTrue if the image format type, identified by the
1208% magick string, is JNG.
1209%
1210% The format of the IsJNG method is:
1211%
1212% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1213%
1214% A description of each parameter follows:
1215%
1216% o magick: compare image format pattern against these bytes.
1217%
1218% o length: Specifies the length of the magick string.
1219%
1220%
1221*/
1222static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1223{
1224 if (length < 8)
1225 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001226
cristy3ed852e2009-09-05 21:47:34 +00001227 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1228 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001229
cristy3ed852e2009-09-05 21:47:34 +00001230 return(MagickFalse);
1231}
1232
1233/*
1234%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1235% %
1236% %
1237% %
1238% I s P N G %
1239% %
1240% %
1241% %
1242%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1243%
1244% IsPNG() returns MagickTrue if the image format type, identified by the
1245% magick string, is PNG.
1246%
1247% The format of the IsPNG method is:
1248%
1249% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1250%
1251% A description of each parameter follows:
1252%
1253% o magick: compare image format pattern against these bytes.
1254%
1255% o length: Specifies the length of the magick string.
1256%
1257*/
1258static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1259{
1260 if (length < 8)
1261 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001262
cristy3ed852e2009-09-05 21:47:34 +00001263 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1264 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001265
cristy3ed852e2009-09-05 21:47:34 +00001266 return(MagickFalse);
1267}
1268
1269#if defined(MAGICKCORE_PNG_DELEGATE)
1270#if defined(__cplusplus) || defined(c_plusplus)
1271extern "C" {
1272#endif
1273
glennrpd5045b42010-03-24 12:40:35 +00001274#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001275static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001276{
1277 unsigned char
1278 buffer[4];
1279
1280 assert(image != (Image *) NULL);
1281 assert(image->signature == MagickSignature);
1282 buffer[0]=(unsigned char) (value >> 24);
1283 buffer[1]=(unsigned char) (value >> 16);
1284 buffer[2]=(unsigned char) (value >> 8);
1285 buffer[3]=(unsigned char) value;
1286 return((size_t) WriteBlob(image,4,buffer));
1287}
1288
1289static void PNGLong(png_bytep p,png_uint_32 value)
1290{
1291 *p++=(png_byte) ((value >> 24) & 0xff);
1292 *p++=(png_byte) ((value >> 16) & 0xff);
1293 *p++=(png_byte) ((value >> 8) & 0xff);
1294 *p++=(png_byte) (value & 0xff);
1295}
1296
glennrpa521b2f2010-10-29 04:11:03 +00001297#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001298static void PNGsLong(png_bytep p,png_int_32 value)
1299{
1300 *p++=(png_byte) ((value >> 24) & 0xff);
1301 *p++=(png_byte) ((value >> 16) & 0xff);
1302 *p++=(png_byte) ((value >> 8) & 0xff);
1303 *p++=(png_byte) (value & 0xff);
1304}
glennrpa521b2f2010-10-29 04:11:03 +00001305#endif
cristy3ed852e2009-09-05 21:47:34 +00001306
1307static void PNGShort(png_bytep p,png_uint_16 value)
1308{
1309 *p++=(png_byte) ((value >> 8) & 0xff);
1310 *p++=(png_byte) (value & 0xff);
1311}
1312
1313static void PNGType(png_bytep p,png_bytep type)
1314{
1315 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1316}
1317
glennrp03812ae2010-12-24 01:31:34 +00001318static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1319 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001320{
1321 if (logging != MagickFalse)
1322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001323 " Writing %c%c%c%c chunk, length: %.20g",
1324 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001325}
glennrpd5045b42010-03-24 12:40:35 +00001326#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001327
1328#if defined(__cplusplus) || defined(c_plusplus)
1329}
1330#endif
1331
glennrpd5045b42010-03-24 12:40:35 +00001332#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001333/*
1334%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1335% %
1336% %
1337% %
1338% R e a d P N G I m a g e %
1339% %
1340% %
1341% %
1342%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1343%
1344% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1345% Multiple-image Network Graphics (MNG) image file and returns it. It
1346% allocates the memory necessary for the new Image structure and returns a
1347% pointer to the new image or set of images.
1348%
1349% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1350%
1351% The format of the ReadPNGImage method is:
1352%
1353% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1354%
1355% A description of each parameter follows:
1356%
1357% o image_info: the image info.
1358%
1359% o exception: return any errors or warnings in this structure.
1360%
1361% To do, more or less in chronological order (as of version 5.5.2,
1362% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1363%
1364% Get 16-bit cheap transparency working.
1365%
1366% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1367%
1368% Preserve all unknown and not-yet-handled known chunks found in input
1369% PNG file and copy them into output PNG files according to the PNG
1370% copying rules.
1371%
1372% (At this point, PNG encoding should be in full MNG compliance)
1373%
1374% Provide options for choice of background to use when the MNG BACK
1375% chunk is not present or is not mandatory (i.e., leave transparent,
1376% user specified, MNG BACK, PNG bKGD)
1377%
1378% Implement LOOP/ENDL [done, but could do discretionary loops more
1379% efficiently by linking in the duplicate frames.].
1380%
1381% Decode and act on the MHDR simplicity profile (offer option to reject
1382% files or attempt to process them anyway when the profile isn't LC or VLC).
1383%
1384% Upgrade to full MNG without Delta-PNG.
1385%
1386% o BACK [done a while ago except for background image ID]
1387% o MOVE [done 15 May 1999]
1388% o CLIP [done 15 May 1999]
1389% o DISC [done 19 May 1999]
1390% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1391% o SEEK [partially done 19 May 1999 (discard function only)]
1392% o SHOW
1393% o PAST
1394% o BASI
1395% o MNG-level tEXt/iTXt/zTXt
1396% o pHYg
1397% o pHYs
1398% o sBIT
1399% o bKGD
1400% o iTXt (wait for libpng implementation).
1401%
1402% Use the scene signature to discover when an identical scene is
1403% being reused, and just point to the original image->exception instead
1404% of storing another set of pixels. This not specific to MNG
1405% but could be applied generally.
1406%
1407% Upgrade to full MNG with Delta-PNG.
1408%
1409% JNG tEXt/iTXt/zTXt
1410%
1411% We will not attempt to read files containing the CgBI chunk.
1412% They are really Xcode files meant for display on the iPhone.
1413% These are not valid PNG files and it is impossible to recover
1414% the orginal PNG from files that have been converted to Xcode-PNG,
1415% since irretrievable loss of color data has occurred due to the
1416% use of premultiplied alpha.
1417*/
1418
1419#if defined(__cplusplus) || defined(c_plusplus)
1420extern "C" {
1421#endif
1422
1423/*
1424 This the function that does the actual reading of data. It is
1425 the same as the one supplied in libpng, except that it receives the
1426 datastream from the ReadBlob() function instead of standard input.
1427*/
1428static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1429{
1430 Image
1431 *image;
1432
1433 image=(Image *) png_get_io_ptr(png_ptr);
1434 if (length)
1435 {
1436 png_size_t
1437 check;
1438
1439 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1440 if (check != length)
1441 {
1442 char
1443 msg[MaxTextExtent];
1444
cristy3b6fd2e2011-05-20 12:53:50 +00001445 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001446 "Expected %.20g bytes; found %.20g bytes",(double) length,
1447 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001448 png_warning(png_ptr,msg);
1449 png_error(png_ptr,"Read Exception");
1450 }
1451 }
1452}
1453
1454#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1455 !defined(PNG_MNG_FEATURES_SUPPORTED)
1456/* We use mng_get_data() instead of png_get_data() if we have a libpng
1457 * older than libpng-1.0.3a, which was the first to allow the empty
1458 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1459 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1460 * encountered after an empty PLTE, so we have to look ahead for bKGD
1461 * chunks and remove them from the datastream that is passed to libpng,
1462 * and store their contents for later use.
1463 */
1464static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1465{
1466 MngInfo
1467 *mng_info;
1468
1469 Image
1470 *image;
1471
1472 png_size_t
1473 check;
1474
cristybb503372010-05-27 20:51:26 +00001475 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001476 i;
1477
1478 i=0;
1479 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1480 image=(Image *) mng_info->image;
1481 while (mng_info->bytes_in_read_buffer && length)
1482 {
1483 data[i]=mng_info->read_buffer[i];
1484 mng_info->bytes_in_read_buffer--;
1485 length--;
1486 i++;
1487 }
1488 if (length)
1489 {
1490 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001491
cristy3ed852e2009-09-05 21:47:34 +00001492 if (check != length)
1493 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001494
cristy3ed852e2009-09-05 21:47:34 +00001495 if (length == 4)
1496 {
1497 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1498 (data[3] == 0))
1499 {
1500 check=(png_size_t) ReadBlob(image,(size_t) length,
1501 (char *) mng_info->read_buffer);
1502 mng_info->read_buffer[4]=0;
1503 mng_info->bytes_in_read_buffer=4;
1504 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1505 mng_info->found_empty_plte=MagickTrue;
1506 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1507 {
1508 mng_info->found_empty_plte=MagickFalse;
1509 mng_info->have_saved_bkgd_index=MagickFalse;
1510 }
1511 }
glennrp0fe50b42010-11-16 03:52:51 +00001512
cristy3ed852e2009-09-05 21:47:34 +00001513 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1514 (data[3] == 1))
1515 {
1516 check=(png_size_t) ReadBlob(image,(size_t) length,
1517 (char *) mng_info->read_buffer);
1518 mng_info->read_buffer[4]=0;
1519 mng_info->bytes_in_read_buffer=4;
1520 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1521 if (mng_info->found_empty_plte)
1522 {
1523 /*
1524 Skip the bKGD data byte and CRC.
1525 */
1526 check=(png_size_t)
1527 ReadBlob(image,5,(char *) mng_info->read_buffer);
1528 check=(png_size_t) ReadBlob(image,(size_t) length,
1529 (char *) mng_info->read_buffer);
1530 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1531 mng_info->have_saved_bkgd_index=MagickTrue;
1532 mng_info->bytes_in_read_buffer=0;
1533 }
1534 }
1535 }
1536 }
1537}
1538#endif
1539
1540static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1541{
1542 Image
1543 *image;
1544
1545 image=(Image *) png_get_io_ptr(png_ptr);
1546 if (length)
1547 {
1548 png_size_t
1549 check;
1550
cristybb503372010-05-27 20:51:26 +00001551 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001552
cristy3ed852e2009-09-05 21:47:34 +00001553 if (check != length)
1554 png_error(png_ptr,"WriteBlob Failed");
1555 }
1556}
1557
1558static void png_flush_data(png_structp png_ptr)
1559{
1560 (void) png_ptr;
1561}
1562
1563#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1564static int PalettesAreEqual(Image *a,Image *b)
1565{
cristybb503372010-05-27 20:51:26 +00001566 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001567 i;
1568
1569 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1570 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001571
cristy3ed852e2009-09-05 21:47:34 +00001572 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1573 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001574
cristy3ed852e2009-09-05 21:47:34 +00001575 if (a->colors != b->colors)
1576 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001577
cristybb503372010-05-27 20:51:26 +00001578 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001579 {
1580 if ((a->colormap[i].red != b->colormap[i].red) ||
1581 (a->colormap[i].green != b->colormap[i].green) ||
1582 (a->colormap[i].blue != b->colormap[i].blue))
1583 return((int) MagickFalse);
1584 }
glennrp0fe50b42010-11-16 03:52:51 +00001585
cristy3ed852e2009-09-05 21:47:34 +00001586 return((int) MagickTrue);
1587}
1588#endif
1589
1590static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1591{
1592 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1593 mng_info->exists[i] && !mng_info->frozen[i])
1594 {
1595#ifdef MNG_OBJECT_BUFFERS
1596 if (mng_info->ob[i] != (MngBuffer *) NULL)
1597 {
1598 if (mng_info->ob[i]->reference_count > 0)
1599 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001600
cristy3ed852e2009-09-05 21:47:34 +00001601 if (mng_info->ob[i]->reference_count == 0)
1602 {
1603 if (mng_info->ob[i]->image != (Image *) NULL)
1604 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001605
cristy3ed852e2009-09-05 21:47:34 +00001606 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1607 }
1608 }
1609 mng_info->ob[i]=(MngBuffer *) NULL;
1610#endif
1611 mng_info->exists[i]=MagickFalse;
1612 mng_info->invisible[i]=MagickFalse;
1613 mng_info->viewable[i]=MagickFalse;
1614 mng_info->frozen[i]=MagickFalse;
1615 mng_info->x_off[i]=0;
1616 mng_info->y_off[i]=0;
1617 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001618 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001619 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001620 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001621 }
1622}
1623
glennrp21f0e622011-01-07 16:20:57 +00001624static void MngInfoFreeStruct(MngInfo *mng_info,
1625 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001626{
glennrp21f0e622011-01-07 16:20:57 +00001627 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001628 {
cristybb503372010-05-27 20:51:26 +00001629 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001630 i;
1631
1632 for (i=1; i < MNG_MAX_OBJECTS; i++)
1633 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001634
cristy3ed852e2009-09-05 21:47:34 +00001635 if (mng_info->global_plte != (png_colorp) NULL)
1636 mng_info->global_plte=(png_colorp)
1637 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001638
cristy3ed852e2009-09-05 21:47:34 +00001639 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1640 *have_mng_structure=MagickFalse;
1641 }
1642}
1643
1644static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1645{
1646 MngBox
1647 box;
1648
1649 box=box1;
1650 if (box.left < box2.left)
1651 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001652
cristy3ed852e2009-09-05 21:47:34 +00001653 if (box.top < box2.top)
1654 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001655
cristy3ed852e2009-09-05 21:47:34 +00001656 if (box.right > box2.right)
1657 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001658
cristy3ed852e2009-09-05 21:47:34 +00001659 if (box.bottom > box2.bottom)
1660 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001661
cristy3ed852e2009-09-05 21:47:34 +00001662 return box;
1663}
1664
1665static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1666{
1667 MngBox
1668 box;
1669
1670 /*
1671 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1672 */
cristybb503372010-05-27 20:51:26 +00001673 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1674 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1675 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1676 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001677 if (delta_type != 0)
1678 {
1679 box.left+=previous_box.left;
1680 box.right+=previous_box.right;
1681 box.top+=previous_box.top;
1682 box.bottom+=previous_box.bottom;
1683 }
glennrp0fe50b42010-11-16 03:52:51 +00001684
cristy3ed852e2009-09-05 21:47:34 +00001685 return(box);
1686}
1687
1688static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1689 unsigned char *p)
1690{
1691 MngPair
1692 pair;
1693 /*
cristybb503372010-05-27 20:51:26 +00001694 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001695 */
cristy8182b072010-05-30 20:10:53 +00001696 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1697 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001698
cristy3ed852e2009-09-05 21:47:34 +00001699 if (delta_type != 0)
1700 {
1701 pair.a+=previous_pair.a;
1702 pair.b+=previous_pair.b;
1703 }
glennrp0fe50b42010-11-16 03:52:51 +00001704
cristy3ed852e2009-09-05 21:47:34 +00001705 return(pair);
1706}
1707
cristy8182b072010-05-30 20:10:53 +00001708static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001709{
cristy8182b072010-05-30 20:10:53 +00001710 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001711}
1712
glennrpcf002022011-01-30 02:38:15 +00001713static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001714{
1715 Image
1716 *image;
1717
1718 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001719
cristy3ed852e2009-09-05 21:47:34 +00001720 if (image->debug != MagickFalse)
1721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1722 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001723
cristy3ed852e2009-09-05 21:47:34 +00001724 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1725 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001726
glennrpe4017e32011-01-08 17:16:09 +00001727#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001728 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1729 * are building with libpng-1.4.x and can be ignored.
1730 */
cristy3ed852e2009-09-05 21:47:34 +00001731 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001732#else
1733 png_longjmp(ping,1);
1734#endif
cristy3ed852e2009-09-05 21:47:34 +00001735}
1736
glennrpcf002022011-01-30 02:38:15 +00001737static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001738{
1739 Image
1740 *image;
1741
1742 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1743 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001744
cristy3ed852e2009-09-05 21:47:34 +00001745 image=(Image *) png_get_error_ptr(ping);
1746 if (image->debug != MagickFalse)
1747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001748 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001749
cristy3ed852e2009-09-05 21:47:34 +00001750 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1751 message,"`%s'",image->filename);
1752}
1753
1754#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001755static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001756{
1757#if (PNG_LIBPNG_VER < 10011)
1758 png_voidp
1759 ret;
1760
1761 png_ptr=png_ptr;
1762 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001763
cristy3ed852e2009-09-05 21:47:34 +00001764 if (ret == NULL)
1765 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001766
cristy3ed852e2009-09-05 21:47:34 +00001767 return(ret);
1768#else
1769 png_ptr=png_ptr;
1770 return((png_voidp) AcquireMagickMemory((size_t) size));
1771#endif
1772}
1773
1774/*
1775 Free a pointer. It is removed from the list at the same time.
1776*/
glennrpcf002022011-01-30 02:38:15 +00001777static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001778{
1779 png_ptr=png_ptr;
1780 ptr=RelinquishMagickMemory(ptr);
1781 return((png_free_ptr) NULL);
1782}
1783#endif
1784
1785#if defined(__cplusplus) || defined(c_plusplus)
1786}
1787#endif
1788
1789static int
glennrpcf002022011-01-30 02:38:15 +00001790Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001791 png_textp text,int ii)
1792{
cristybb503372010-05-27 20:51:26 +00001793 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001794 i;
1795
1796 register unsigned char
1797 *dp;
1798
1799 register png_charp
1800 sp;
1801
1802 png_uint_32
1803 length,
1804 nibbles;
1805
1806 StringInfo
1807 *profile;
1808
glennrp0c3e06b2010-11-19 13:45:02 +00001809 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001810 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1811 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1812 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1813 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,10,11,12,
1815 13,14,15};
1816
1817 sp=text[ii].text+1;
1818 /* look for newline */
1819 while (*sp != '\n')
1820 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001821
cristy3ed852e2009-09-05 21:47:34 +00001822 /* look for length */
1823 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1824 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001825
cristyf2f27272009-12-17 14:48:46 +00001826 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001827
glennrp97f90e22011-02-22 05:47:58 +00001828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1829 " length: %lu",(unsigned long) length);
1830
cristy3ed852e2009-09-05 21:47:34 +00001831 while (*sp != ' ' && *sp != '\n')
1832 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001833
cristy3ed852e2009-09-05 21:47:34 +00001834 /* allocate space */
1835 if (length == 0)
1836 {
1837 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1838 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1839 return(MagickFalse);
1840 }
glennrp0fe50b42010-11-16 03:52:51 +00001841
cristy3ed852e2009-09-05 21:47:34 +00001842 profile=AcquireStringInfo(length);
glennrp0fe50b42010-11-16 03:52:51 +00001843
cristy3ed852e2009-09-05 21:47:34 +00001844 if (profile == (StringInfo *) NULL)
1845 {
1846 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1847 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1848 "unable to copy profile");
1849 return(MagickFalse);
1850 }
glennrp0fe50b42010-11-16 03:52:51 +00001851
cristy3ed852e2009-09-05 21:47:34 +00001852 /* copy profile, skipping white space and column 1 "=" signs */
1853 dp=GetStringInfoDatum(profile);
1854 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001855
cristybb503372010-05-27 20:51:26 +00001856 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001857 {
1858 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1859 {
1860 if (*sp == '\0')
1861 {
1862 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1863 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1864 profile=DestroyStringInfo(profile);
1865 return(MagickFalse);
1866 }
1867 sp++;
1868 }
glennrp0fe50b42010-11-16 03:52:51 +00001869
cristy3ed852e2009-09-05 21:47:34 +00001870 if (i%2 == 0)
1871 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001872
cristy3ed852e2009-09-05 21:47:34 +00001873 else
1874 (*dp++)+=unhex[(int) *sp++];
1875 }
1876 /*
1877 We have already read "Raw profile type.
1878 */
1879 (void) SetImageProfile(image,&text[ii].key[17],profile);
1880 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001881
cristy3ed852e2009-09-05 21:47:34 +00001882 if (image_info->verbose)
1883 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001884
cristy3ed852e2009-09-05 21:47:34 +00001885 return MagickTrue;
1886}
1887
1888#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1889static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1890{
1891 Image
1892 *image;
1893
1894
1895 /* The unknown chunk structure contains the chunk data:
1896 png_byte name[5];
1897 png_byte *data;
1898 png_size_t size;
1899
1900 Note that libpng has already taken care of the CRC handling.
1901 */
1902
1903
1904 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1905 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1906 return(0); /* Did not recognize */
1907
1908 /* recognized vpAg */
1909
1910 if (chunk->size != 9)
1911 return(-1); /* Error return */
1912
1913 if (chunk->data[8] != 0)
1914 return(0); /* ImageMagick requires pixel units */
1915
1916 image=(Image *) png_get_user_chunk_ptr(ping);
1917
cristybb503372010-05-27 20:51:26 +00001918 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001919 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001920
cristybb503372010-05-27 20:51:26 +00001921 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001922 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1923
1924 /* Return one of the following: */
1925 /* return(-n); chunk had an error */
1926 /* return(0); did not recognize */
1927 /* return(n); success */
1928
1929 return(1);
1930
1931}
1932#endif
1933
1934/*
1935%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1936% %
1937% %
1938% %
1939% R e a d O n e P N G I m a g e %
1940% %
1941% %
1942% %
1943%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1944%
1945% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1946% (minus the 8-byte signature) and returns it. It allocates the memory
1947% necessary for the new Image structure and returns a pointer to the new
1948% image.
1949%
1950% The format of the ReadOnePNGImage method is:
1951%
1952% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1953% ExceptionInfo *exception)
1954%
1955% A description of each parameter follows:
1956%
1957% o mng_info: Specifies a pointer to a MngInfo structure.
1958%
1959% o image_info: the image info.
1960%
1961% o exception: return any errors or warnings in this structure.
1962%
1963*/
1964static Image *ReadOnePNGImage(MngInfo *mng_info,
1965 const ImageInfo *image_info, ExceptionInfo *exception)
1966{
1967 /* Read one PNG image */
1968
glennrpcc95c3f2011-04-18 16:46:48 +00001969 /* To do: Read the tIME chunk into the date:modify property */
1970 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1971
cristy3ed852e2009-09-05 21:47:34 +00001972 Image
1973 *image;
1974
1975 int
glennrp4eb39312011-03-30 21:34:55 +00001976 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001977 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001978 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001979 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001980 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001981 pass,
1982 ping_bit_depth,
1983 ping_color_type,
1984 ping_interlace_method,
1985 ping_compression_method,
1986 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001987 ping_num_trans,
1988 unit_type;
1989
1990 double
1991 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00001992
glennrpa6a06632011-01-19 15:15:34 +00001993 LongPixelPacket
1994 transparent_color;
1995
cristy3ed852e2009-09-05 21:47:34 +00001996 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00001997 logging,
cristy3ed852e2009-09-05 21:47:34 +00001998 status;
1999
glennrpfaa852b2010-03-30 12:17:00 +00002000 png_bytep
2001 ping_trans_alpha;
2002
2003 png_color_16p
2004 ping_background,
2005 ping_trans_color;
2006
cristy3ed852e2009-09-05 21:47:34 +00002007 png_info
2008 *end_info,
2009 *ping_info;
2010
2011 png_struct
2012 *ping;
2013
2014 png_textp
2015 text;
2016
glennrpfaa852b2010-03-30 12:17:00 +00002017 png_uint_32
2018 ping_height,
2019 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002020 ping_rowbytes,
2021 x_resolution,
2022 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002023
cristy3ed852e2009-09-05 21:47:34 +00002024 QuantumInfo
2025 *quantum_info;
2026
2027 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002028 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002029
cristybb503372010-05-27 20:51:26 +00002030 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002031 y;
2032
2033 register unsigned char
2034 *p;
2035
2036 register IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00002037 *indexes;
cristy3ed852e2009-09-05 21:47:34 +00002038
cristybb503372010-05-27 20:51:26 +00002039 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002040 i,
2041 x;
2042
2043 register PixelPacket
2044 *q;
2045
2046 size_t
glennrp39992b42010-11-14 00:03:43 +00002047 length,
cristy3ed852e2009-09-05 21:47:34 +00002048 row_offset;
2049
cristyeb3b22a2011-03-31 20:16:11 +00002050 ssize_t
2051 j;
2052
cristy3ed852e2009-09-05 21:47:34 +00002053#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2054 png_byte unused_chunks[]=
2055 {
2056 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2057 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2058 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2059 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2060 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2061 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2062 };
2063#endif
2064
2065 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002066 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002067
2068#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002069 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002070#endif
2071
glennrp25c1e2b2010-03-25 01:39:56 +00002072#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002073 if (image_info->verbose)
2074 printf("Your PNG library (libpng-%s) is rather old.\n",
2075 PNG_LIBPNG_VER_STRING);
2076#endif
2077
glennrp61b4c952009-11-10 20:40:41 +00002078#if (PNG_LIBPNG_VER >= 10400)
2079# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2080 if (image_info->verbose)
2081 {
2082 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2083 PNG_LIBPNG_VER_STRING);
2084 printf("Please update it.\n");
2085 }
2086# endif
2087#endif
2088
2089
cristyed552522009-10-16 14:04:35 +00002090 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002091 image=mng_info->image;
2092
glennrpa6a06632011-01-19 15:15:34 +00002093 if (logging != MagickFalse)
2094 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2095 " image->matte=%d",(int) image->matte);
2096
glennrp0e319732011-01-25 21:53:13 +00002097 /* Set to an out-of-range color unless tRNS chunk is present */
2098 transparent_color.red=65537;
2099 transparent_color.green=65537;
2100 transparent_color.blue=65537;
2101 transparent_color.opacity=65537;
2102
glennrpcb395ac2011-03-30 19:50:23 +00002103 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002104 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002105 num_raw_profiles = 0;
2106
cristy3ed852e2009-09-05 21:47:34 +00002107 /*
2108 Allocate the PNG structures
2109 */
2110#ifdef PNG_USER_MEM_SUPPORTED
2111 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00002112 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2113 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002114#else
2115 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00002116 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002117#endif
2118 if (ping == (png_struct *) NULL)
2119 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002120
cristy3ed852e2009-09-05 21:47:34 +00002121 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002122
cristy3ed852e2009-09-05 21:47:34 +00002123 if (ping_info == (png_info *) NULL)
2124 {
2125 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2126 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2127 }
glennrp0fe50b42010-11-16 03:52:51 +00002128
cristy3ed852e2009-09-05 21:47:34 +00002129 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002130
cristy3ed852e2009-09-05 21:47:34 +00002131 if (end_info == (png_info *) NULL)
2132 {
2133 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2134 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2135 }
glennrp0fe50b42010-11-16 03:52:51 +00002136
glennrpcf002022011-01-30 02:38:15 +00002137 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002138
glennrpfaa852b2010-03-30 12:17:00 +00002139 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002140 {
2141 /*
2142 PNG image is corrupt.
2143 */
2144 png_destroy_read_struct(&ping,&ping_info,&end_info);
2145#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002146 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002147#endif
2148 if (logging != MagickFalse)
2149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2150 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002151
cristy3ed852e2009-09-05 21:47:34 +00002152 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002153 {
2154 InheritException(exception,&image->exception);
2155 image->columns=0;
2156 }
glennrp0fe50b42010-11-16 03:52:51 +00002157
cristy3ed852e2009-09-05 21:47:34 +00002158 return(GetFirstImageInList(image));
2159 }
2160 /*
2161 Prepare PNG for reading.
2162 */
glennrpfaa852b2010-03-30 12:17:00 +00002163
cristy3ed852e2009-09-05 21:47:34 +00002164 mng_info->image_found++;
2165 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002166
cristy3ed852e2009-09-05 21:47:34 +00002167 if (LocaleCompare(image_info->magick,"MNG") == 0)
2168 {
2169#if defined(PNG_MNG_FEATURES_SUPPORTED)
2170 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2171 png_set_read_fn(ping,image,png_get_data);
2172#else
2173#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2174 png_permit_empty_plte(ping,MagickTrue);
2175 png_set_read_fn(ping,image,png_get_data);
2176#else
2177 mng_info->image=image;
2178 mng_info->bytes_in_read_buffer=0;
2179 mng_info->found_empty_plte=MagickFalse;
2180 mng_info->have_saved_bkgd_index=MagickFalse;
2181 png_set_read_fn(ping,mng_info,mng_get_data);
2182#endif
2183#endif
2184 }
glennrp0fe50b42010-11-16 03:52:51 +00002185
cristy3ed852e2009-09-05 21:47:34 +00002186 else
2187 png_set_read_fn(ping,image,png_get_data);
2188
2189#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2190 /* Ignore unused chunks and all unknown chunks except for vpAg */
2191 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2192 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2193 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2194 (int)sizeof(unused_chunks)/5);
2195 /* Callback for other unknown chunks */
2196 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2197#endif
2198
glennrp991e92a2010-01-28 03:09:00 +00002199#if (PNG_LIBPNG_VER < 10400)
2200# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2201 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002202 /* Disable thread-unsafe features of pnggccrd */
2203 if (png_access_version_number() >= 10200)
2204 {
2205 png_uint_32 mmx_disable_mask=0;
2206 png_uint_32 asm_flags;
2207
2208 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2209 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2210 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2211 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2212 asm_flags=png_get_asm_flags(ping);
2213 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2214 }
glennrp991e92a2010-01-28 03:09:00 +00002215# endif
cristy3ed852e2009-09-05 21:47:34 +00002216#endif
2217
2218 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002219
2220 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2221 &ping_bit_depth,&ping_color_type,
2222 &ping_interlace_method,&ping_compression_method,
2223 &ping_filter_method);
2224
2225 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2226 &ping_trans_color);
2227
2228 (void) png_get_bKGD(ping, ping_info, &ping_background);
2229
2230 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002231 {
glennrpfaa852b2010-03-30 12:17:00 +00002232 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2233 {
2234 png_set_packing(ping);
2235 ping_bit_depth = 8;
2236 }
cristy3ed852e2009-09-05 21:47:34 +00002237 }
glennrpfaa852b2010-03-30 12:17:00 +00002238
2239 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002240 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002241 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002242 if (logging != MagickFalse)
2243 {
2244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002245 " PNG width: %.20g, height: %.20g",
2246 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002247
cristy3ed852e2009-09-05 21:47:34 +00002248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2249 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002250 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002251
cristy3ed852e2009-09-05 21:47:34 +00002252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2253 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002254 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002255
cristy3ed852e2009-09-05 21:47:34 +00002256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2257 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002258 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002259 }
2260
glennrpfaa852b2010-03-30 12:17:00 +00002261#ifdef PNG_READ_iCCP_SUPPORTED
2262 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002263 {
2264 int
2265 compression;
2266
glennrpe4017e32011-01-08 17:16:09 +00002267#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002268 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002269 info;
2270#else
2271 png_bytep
2272 info;
2273#endif
2274
2275 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002276 name;
2277
2278 png_uint_32
2279 profile_length;
2280
2281 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2282 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002283
cristy3ed852e2009-09-05 21:47:34 +00002284 if (profile_length != 0)
2285 {
2286 StringInfo
2287 *profile;
2288
2289 if (logging != MagickFalse)
2290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2291 " Reading PNG iCCP chunk.");
2292 profile=AcquireStringInfo(profile_length);
2293 SetStringInfoDatum(profile,(const unsigned char *) info);
2294 (void) SetImageProfile(image,"icc",profile);
2295 profile=DestroyStringInfo(profile);
2296 }
2297 }
2298#endif
2299#if defined(PNG_READ_sRGB_SUPPORTED)
2300 {
cristy3ed852e2009-09-05 21:47:34 +00002301 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002302 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2303 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002304
cristy3ed852e2009-09-05 21:47:34 +00002305 if (png_get_sRGB(ping,ping_info,&intent))
2306 {
glennrpcf002022011-01-30 02:38:15 +00002307 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2308 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002309
cristy3ed852e2009-09-05 21:47:34 +00002310 if (logging != MagickFalse)
2311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002312 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002313 }
2314 }
2315#endif
2316 {
glennrpfaa852b2010-03-30 12:17:00 +00002317 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2318 if (mng_info->have_global_gama)
2319 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002320
cristy3ed852e2009-09-05 21:47:34 +00002321 if (png_get_gAMA(ping,ping_info,&file_gamma))
2322 {
2323 image->gamma=(float) file_gamma;
2324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2326 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2327 }
2328 }
glennrpfaa852b2010-03-30 12:17:00 +00002329 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2330 {
2331 if (mng_info->have_global_chrm != MagickFalse)
2332 {
2333 (void) png_set_cHRM(ping,ping_info,
2334 mng_info->global_chrm.white_point.x,
2335 mng_info->global_chrm.white_point.y,
2336 mng_info->global_chrm.red_primary.x,
2337 mng_info->global_chrm.red_primary.y,
2338 mng_info->global_chrm.green_primary.x,
2339 mng_info->global_chrm.green_primary.y,
2340 mng_info->global_chrm.blue_primary.x,
2341 mng_info->global_chrm.blue_primary.y);
2342 }
2343 }
glennrp0fe50b42010-11-16 03:52:51 +00002344
glennrpfaa852b2010-03-30 12:17:00 +00002345 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002346 {
2347 (void) png_get_cHRM(ping,ping_info,
2348 &image->chromaticity.white_point.x,
2349 &image->chromaticity.white_point.y,
2350 &image->chromaticity.red_primary.x,
2351 &image->chromaticity.red_primary.y,
2352 &image->chromaticity.green_primary.x,
2353 &image->chromaticity.green_primary.y,
2354 &image->chromaticity.blue_primary.x,
2355 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002356
cristy3ed852e2009-09-05 21:47:34 +00002357 if (logging != MagickFalse)
2358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2359 " Reading PNG cHRM chunk.");
2360 }
glennrp0fe50b42010-11-16 03:52:51 +00002361
glennrpe610a072010-08-05 17:08:46 +00002362 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002363 {
glennrpe610a072010-08-05 17:08:46 +00002364 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002365 Magick_RenderingIntent_to_PNG_RenderingIntent
2366 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002367 png_set_gAMA(ping,ping_info,0.45455f);
2368 png_set_cHRM(ping,ping_info,
2369 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2370 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002371 }
cristy3ed852e2009-09-05 21:47:34 +00002372#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002373 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002374 {
cristy905ef802011-02-23 00:29:18 +00002375 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2376 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002377
cristy3ed852e2009-09-05 21:47:34 +00002378 if (logging != MagickFalse)
2379 if (image->page.x || image->page.y)
2380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002381 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2382 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002383 }
2384#endif
2385#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002386 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2387 {
2388 if (mng_info->have_global_phys)
2389 {
2390 png_set_pHYs(ping,ping_info,
2391 mng_info->global_x_pixels_per_unit,
2392 mng_info->global_y_pixels_per_unit,
2393 mng_info->global_phys_unit_type);
2394 }
2395 }
2396
2397 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002398 {
cristy3ed852e2009-09-05 21:47:34 +00002399 /*
2400 Set image resolution.
2401 */
2402 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002403 &unit_type);
2404 image->x_resolution=(double) x_resolution;
2405 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002406
cristy3ed852e2009-09-05 21:47:34 +00002407 if (unit_type == PNG_RESOLUTION_METER)
2408 {
2409 image->units=PixelsPerCentimeterResolution;
2410 image->x_resolution=(double) x_resolution/100.0;
2411 image->y_resolution=(double) y_resolution/100.0;
2412 }
glennrp0fe50b42010-11-16 03:52:51 +00002413
cristy3ed852e2009-09-05 21:47:34 +00002414 if (logging != MagickFalse)
2415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002416 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2417 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002418 }
cristy3ed852e2009-09-05 21:47:34 +00002419#endif
glennrp823b55c2011-03-14 18:46:46 +00002420
glennrpfaa852b2010-03-30 12:17:00 +00002421 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002422 {
2423 int
2424 number_colors;
2425
2426 png_colorp
2427 palette;
2428
2429 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002430
cristy3ed852e2009-09-05 21:47:34 +00002431 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002432 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002433 {
2434 if (mng_info->global_plte_length)
2435 {
2436 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2437 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002438
glennrpfaa852b2010-03-30 12:17:00 +00002439 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002440 if (mng_info->global_trns_length)
2441 {
2442 if (mng_info->global_trns_length >
2443 mng_info->global_plte_length)
2444 (void) ThrowMagickException(&image->exception,
2445 GetMagickModule(),CoderError,
2446 "global tRNS has more entries than global PLTE",
2447 "`%s'",image_info->filename);
2448 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2449 (int) mng_info->global_trns_length,NULL);
2450 }
glennrpbfd9e612011-04-22 14:02:20 +00002451#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002452 if (
2453#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2454 mng_info->have_saved_bkgd_index ||
2455#endif
glennrpfaa852b2010-03-30 12:17:00 +00002456 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002457 {
2458 png_color_16
2459 background;
2460
2461#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2462 if (mng_info->have_saved_bkgd_index)
2463 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002464#endif
glennrpfaa852b2010-03-30 12:17:00 +00002465 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2466 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002467
cristy3ed852e2009-09-05 21:47:34 +00002468 background.red=(png_uint_16)
2469 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002470
cristy3ed852e2009-09-05 21:47:34 +00002471 background.green=(png_uint_16)
2472 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002473
cristy3ed852e2009-09-05 21:47:34 +00002474 background.blue=(png_uint_16)
2475 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002476
glennrpc6c391a2011-04-27 02:23:56 +00002477 background.gray=(png_uint_16)
2478 mng_info->global_plte[background.index].green;
2479
cristy3ed852e2009-09-05 21:47:34 +00002480 png_set_bKGD(ping,ping_info,&background);
2481 }
2482#endif
2483 }
2484 else
2485 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2486 CoderError,"No global PLTE in file","`%s'",
2487 image_info->filename);
2488 }
2489 }
2490
glennrpbfd9e612011-04-22 14:02:20 +00002491#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002492 if (mng_info->have_global_bkgd &&
2493 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002494 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002495
glennrpfaa852b2010-03-30 12:17:00 +00002496 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002497 {
glennrpbfd9e612011-04-22 14:02:20 +00002498 unsigned int
2499 bkgd_scale;
2500
cristy3ed852e2009-09-05 21:47:34 +00002501 /*
2502 Set image background color.
2503 */
2504 if (logging != MagickFalse)
2505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2506 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002507
glennrpbfd9e612011-04-22 14:02:20 +00002508 /* Scale background components to 16-bit, then scale
2509 * to quantum depth
2510 */
2511 if (logging != MagickFalse)
2512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2513 " raw ping_background=(%d,%d,%d).",ping_background->red,
2514 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002515
glennrpbfd9e612011-04-22 14:02:20 +00002516 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002517
glennrpbfd9e612011-04-22 14:02:20 +00002518 if (ping_bit_depth == 1)
2519 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002520
glennrpbfd9e612011-04-22 14:02:20 +00002521 else if (ping_bit_depth == 2)
2522 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002523
glennrpbfd9e612011-04-22 14:02:20 +00002524 else if (ping_bit_depth == 4)
2525 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002526
glennrpbfd9e612011-04-22 14:02:20 +00002527 if (ping_bit_depth <= 8)
2528 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002529
glennrpbfd9e612011-04-22 14:02:20 +00002530 ping_background->red *= bkgd_scale;
2531 ping_background->green *= bkgd_scale;
2532 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002533
glennrpbfd9e612011-04-22 14:02:20 +00002534 if (logging != MagickFalse)
2535 {
glennrp2cbb4482010-06-02 04:37:24 +00002536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2537 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002538
glennrp2cbb4482010-06-02 04:37:24 +00002539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2540 " ping_background=(%d,%d,%d).",ping_background->red,
2541 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002542 }
glennrp2cbb4482010-06-02 04:37:24 +00002543
glennrpbfd9e612011-04-22 14:02:20 +00002544 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002545 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002546
glennrpbfd9e612011-04-22 14:02:20 +00002547 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002548 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002549
glennrpbfd9e612011-04-22 14:02:20 +00002550 image->background_color.blue=
2551 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002552
glennrpbfd9e612011-04-22 14:02:20 +00002553 image->background_color.opacity=OpaqueOpacity;
glennrp2cbb4482010-06-02 04:37:24 +00002554
glennrpbfd9e612011-04-22 14:02:20 +00002555 if (logging != MagickFalse)
2556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2557 " image->background_color=(%.20g,%.20g,%.20g).",
2558 (double) image->background_color.red,
2559 (double) image->background_color.green,
2560 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002561 }
glennrpbfd9e612011-04-22 14:02:20 +00002562#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002563
glennrpfaa852b2010-03-30 12:17:00 +00002564 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002565 {
2566 /*
glennrpa6a06632011-01-19 15:15:34 +00002567 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002568 */
2569 int
2570 max_sample;
2571
cristy35ef8242010-06-03 16:24:13 +00002572 size_t
2573 one=1;
2574
cristy3ed852e2009-09-05 21:47:34 +00002575 if (logging != MagickFalse)
2576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2577 " Reading PNG tRNS chunk.");
2578
cristyf9cca6a2010-06-04 23:49:28 +00002579 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002580
glennrpfaa852b2010-03-30 12:17:00 +00002581 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2582 (int)ping_trans_color->gray > max_sample) ||
2583 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2584 ((int)ping_trans_color->red > max_sample ||
2585 (int)ping_trans_color->green > max_sample ||
2586 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002587 {
2588 if (logging != MagickFalse)
2589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2590 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002591 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002592 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002593 image->matte=MagickFalse;
2594 }
2595 else
2596 {
glennrpa6a06632011-01-19 15:15:34 +00002597 int
2598 scale_to_short;
2599
2600 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2601
2602 /* Scale transparent_color to short */
2603 transparent_color.red= scale_to_short*ping_trans_color->red;
2604 transparent_color.green= scale_to_short*ping_trans_color->green;
2605 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2606 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002607
glennrpfaa852b2010-03-30 12:17:00 +00002608 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002609 {
glennrp0f111982010-07-07 20:18:33 +00002610 if (logging != MagickFalse)
2611 {
2612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2613 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002614
glennrp0f111982010-07-07 20:18:33 +00002615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2616 " scaled graylevel is %d.",transparent_color.opacity);
2617 }
cristy3ed852e2009-09-05 21:47:34 +00002618 transparent_color.red=transparent_color.opacity;
2619 transparent_color.green=transparent_color.opacity;
2620 transparent_color.blue=transparent_color.opacity;
2621 }
2622 }
2623 }
2624#if defined(PNG_READ_sBIT_SUPPORTED)
2625 if (mng_info->have_global_sbit)
2626 {
glennrpfaa852b2010-03-30 12:17:00 +00002627 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002628 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2629 }
2630#endif
2631 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002632
cristy3ed852e2009-09-05 21:47:34 +00002633 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002634
2635 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2636
cristy3ed852e2009-09-05 21:47:34 +00002637 /*
2638 Initialize image structure.
2639 */
2640 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002641 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002642 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002643 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002644 if (mng_info->mng_type == 0)
2645 {
glennrpfaa852b2010-03-30 12:17:00 +00002646 mng_info->mng_width=ping_width;
2647 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002648 mng_info->frame=mng_info->image_box;
2649 mng_info->clip=mng_info->image_box;
2650 }
glennrp0fe50b42010-11-16 03:52:51 +00002651
cristy3ed852e2009-09-05 21:47:34 +00002652 else
2653 {
2654 image->page.y=mng_info->y_off[mng_info->object_id];
2655 }
glennrp0fe50b42010-11-16 03:52:51 +00002656
cristy3ed852e2009-09-05 21:47:34 +00002657 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002658 image->columns=ping_width;
2659 image->rows=ping_height;
2660 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002661 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002662 {
cristybefe4d22010-06-07 01:18:58 +00002663 size_t
2664 one;
2665
cristy3ed852e2009-09-05 21:47:34 +00002666 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002667 one=1;
2668 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002669#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2670 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002671 image->colors=256;
2672#else
2673 if (image->colors > 65536L)
2674 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002675#endif
glennrpfaa852b2010-03-30 12:17:00 +00002676 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002677 {
2678 int
2679 number_colors;
2680
2681 png_colorp
2682 palette;
2683
2684 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002685 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002686
cristy3ed852e2009-09-05 21:47:34 +00002687 if (logging != MagickFalse)
2688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2689 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2690 }
2691 }
2692
2693 if (image->storage_class == PseudoClass)
2694 {
2695 /*
2696 Initialize image colormap.
2697 */
2698 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2699 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002700
glennrpfaa852b2010-03-30 12:17:00 +00002701 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002702 {
2703 int
2704 number_colors;
2705
2706 png_colorp
2707 palette;
2708
2709 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002710
glennrp6af6cf12011-04-22 13:05:16 +00002711 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002712 {
2713 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2714 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2715 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2716 }
glennrp6af6cf12011-04-22 13:05:16 +00002717
glennrp67b9c1a2011-04-22 18:47:36 +00002718 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002719 {
2720 image->colormap[i].red=0;
2721 image->colormap[i].green=0;
2722 image->colormap[i].blue=0;
2723 }
cristy3ed852e2009-09-05 21:47:34 +00002724 }
glennrp0fe50b42010-11-16 03:52:51 +00002725
cristy3ed852e2009-09-05 21:47:34 +00002726 else
2727 {
cristybb503372010-05-27 20:51:26 +00002728 size_t
cristy3ed852e2009-09-05 21:47:34 +00002729 scale;
2730
glennrpfaa852b2010-03-30 12:17:00 +00002731 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002732
cristy3ed852e2009-09-05 21:47:34 +00002733 if (scale < 1)
2734 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002735
cristybb503372010-05-27 20:51:26 +00002736 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002737 {
2738 image->colormap[i].red=(Quantum) (i*scale);
2739 image->colormap[i].green=(Quantum) (i*scale);
2740 image->colormap[i].blue=(Quantum) (i*scale);
2741 }
2742 }
2743 }
glennrp147bc912011-03-30 18:47:21 +00002744
glennrpcb395ac2011-03-30 19:50:23 +00002745 /* Set some properties for reporting by "identify" */
2746 {
glennrp147bc912011-03-30 18:47:21 +00002747 char
2748 msg[MaxTextExtent];
2749
2750 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2751 ping_interlace_method in value */
2752
cristy3b6fd2e2011-05-20 12:53:50 +00002753 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002754 "%d, %d",(int) ping_width, (int) ping_height);
2755 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002756
cristy3b6fd2e2011-05-20 12:53:50 +00002757 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
glennrp147bc912011-03-30 18:47:21 +00002758 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2759
cristy3b6fd2e2011-05-20 12:53:50 +00002760 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
glennrp147bc912011-03-30 18:47:21 +00002761 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2762
cristy3b6fd2e2011-05-20 12:53:50 +00002763 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002764 (int) ping_interlace_method);
2765 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002766 }
glennrp147bc912011-03-30 18:47:21 +00002767
cristy3ed852e2009-09-05 21:47:34 +00002768 /*
2769 Read image scanlines.
2770 */
2771 if (image->delay != 0)
2772 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002773
glennrp0ca69b12010-07-26 01:57:52 +00002774 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002775 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2776 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002777 {
2778 if (logging != MagickFalse)
2779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002780 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002781 mng_info->scenes_found-1);
2782 png_destroy_read_struct(&ping,&ping_info,&end_info);
2783#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002784 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002785#endif
2786 if (logging != MagickFalse)
2787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2788 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002789
cristy3ed852e2009-09-05 21:47:34 +00002790 return(image);
2791 }
glennrp0fe50b42010-11-16 03:52:51 +00002792
cristy3ed852e2009-09-05 21:47:34 +00002793 if (logging != MagickFalse)
2794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2795 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002796
cristy3ed852e2009-09-05 21:47:34 +00002797 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002798 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2799 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002800
cristy3ed852e2009-09-05 21:47:34 +00002801 else
glennrpcf002022011-01-30 02:38:15 +00002802 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2803 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002804
glennrpcf002022011-01-30 02:38:15 +00002805 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002806 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002807
cristy3ed852e2009-09-05 21:47:34 +00002808 if (logging != MagickFalse)
2809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2810 " Converting PNG pixels to pixel packets");
2811 /*
2812 Convert PNG pixels to pixel packets.
2813 */
glennrpfaa852b2010-03-30 12:17:00 +00002814 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002815 {
2816 /*
2817 PNG image is corrupt.
2818 */
2819 png_destroy_read_struct(&ping,&ping_info,&end_info);
2820#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002821 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002822#endif
2823 if (quantum_info != (QuantumInfo *) NULL)
2824 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002825
glennrpcf002022011-01-30 02:38:15 +00002826 if (ping_pixels != (unsigned char *) NULL)
2827 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002828
cristy3ed852e2009-09-05 21:47:34 +00002829 if (logging != MagickFalse)
2830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2831 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002832
cristy3ed852e2009-09-05 21:47:34 +00002833 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002834 {
2835 InheritException(exception,&image->exception);
2836 image->columns=0;
2837 }
glennrp0fe50b42010-11-16 03:52:51 +00002838
cristy3ed852e2009-09-05 21:47:34 +00002839 return(GetFirstImageInList(image));
2840 }
glennrp0fe50b42010-11-16 03:52:51 +00002841
cristyed552522009-10-16 14:04:35 +00002842 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002843
cristyed552522009-10-16 14:04:35 +00002844 if (quantum_info == (QuantumInfo *) NULL)
2845 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002846
glennrpc8cbc5d2011-01-01 00:12:34 +00002847 {
2848
2849 MagickBooleanType
2850 found_transparent_pixel;
2851
2852 found_transparent_pixel=MagickFalse;
2853
cristy3ed852e2009-09-05 21:47:34 +00002854 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002855 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002856 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002857 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002858 /*
2859 Convert image to DirectClass pixel packets.
2860 */
glennrp67b9c1a2011-04-22 18:47:36 +00002861#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2862 int
2863 depth;
2864
2865 depth=(ssize_t) ping_bit_depth;
2866#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002867 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2868 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2869 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2870 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002871
glennrpc8cbc5d2011-01-01 00:12:34 +00002872 for (y=0; y < (ssize_t) image->rows; y++)
2873 {
2874 if (num_passes > 1)
2875 row_offset=ping_rowbytes*y;
2876
2877 else
2878 row_offset=0;
2879
glennrpcf002022011-01-30 02:38:15 +00002880 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002881 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2882
2883 if (q == (PixelPacket *) NULL)
2884 break;
2885
glennrpc8cbc5d2011-01-01 00:12:34 +00002886 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2887 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002888 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002889
2890 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2891 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002892 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002893
2894 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2895 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002896 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002897
2898 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2899 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002900 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002901
2902 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2903 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002904 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002905
glennrpc8cbc5d2011-01-01 00:12:34 +00002906 if (found_transparent_pixel == MagickFalse)
2907 {
2908 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002909 if (y== 0 && logging != MagickFalse)
2910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2911 " Looking for cheap transparent pixel");
2912
glennrpc8cbc5d2011-01-01 00:12:34 +00002913 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2914 {
glennrp5aa37f62011-01-02 03:07:57 +00002915 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2916 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
glennrp8b698592011-04-26 03:38:21 +00002917 (GetOpacityPixelComponent(q) != OpaqueOpacity))
glennrpc8cbc5d2011-01-01 00:12:34 +00002918 {
glennrpa6a06632011-01-19 15:15:34 +00002919 if (logging != MagickFalse)
2920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2921 " ...got one.");
2922
glennrpc8cbc5d2011-01-01 00:12:34 +00002923 found_transparent_pixel = MagickTrue;
2924 break;
2925 }
glennrp4f25bd02011-01-01 18:51:28 +00002926 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2927 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp8b698592011-04-26 03:38:21 +00002928 (ScaleQuantumToShort(GetRedPixelComponent(q))
2929 == transparent_color.red &&
2930 ScaleQuantumToShort(GetGreenPixelComponent(q))
2931 == transparent_color.green &&
2932 ScaleQuantumToShort(GetBluePixelComponent(q))
2933 == transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002934 {
glennrpa6a06632011-01-19 15:15:34 +00002935 if (logging != MagickFalse)
2936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2937 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002938 found_transparent_pixel = MagickTrue;
2939 break;
2940 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002941 q++;
2942 }
2943 }
2944
2945 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2946 {
2947 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2948 image->rows);
2949
2950 if (status == MagickFalse)
2951 break;
2952 }
2953 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2954 break;
2955 }
2956
2957 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2958 {
2959 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002960 if (status == MagickFalse)
2961 break;
2962 }
cristy3ed852e2009-09-05 21:47:34 +00002963 }
cristy3ed852e2009-09-05 21:47:34 +00002964 }
glennrp0fe50b42010-11-16 03:52:51 +00002965
cristy3ed852e2009-09-05 21:47:34 +00002966 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002967
cristy3ed852e2009-09-05 21:47:34 +00002968 for (pass=0; pass < num_passes; pass++)
2969 {
2970 Quantum
2971 *quantum_scanline;
2972
2973 register Quantum
2974 *r;
2975
2976 /*
2977 Convert grayscale image to PseudoClass pixel packets.
2978 */
glennrpfaa852b2010-03-30 12:17:00 +00002979 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002980 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002981
cristy3ed852e2009-09-05 21:47:34 +00002982 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2983 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00002984
cristy3ed852e2009-09-05 21:47:34 +00002985 if (quantum_scanline == (Quantum *) NULL)
2986 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002987
cristybb503372010-05-27 20:51:26 +00002988 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002989 {
2990 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002991 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00002992
cristy3ed852e2009-09-05 21:47:34 +00002993 else
2994 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00002995
glennrpcf002022011-01-30 02:38:15 +00002996 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00002997 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00002998
cristy3ed852e2009-09-05 21:47:34 +00002999 if (q == (PixelPacket *) NULL)
3000 break;
glennrp0fe50b42010-11-16 03:52:51 +00003001
cristy5c6f7892010-05-05 22:53:29 +00003002 indexes=GetAuthenticIndexQueue(image);
glennrpcf002022011-01-30 02:38:15 +00003003 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003004 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003005
glennrpfaa852b2010-03-30 12:17:00 +00003006 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003007 {
3008 case 1:
3009 {
cristybb503372010-05-27 20:51:26 +00003010 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003011 bit;
3012
cristybb503372010-05-27 20:51:26 +00003013 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003014 {
3015 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003016 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003017 p++;
3018 }
glennrp0fe50b42010-11-16 03:52:51 +00003019
cristy3ed852e2009-09-05 21:47:34 +00003020 if ((image->columns % 8) != 0)
3021 {
cristybb503372010-05-27 20:51:26 +00003022 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003023 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003024 }
glennrp0fe50b42010-11-16 03:52:51 +00003025
cristy3ed852e2009-09-05 21:47:34 +00003026 break;
3027 }
glennrp47b9dd52010-11-24 18:12:06 +00003028
cristy3ed852e2009-09-05 21:47:34 +00003029 case 2:
3030 {
cristybb503372010-05-27 20:51:26 +00003031 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003032 {
glennrpa18d5bc2011-04-23 14:51:34 +00003033 *r++=(*p >> 6) & 0x03;
3034 *r++=(*p >> 4) & 0x03;
3035 *r++=(*p >> 2) & 0x03;
3036 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003037 }
glennrp0fe50b42010-11-16 03:52:51 +00003038
cristy3ed852e2009-09-05 21:47:34 +00003039 if ((image->columns % 4) != 0)
3040 {
cristybb503372010-05-27 20:51:26 +00003041 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003042 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003043 }
glennrp0fe50b42010-11-16 03:52:51 +00003044
cristy3ed852e2009-09-05 21:47:34 +00003045 break;
3046 }
glennrp47b9dd52010-11-24 18:12:06 +00003047
cristy3ed852e2009-09-05 21:47:34 +00003048 case 4:
3049 {
cristybb503372010-05-27 20:51:26 +00003050 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003051 {
glennrpa18d5bc2011-04-23 14:51:34 +00003052 *r++=(*p >> 4) & 0x0f;
3053 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003054 }
glennrp0fe50b42010-11-16 03:52:51 +00003055
cristy3ed852e2009-09-05 21:47:34 +00003056 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003057 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003058
cristy3ed852e2009-09-05 21:47:34 +00003059 break;
3060 }
glennrp47b9dd52010-11-24 18:12:06 +00003061
cristy3ed852e2009-09-05 21:47:34 +00003062 case 8:
3063 {
glennrpfaa852b2010-03-30 12:17:00 +00003064 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003065 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003066 {
glennrpa18d5bc2011-04-23 14:51:34 +00003067 *r++=*p++;
cristy3ed852e2009-09-05 21:47:34 +00003068 /* In image.h, OpaqueOpacity is 0
3069 * TransparentOpacity is QuantumRange
3070 * In a PNG datastream, Opaque is QuantumRange
3071 * and Transparent is 0.
3072 */
glennrp8b698592011-04-26 03:38:21 +00003073 SetOpacityPixelComponent(q,
3074 ScaleCharToQuantum((unsigned char) (255-(*p++))));
3075 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp0b206f52011-01-07 04:55:32 +00003076 found_transparent_pixel = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00003077 q++;
3078 }
glennrp0fe50b42010-11-16 03:52:51 +00003079
cristy3ed852e2009-09-05 21:47:34 +00003080 else
cristybb503372010-05-27 20:51:26 +00003081 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003082 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003083
cristy3ed852e2009-09-05 21:47:34 +00003084 break;
3085 }
glennrp47b9dd52010-11-24 18:12:06 +00003086
cristy3ed852e2009-09-05 21:47:34 +00003087 case 16:
3088 {
cristybb503372010-05-27 20:51:26 +00003089 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003090 {
glennrp58f77c72011-04-23 14:09:09 +00003091#if (MAGICKCORE_QUANTUM_DEPTH == 16)
3092 size_t
3093 quantum;
3094
3095 if (image->colors > 256)
3096 *r=((*p++) << 8);
3097
3098 else
3099 *r=0;
3100
3101 quantum=(*r);
3102 quantum|=(*p++);
3103 *r=(Quantum) quantum;
3104 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003105
3106 if (ping_color_type == 4)
3107 {
glennrp58f77c72011-04-23 14:09:09 +00003108 quantum=((*p++) << 8);
3109 quantum|=(*p++);
cristy3da0b402011-05-29 21:13:36 +00003110 SetAlphaPixelComponent(q,quantum);
glennrp8b698592011-04-26 03:38:21 +00003111 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00003112 found_transparent_pixel = MagickTrue;
3113 q++;
3114 }
3115#else
3116#if (MAGICKCORE_QUANTUM_DEPTH == 32)
3117 size_t
3118 quantum;
3119
3120 if (image->colors > 256)
3121 *r=((*p++) << 8);
3122
3123 else
3124 *r=0;
3125
3126 quantum=(*r);
3127 quantum|=(*p++);
3128 *r=quantum;
3129 r++;
3130
3131 if (ping_color_type == 4)
3132 {
glennrp8b698592011-04-26 03:38:21 +00003133 quantum=(*p << 8) | *(p+1);
3134 quantum*=65537L;
3135 SetOpacityPixelComponent(q,
3136 (Quantum) GetAlphaPixelComponent(q));
3137 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00003138 found_transparent_pixel = MagickTrue;
3139 p+=2;
3140 q++;
3141 }
3142
3143#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3144 *r++=(*p++);
3145 p++; /* strip low byte */
3146
3147 if (ping_color_type == 4)
3148 {
cristy3da0b402011-05-29 21:13:36 +00003149 SetAlphaPixelComponent(q,*p++);
glennrp8b698592011-04-26 03:38:21 +00003150 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp9d0ea4d2011-04-22 18:35:57 +00003151 found_transparent_pixel = MagickTrue;
3152 p++;
3153 q++;
3154 }
cristy3ed852e2009-09-05 21:47:34 +00003155#endif
glennrp58f77c72011-04-23 14:09:09 +00003156#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003157 }
glennrp47b9dd52010-11-24 18:12:06 +00003158
cristy3ed852e2009-09-05 21:47:34 +00003159 break;
3160 }
glennrp47b9dd52010-11-24 18:12:06 +00003161
cristy3ed852e2009-09-05 21:47:34 +00003162 default:
3163 break;
3164 }
glennrp3faa9a32011-04-23 14:00:25 +00003165
cristy3ed852e2009-09-05 21:47:34 +00003166 /*
3167 Transfer image scanline.
3168 */
3169 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003170
cristybb503372010-05-27 20:51:26 +00003171 for (x=0; x < (ssize_t) image->columns; x++)
cristy9fff7b42011-04-29 01:09:31 +00003172 SetIndexPixelComponent(indexes+x,*r++);
glennrp0fe50b42010-11-16 03:52:51 +00003173
cristy3ed852e2009-09-05 21:47:34 +00003174 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3175 break;
glennrp0fe50b42010-11-16 03:52:51 +00003176
cristy7a287bf2010-02-14 02:18:09 +00003177 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3178 {
cristycee97112010-05-28 00:44:52 +00003179 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003180 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003181
cristy7a287bf2010-02-14 02:18:09 +00003182 if (status == MagickFalse)
3183 break;
3184 }
cristy3ed852e2009-09-05 21:47:34 +00003185 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003186
cristy7a287bf2010-02-14 02:18:09 +00003187 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003188 {
3189 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003190
cristy3ed852e2009-09-05 21:47:34 +00003191 if (status == MagickFalse)
3192 break;
3193 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003194
cristy3ed852e2009-09-05 21:47:34 +00003195 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3196 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003197
3198 image->matte=found_transparent_pixel;
3199
3200 if (logging != MagickFalse)
3201 {
3202 if (found_transparent_pixel != MagickFalse)
3203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3204 " Found transparent pixel");
3205 else
glennrp5aa37f62011-01-02 03:07:57 +00003206 {
3207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3208 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003209
glennrp5aa37f62011-01-02 03:07:57 +00003210 ping_color_type&=0x03;
3211 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003212 }
3213 }
3214
cristyb32b90a2009-09-07 21:45:48 +00003215 if (quantum_info != (QuantumInfo *) NULL)
3216 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003217
cristy5c6f7892010-05-05 22:53:29 +00003218 if (image->storage_class == PseudoClass)
3219 {
cristyaeb2cbc2010-05-07 13:28:58 +00003220 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003221 matte;
3222
3223 matte=image->matte;
3224 image->matte=MagickFalse;
3225 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00003226 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003227 }
glennrp47b9dd52010-11-24 18:12:06 +00003228
glennrp4eb39312011-03-30 21:34:55 +00003229 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003230
3231 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003232 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003233 {
3234 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003235 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003236 image->colors=2;
3237 (void) SetImageBackgroundColor(image);
3238#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003239 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003240#endif
3241 if (logging != MagickFalse)
3242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3243 " exit ReadOnePNGImage() early.");
3244 return(image);
3245 }
glennrp47b9dd52010-11-24 18:12:06 +00003246
glennrpfaa852b2010-03-30 12:17:00 +00003247 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003248 {
3249 ClassType
3250 storage_class;
3251
3252 /*
3253 Image has a transparent background.
3254 */
3255 storage_class=image->storage_class;
3256 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003257
glennrp3c218112010-11-27 15:31:26 +00003258/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003259
glennrp0fe50b42010-11-16 03:52:51 +00003260 if (storage_class == PseudoClass)
3261 {
3262 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003263 {
glennrp0fe50b42010-11-16 03:52:51 +00003264 for (x=0; x < ping_num_trans; x++)
3265 {
3266 image->colormap[x].opacity =
3267 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
3268 }
glennrpc11cf6a2010-03-20 16:46:19 +00003269 }
glennrp47b9dd52010-11-24 18:12:06 +00003270
glennrp0fe50b42010-11-16 03:52:51 +00003271 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3272 {
3273 for (x=0; x < (int) image->colors; x++)
3274 {
3275 if (ScaleQuantumToShort(image->colormap[x].red) ==
3276 transparent_color.opacity)
3277 {
3278 image->colormap[x].opacity = (Quantum) TransparentOpacity;
3279 }
3280 }
3281 }
3282 (void) SyncImage(image);
3283 }
glennrp47b9dd52010-11-24 18:12:06 +00003284
glennrpa6a06632011-01-19 15:15:34 +00003285#if 1 /* Should have already been done above, but glennrp problem P10
3286 * needs this.
3287 */
glennrp0fe50b42010-11-16 03:52:51 +00003288 else
3289 {
3290 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003291 {
glennrp0fe50b42010-11-16 03:52:51 +00003292 image->storage_class=storage_class;
3293 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3294
3295 if (q == (PixelPacket *) NULL)
3296 break;
3297
3298 indexes=GetAuthenticIndexQueue(image);
3299
glennrpa6a06632011-01-19 15:15:34 +00003300 /* Caution: on a Q8 build, this does not distinguish between
3301 * 16-bit colors that differ only in the low byte
3302 */
glennrp0fe50b42010-11-16 03:52:51 +00003303 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3304 {
glennrp8b698592011-04-26 03:38:21 +00003305 if (ScaleQuantumToShort(GetRedPixelComponent(q))
3306 == transparent_color.red &&
3307 ScaleQuantumToShort(GetGreenPixelComponent(q))
3308 == transparent_color.green &&
3309 ScaleQuantumToShort(GetBluePixelComponent(q))
3310 == transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003311 {
glennrp8b698592011-04-26 03:38:21 +00003312 SetOpacityPixelComponent(q,TransparentOpacity);
glennrp4f25bd02011-01-01 18:51:28 +00003313 }
glennrp0fe50b42010-11-16 03:52:51 +00003314
glennrp67b9c1a2011-04-22 18:47:36 +00003315#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003316 else
glennrp4f25bd02011-01-01 18:51:28 +00003317 {
glennrp4737d522011-04-29 03:33:42 +00003318 SetOpacityPixelComponent(q)=(Quantum) OpaqueOpacity;
glennrp4f25bd02011-01-01 18:51:28 +00003319 }
glennrpa6a06632011-01-19 15:15:34 +00003320#endif
glennrp0fe50b42010-11-16 03:52:51 +00003321
3322 q++;
3323 }
3324
3325 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3326 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003327 }
glennrp0fe50b42010-11-16 03:52:51 +00003328 }
glennrpa6a06632011-01-19 15:15:34 +00003329#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003330
cristy3ed852e2009-09-05 21:47:34 +00003331 image->storage_class=DirectClass;
3332 }
glennrp3c218112010-11-27 15:31:26 +00003333
cristyb40fc462010-08-08 00:49:49 +00003334 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3335 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3336 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003337
cristyeb3b22a2011-03-31 20:16:11 +00003338 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003339 {
3340 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003341 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3342 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003343 else
glennrpa0ed0092011-04-18 16:36:29 +00003344 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3345 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003346
glennrp4eb39312011-03-30 21:34:55 +00003347 if (status != MagickFalse)
3348 for (i=0; i < (ssize_t) num_text; i++)
3349 {
3350 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003351
glennrp4eb39312011-03-30 21:34:55 +00003352 if (logging != MagickFalse)
3353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3354 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003355
glennrp4eb39312011-03-30 21:34:55 +00003356 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003357 {
glennrp4eb39312011-03-30 21:34:55 +00003358 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
3359 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003360 }
glennrp0fe50b42010-11-16 03:52:51 +00003361
glennrp4eb39312011-03-30 21:34:55 +00003362 else
3363 {
3364 char
3365 *value;
3366
3367 length=text[i].text_length;
3368 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3369 sizeof(*value));
3370 if (value == (char *) NULL)
3371 {
3372 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3373 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3374 image->filename);
3375 break;
3376 }
3377 *value='\0';
3378 (void) ConcatenateMagickString(value,text[i].text,length+2);
3379
3380 /* Don't save "density" or "units" property if we have a pHYs
3381 * chunk
3382 */
3383 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3384 (LocaleCompare(text[i].key,"density") != 0 &&
3385 LocaleCompare(text[i].key,"units") != 0))
3386 (void) SetImageProperty(image,text[i].key,value);
3387
3388 if (logging != MagickFalse)
3389 {
3390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3391 " length: %lu",(unsigned long) length);
3392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3393 " Keyword: %s",text[i].key);
3394 }
3395
3396 value=DestroyString(value);
3397 }
3398 }
3399 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003400 }
glennrp3c218112010-11-27 15:31:26 +00003401
cristy3ed852e2009-09-05 21:47:34 +00003402#ifdef MNG_OBJECT_BUFFERS
3403 /*
3404 Store the object if necessary.
3405 */
3406 if (object_id && !mng_info->frozen[object_id])
3407 {
3408 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3409 {
3410 /*
3411 create a new object buffer.
3412 */
3413 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003414 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003415
cristy3ed852e2009-09-05 21:47:34 +00003416 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3417 {
3418 mng_info->ob[object_id]->image=(Image *) NULL;
3419 mng_info->ob[object_id]->reference_count=1;
3420 }
3421 }
glennrp47b9dd52010-11-24 18:12:06 +00003422
cristy3ed852e2009-09-05 21:47:34 +00003423 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3424 mng_info->ob[object_id]->frozen)
3425 {
3426 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3427 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3428 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3429 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003430
cristy3ed852e2009-09-05 21:47:34 +00003431 if (mng_info->ob[object_id]->frozen)
3432 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3433 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3434 "`%s'",image->filename);
3435 }
glennrp0fe50b42010-11-16 03:52:51 +00003436
cristy3ed852e2009-09-05 21:47:34 +00003437 else
3438 {
cristy3ed852e2009-09-05 21:47:34 +00003439
3440 if (mng_info->ob[object_id]->image != (Image *) NULL)
3441 mng_info->ob[object_id]->image=DestroyImage
3442 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003443
cristy3ed852e2009-09-05 21:47:34 +00003444 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3445 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003446
cristy3ed852e2009-09-05 21:47:34 +00003447 if (mng_info->ob[object_id]->image != (Image *) NULL)
3448 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003449
cristy3ed852e2009-09-05 21:47:34 +00003450 else
3451 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3452 ResourceLimitError,"Cloning image for object buffer failed",
3453 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003454
glennrpfaa852b2010-03-30 12:17:00 +00003455 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003456 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003457
glennrpfaa852b2010-03-30 12:17:00 +00003458 mng_info->ob[object_id]->width=ping_width;
3459 mng_info->ob[object_id]->height=ping_height;
3460 mng_info->ob[object_id]->color_type=ping_color_type;
3461 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3462 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3463 mng_info->ob[object_id]->compression_method=
3464 ping_compression_method;
3465 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003466
glennrpfaa852b2010-03-30 12:17:00 +00003467 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003468 {
3469 int
3470 number_colors;
3471
3472 png_colorp
3473 plte;
3474
3475 /*
3476 Copy the PLTE to the object buffer.
3477 */
3478 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3479 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003480
cristy3ed852e2009-09-05 21:47:34 +00003481 for (i=0; i < number_colors; i++)
3482 {
3483 mng_info->ob[object_id]->plte[i]=plte[i];
3484 }
3485 }
glennrp47b9dd52010-11-24 18:12:06 +00003486
cristy3ed852e2009-09-05 21:47:34 +00003487 else
3488 mng_info->ob[object_id]->plte_length=0;
3489 }
3490 }
3491#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003492
3493 /* Set image->matte to MagickTrue if the input colortype supports
3494 * alpha or if a valid tRNS chunk is present, no matter whether there
3495 * is actual transparency present.
3496 */
3497 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3498 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3499 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3500 MagickTrue : MagickFalse;
3501
glennrpcb395ac2011-03-30 19:50:23 +00003502 /* Set more properties for identify to retrieve */
3503 {
3504 char
3505 msg[MaxTextExtent];
3506
glennrp4eb39312011-03-30 21:34:55 +00003507 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003508 {
3509 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003510 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003511 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003512 (void) SetImageProperty(image,"PNG:text ",msg);
3513 }
3514
3515 if (num_raw_profiles != 0)
3516 {
cristy3b6fd2e2011-05-20 12:53:50 +00003517 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003518 "%d were found", num_raw_profiles);
3519 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3520 }
3521
glennrpcb395ac2011-03-30 19:50:23 +00003522 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003523 {
cristy3b6fd2e2011-05-20 12:53:50 +00003524 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003525 "chunk was found (see Chromaticity, above)");
3526 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3527 }
glennrpcb395ac2011-03-30 19:50:23 +00003528
3529 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003530 {
cristy3b6fd2e2011-05-20 12:53:50 +00003531 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003532 "chunk was found (see Background color, above)");
3533 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3534 }
3535
cristy3b6fd2e2011-05-20 12:53:50 +00003536 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003537 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003538
3539 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3540 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3541
glennrpcb395ac2011-03-30 19:50:23 +00003542 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3543 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003544
3545#if defined(PNG_sRGB_SUPPORTED)
3546 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3547 {
cristy3b6fd2e2011-05-20 12:53:50 +00003548 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003549 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003550 (int) intent);
3551 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3552 }
3553#endif
3554
3555 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3556 {
cristy3b6fd2e2011-05-20 12:53:50 +00003557 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003558 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003559 file_gamma);
3560 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3561 }
3562
3563#if defined(PNG_pHYs_SUPPORTED)
3564 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3565 {
cristy3b6fd2e2011-05-20 12:53:50 +00003566 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003567 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003568 (double) x_resolution,(double) y_resolution, unit_type);
3569 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3570 }
3571#endif
3572
3573#if defined(PNG_oFFs_SUPPORTED)
3574 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3575 {
cristy3b6fd2e2011-05-20 12:53:50 +00003576 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003577 (double) image->page.x,(double) image->page.y);
3578 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3579 }
3580#endif
3581
glennrp07523c72011-03-31 18:12:10 +00003582 if ((image->page.width != 0 && image->page.width != image->columns) ||
3583 (image->page.height != 0 && image->page.height != image->rows))
3584 {
cristy3b6fd2e2011-05-20 12:53:50 +00003585 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003586 "width=%.20g, height=%.20g",
3587 (double) image->page.width,(double) image->page.height);
3588 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3589 }
glennrpcb395ac2011-03-30 19:50:23 +00003590 }
3591
cristy3ed852e2009-09-05 21:47:34 +00003592 /*
3593 Relinquish resources.
3594 */
3595 png_destroy_read_struct(&ping,&ping_info,&end_info);
3596
glennrpcf002022011-01-30 02:38:15 +00003597 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003598#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003599 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003600#endif
3601
3602 if (logging != MagickFalse)
3603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3604 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003605
cristy3ed852e2009-09-05 21:47:34 +00003606 return(image);
3607
3608/* end of reading one PNG image */
3609}
3610
3611static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3612{
3613 Image
3614 *image,
3615 *previous;
3616
3617 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003618 have_mng_structure,
3619 logging,
cristy3ed852e2009-09-05 21:47:34 +00003620 status;
3621
3622 MngInfo
3623 *mng_info;
3624
3625 char
3626 magic_number[MaxTextExtent];
3627
cristy3ed852e2009-09-05 21:47:34 +00003628 ssize_t
3629 count;
3630
3631 /*
3632 Open image file.
3633 */
3634 assert(image_info != (const ImageInfo *) NULL);
3635 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003636
cristy3ed852e2009-09-05 21:47:34 +00003637 if (image_info->debug != MagickFalse)
3638 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3639 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003640
cristy3ed852e2009-09-05 21:47:34 +00003641 assert(exception != (ExceptionInfo *) NULL);
3642 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003643 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003644 image=AcquireImage(image_info);
3645 mng_info=(MngInfo *) NULL;
3646 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003647
cristy3ed852e2009-09-05 21:47:34 +00003648 if (status == MagickFalse)
3649 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003650
cristy3ed852e2009-09-05 21:47:34 +00003651 /*
3652 Verify PNG signature.
3653 */
3654 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003655
glennrpdde35db2011-02-21 12:06:32 +00003656 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003657 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003658
cristy3ed852e2009-09-05 21:47:34 +00003659 /*
3660 Allocate a MngInfo structure.
3661 */
3662 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003663 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003664
cristy3ed852e2009-09-05 21:47:34 +00003665 if (mng_info == (MngInfo *) NULL)
3666 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003667
cristy3ed852e2009-09-05 21:47:34 +00003668 /*
3669 Initialize members of the MngInfo structure.
3670 */
3671 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3672 mng_info->image=image;
3673 have_mng_structure=MagickTrue;
3674
3675 previous=image;
3676 image=ReadOnePNGImage(mng_info,image_info,exception);
3677 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003678
cristy3ed852e2009-09-05 21:47:34 +00003679 if (image == (Image *) NULL)
3680 {
3681 if (previous != (Image *) NULL)
3682 {
3683 if (previous->signature != MagickSignature)
3684 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003685
cristy3ed852e2009-09-05 21:47:34 +00003686 (void) CloseBlob(previous);
3687 (void) DestroyImageList(previous);
3688 }
glennrp0fe50b42010-11-16 03:52:51 +00003689
cristy3ed852e2009-09-05 21:47:34 +00003690 if (logging != MagickFalse)
3691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3692 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003693
cristy3ed852e2009-09-05 21:47:34 +00003694 return((Image *) NULL);
3695 }
glennrp47b9dd52010-11-24 18:12:06 +00003696
cristy3ed852e2009-09-05 21:47:34 +00003697 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003698
cristy3ed852e2009-09-05 21:47:34 +00003699 if ((image->columns == 0) || (image->rows == 0))
3700 {
3701 if (logging != MagickFalse)
3702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3703 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003704
cristy3ed852e2009-09-05 21:47:34 +00003705 ThrowReaderException(CorruptImageError,"CorruptImage");
3706 }
glennrp47b9dd52010-11-24 18:12:06 +00003707
cristy3ed852e2009-09-05 21:47:34 +00003708 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3709 {
3710 (void) SetImageType(image,TrueColorType);
3711 image->matte=MagickFalse;
3712 }
glennrp0fe50b42010-11-16 03:52:51 +00003713
cristy3ed852e2009-09-05 21:47:34 +00003714 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3715 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003716
cristy3ed852e2009-09-05 21:47:34 +00003717 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3719 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3720 (double) image->page.width,(double) image->page.height,
3721 (double) image->page.x,(double) image->page.y);
3722
3723 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003725
cristy3ed852e2009-09-05 21:47:34 +00003726 return(image);
3727}
3728
3729
3730
3731#if defined(JNG_SUPPORTED)
3732/*
3733%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3734% %
3735% %
3736% %
3737% R e a d O n e J N G I m a g e %
3738% %
3739% %
3740% %
3741%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3742%
3743% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3744% (minus the 8-byte signature) and returns it. It allocates the memory
3745% necessary for the new Image structure and returns a pointer to the new
3746% image.
3747%
3748% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3749%
3750% The format of the ReadOneJNGImage method is:
3751%
3752% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3753% ExceptionInfo *exception)
3754%
3755% A description of each parameter follows:
3756%
3757% o mng_info: Specifies a pointer to a MngInfo structure.
3758%
3759% o image_info: the image info.
3760%
3761% o exception: return any errors or warnings in this structure.
3762%
3763*/
3764static Image *ReadOneJNGImage(MngInfo *mng_info,
3765 const ImageInfo *image_info, ExceptionInfo *exception)
3766{
3767 Image
3768 *alpha_image,
3769 *color_image,
3770 *image,
3771 *jng_image;
3772
3773 ImageInfo
3774 *alpha_image_info,
3775 *color_image_info;
3776
cristy4383ec82011-01-05 15:42:32 +00003777 MagickBooleanType
3778 logging;
3779
cristybb503372010-05-27 20:51:26 +00003780 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003781 y;
3782
3783 MagickBooleanType
3784 status;
3785
3786 png_uint_32
3787 jng_height,
3788 jng_width;
3789
3790 png_byte
3791 jng_color_type,
3792 jng_image_sample_depth,
3793 jng_image_compression_method,
3794 jng_image_interlace_method,
3795 jng_alpha_sample_depth,
3796 jng_alpha_compression_method,
3797 jng_alpha_filter_method,
3798 jng_alpha_interlace_method;
3799
3800 register const PixelPacket
3801 *s;
3802
cristybb503372010-05-27 20:51:26 +00003803 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003804 i,
3805 x;
3806
3807 register PixelPacket
3808 *q;
3809
3810 register unsigned char
3811 *p;
3812
3813 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003814 read_JSEP,
3815 reading_idat,
3816 skip_to_iend;
3817
cristybb503372010-05-27 20:51:26 +00003818 size_t
cristy3ed852e2009-09-05 21:47:34 +00003819 length;
3820
3821 jng_alpha_compression_method=0;
3822 jng_alpha_sample_depth=8;
3823 jng_color_type=0;
3824 jng_height=0;
3825 jng_width=0;
3826 alpha_image=(Image *) NULL;
3827 color_image=(Image *) NULL;
3828 alpha_image_info=(ImageInfo *) NULL;
3829 color_image_info=(ImageInfo *) NULL;
3830
3831 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003832 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003833
3834 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003835
cristy3ed852e2009-09-05 21:47:34 +00003836 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3837 {
3838 /*
3839 Allocate next image structure.
3840 */
3841 if (logging != MagickFalse)
3842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3843 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003844
cristy3ed852e2009-09-05 21:47:34 +00003845 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003846
cristy3ed852e2009-09-05 21:47:34 +00003847 if (GetNextImageInList(image) == (Image *) NULL)
3848 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003849
cristy3ed852e2009-09-05 21:47:34 +00003850 image=SyncNextImageInList(image);
3851 }
3852 mng_info->image=image;
3853
3854 /*
3855 Signature bytes have already been read.
3856 */
3857
3858 read_JSEP=MagickFalse;
3859 reading_idat=MagickFalse;
3860 skip_to_iend=MagickFalse;
3861 for (;;)
3862 {
3863 char
3864 type[MaxTextExtent];
3865
3866 unsigned char
3867 *chunk;
3868
3869 unsigned int
3870 count;
3871
3872 /*
3873 Read a new JNG chunk.
3874 */
3875 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3876 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003877
cristy3ed852e2009-09-05 21:47:34 +00003878 if (status == MagickFalse)
3879 break;
glennrp0fe50b42010-11-16 03:52:51 +00003880
cristy3ed852e2009-09-05 21:47:34 +00003881 type[0]='\0';
3882 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3883 length=ReadBlobMSBLong(image);
3884 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3885
3886 if (logging != MagickFalse)
3887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003888 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3889 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003890
3891 if (length > PNG_UINT_31_MAX || count == 0)
3892 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003893
cristy3ed852e2009-09-05 21:47:34 +00003894 p=NULL;
3895 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003896
cristy3ed852e2009-09-05 21:47:34 +00003897 if (length)
3898 {
3899 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003900
cristy3ed852e2009-09-05 21:47:34 +00003901 if (chunk == (unsigned char *) NULL)
3902 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003903
cristybb503372010-05-27 20:51:26 +00003904 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003905 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003906
cristy3ed852e2009-09-05 21:47:34 +00003907 p=chunk;
3908 }
glennrp47b9dd52010-11-24 18:12:06 +00003909
cristy3ed852e2009-09-05 21:47:34 +00003910 (void) ReadBlobMSBLong(image); /* read crc word */
3911
3912 if (skip_to_iend)
3913 {
3914 if (length)
3915 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003916
cristy3ed852e2009-09-05 21:47:34 +00003917 continue;
3918 }
3919
3920 if (memcmp(type,mng_JHDR,4) == 0)
3921 {
3922 if (length == 16)
3923 {
cristybb503372010-05-27 20:51:26 +00003924 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003925 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003926 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003927 (p[6] << 8) | p[7]);
3928 jng_color_type=p[8];
3929 jng_image_sample_depth=p[9];
3930 jng_image_compression_method=p[10];
3931 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003932
cristy3ed852e2009-09-05 21:47:34 +00003933 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3934 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003935
cristy3ed852e2009-09-05 21:47:34 +00003936 jng_alpha_sample_depth=p[12];
3937 jng_alpha_compression_method=p[13];
3938 jng_alpha_filter_method=p[14];
3939 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003940
cristy3ed852e2009-09-05 21:47:34 +00003941 if (logging != MagickFalse)
3942 {
3943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003944 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003945
cristy3ed852e2009-09-05 21:47:34 +00003946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003947 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003948
cristy3ed852e2009-09-05 21:47:34 +00003949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3950 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003951
cristy3ed852e2009-09-05 21:47:34 +00003952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3953 " jng_image_sample_depth: %3d",
3954 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003955
cristy3ed852e2009-09-05 21:47:34 +00003956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3957 " jng_image_compression_method:%3d",
3958 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003959
cristy3ed852e2009-09-05 21:47:34 +00003960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3961 " jng_image_interlace_method: %3d",
3962 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003963
cristy3ed852e2009-09-05 21:47:34 +00003964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3965 " jng_alpha_sample_depth: %3d",
3966 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3969 " jng_alpha_compression_method:%3d",
3970 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003971
cristy3ed852e2009-09-05 21:47:34 +00003972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3973 " jng_alpha_filter_method: %3d",
3974 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003975
cristy3ed852e2009-09-05 21:47:34 +00003976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3977 " jng_alpha_interlace_method: %3d",
3978 jng_alpha_interlace_method);
3979 }
3980 }
glennrp47b9dd52010-11-24 18:12:06 +00003981
cristy3ed852e2009-09-05 21:47:34 +00003982 if (length)
3983 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003984
cristy3ed852e2009-09-05 21:47:34 +00003985 continue;
3986 }
3987
3988
3989 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3990 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3991 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3992 {
3993 /*
3994 o create color_image
3995 o open color_blob, attached to color_image
3996 o if (color type has alpha)
3997 open alpha_blob, attached to alpha_image
3998 */
3999
cristy73bd4a52010-10-05 11:24:23 +00004000 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004001
cristy3ed852e2009-09-05 21:47:34 +00004002 if (color_image_info == (ImageInfo *) NULL)
4003 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004004
cristy3ed852e2009-09-05 21:47:34 +00004005 GetImageInfo(color_image_info);
4006 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00004007
cristy3ed852e2009-09-05 21:47:34 +00004008 if (color_image == (Image *) NULL)
4009 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4010
4011 if (logging != MagickFalse)
4012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4013 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004014
cristy3ed852e2009-09-05 21:47:34 +00004015 (void) AcquireUniqueFilename(color_image->filename);
4016 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4017 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004018
cristy3ed852e2009-09-05 21:47:34 +00004019 if (status == MagickFalse)
4020 return((Image *) NULL);
4021
4022 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4023 {
4024 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004025 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004026
cristy3ed852e2009-09-05 21:47:34 +00004027 if (alpha_image_info == (ImageInfo *) NULL)
4028 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004029
cristy3ed852e2009-09-05 21:47:34 +00004030 GetImageInfo(alpha_image_info);
4031 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00004032
cristy3ed852e2009-09-05 21:47:34 +00004033 if (alpha_image == (Image *) NULL)
4034 {
4035 alpha_image=DestroyImage(alpha_image);
4036 ThrowReaderException(ResourceLimitError,
4037 "MemoryAllocationFailed");
4038 }
glennrp0fe50b42010-11-16 03:52:51 +00004039
cristy3ed852e2009-09-05 21:47:34 +00004040 if (logging != MagickFalse)
4041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4042 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004043
cristy3ed852e2009-09-05 21:47:34 +00004044 (void) AcquireUniqueFilename(alpha_image->filename);
4045 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4046 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004047
cristy3ed852e2009-09-05 21:47:34 +00004048 if (status == MagickFalse)
4049 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004050
cristy3ed852e2009-09-05 21:47:34 +00004051 if (jng_alpha_compression_method == 0)
4052 {
4053 unsigned char
4054 data[18];
4055
4056 if (logging != MagickFalse)
4057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4058 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004059
cristy3ed852e2009-09-05 21:47:34 +00004060 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4061 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004062
cristy3ed852e2009-09-05 21:47:34 +00004063 (void) WriteBlobMSBULong(alpha_image,13L);
4064 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004065 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004066 PNGLong(data+4,jng_width);
4067 PNGLong(data+8,jng_height);
4068 data[12]=jng_alpha_sample_depth;
4069 data[13]=0; /* color_type gray */
4070 data[14]=0; /* compression method 0 */
4071 data[15]=0; /* filter_method 0 */
4072 data[16]=0; /* interlace_method 0 */
4073 (void) WriteBlob(alpha_image,17,data);
4074 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4075 }
4076 }
4077 reading_idat=MagickTrue;
4078 }
4079
4080 if (memcmp(type,mng_JDAT,4) == 0)
4081 {
glennrp47b9dd52010-11-24 18:12:06 +00004082 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004083
4084 if (logging != MagickFalse)
4085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4086 " Copying JDAT chunk data to color_blob.");
4087
4088 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004089
cristy3ed852e2009-09-05 21:47:34 +00004090 if (length)
4091 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004092
cristy3ed852e2009-09-05 21:47:34 +00004093 continue;
4094 }
4095
4096 if (memcmp(type,mng_IDAT,4) == 0)
4097 {
4098 png_byte
4099 data[5];
4100
glennrp47b9dd52010-11-24 18:12:06 +00004101 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004102
4103 if (image_info->ping == MagickFalse)
4104 {
4105 if (logging != MagickFalse)
4106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4107 " Copying IDAT chunk data to alpha_blob.");
4108
cristybb503372010-05-27 20:51:26 +00004109 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004110 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004111 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004112 (void) WriteBlob(alpha_image,4,data);
4113 (void) WriteBlob(alpha_image,length,chunk);
4114 (void) WriteBlobMSBULong(alpha_image,
4115 crc32(crc32(0,data,4),chunk,(uInt) length));
4116 }
glennrp0fe50b42010-11-16 03:52:51 +00004117
cristy3ed852e2009-09-05 21:47:34 +00004118 if (length)
4119 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004120
cristy3ed852e2009-09-05 21:47:34 +00004121 continue;
4122 }
4123
4124 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4125 {
glennrp47b9dd52010-11-24 18:12:06 +00004126 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004127
4128 if (image_info->ping == MagickFalse)
4129 {
4130 if (logging != MagickFalse)
4131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4132 " Copying JDAA chunk data to alpha_blob.");
4133
4134 (void) WriteBlob(alpha_image,length,chunk);
4135 }
glennrp0fe50b42010-11-16 03:52:51 +00004136
cristy3ed852e2009-09-05 21:47:34 +00004137 if (length)
4138 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004139
cristy3ed852e2009-09-05 21:47:34 +00004140 continue;
4141 }
4142
4143 if (memcmp(type,mng_JSEP,4) == 0)
4144 {
4145 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004146
cristy3ed852e2009-09-05 21:47:34 +00004147 if (length)
4148 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004149
cristy3ed852e2009-09-05 21:47:34 +00004150 continue;
4151 }
4152
4153 if (memcmp(type,mng_bKGD,4) == 0)
4154 {
4155 if (length == 2)
4156 {
4157 image->background_color.red=ScaleCharToQuantum(p[1]);
4158 image->background_color.green=image->background_color.red;
4159 image->background_color.blue=image->background_color.red;
4160 }
glennrp0fe50b42010-11-16 03:52:51 +00004161
cristy3ed852e2009-09-05 21:47:34 +00004162 if (length == 6)
4163 {
4164 image->background_color.red=ScaleCharToQuantum(p[1]);
4165 image->background_color.green=ScaleCharToQuantum(p[3]);
4166 image->background_color.blue=ScaleCharToQuantum(p[5]);
4167 }
glennrp0fe50b42010-11-16 03:52:51 +00004168
cristy3ed852e2009-09-05 21:47:34 +00004169 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4170 continue;
4171 }
4172
4173 if (memcmp(type,mng_gAMA,4) == 0)
4174 {
4175 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004176 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004177
cristy3ed852e2009-09-05 21:47:34 +00004178 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4179 continue;
4180 }
4181
4182 if (memcmp(type,mng_cHRM,4) == 0)
4183 {
4184 if (length == 32)
4185 {
cristy8182b072010-05-30 20:10:53 +00004186 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4187 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4188 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4189 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4190 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4191 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4192 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4193 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004194 }
glennrp47b9dd52010-11-24 18:12:06 +00004195
cristy3ed852e2009-09-05 21:47:34 +00004196 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4197 continue;
4198 }
4199
4200 if (memcmp(type,mng_sRGB,4) == 0)
4201 {
4202 if (length == 1)
4203 {
glennrpe610a072010-08-05 17:08:46 +00004204 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004205 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004206 image->gamma=0.45455f;
4207 image->chromaticity.red_primary.x=0.6400f;
4208 image->chromaticity.red_primary.y=0.3300f;
4209 image->chromaticity.green_primary.x=0.3000f;
4210 image->chromaticity.green_primary.y=0.6000f;
4211 image->chromaticity.blue_primary.x=0.1500f;
4212 image->chromaticity.blue_primary.y=0.0600f;
4213 image->chromaticity.white_point.x=0.3127f;
4214 image->chromaticity.white_point.y=0.3290f;
4215 }
glennrp47b9dd52010-11-24 18:12:06 +00004216
cristy3ed852e2009-09-05 21:47:34 +00004217 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4218 continue;
4219 }
4220
4221 if (memcmp(type,mng_oFFs,4) == 0)
4222 {
4223 if (length > 8)
4224 {
glennrp5eae7602011-02-22 15:21:32 +00004225 image->page.x=(ssize_t) mng_get_long(p);
4226 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004227
cristy3ed852e2009-09-05 21:47:34 +00004228 if ((int) p[8] != 0)
4229 {
4230 image->page.x/=10000;
4231 image->page.y/=10000;
4232 }
4233 }
glennrp47b9dd52010-11-24 18:12:06 +00004234
cristy3ed852e2009-09-05 21:47:34 +00004235 if (length)
4236 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004237
cristy3ed852e2009-09-05 21:47:34 +00004238 continue;
4239 }
4240
4241 if (memcmp(type,mng_pHYs,4) == 0)
4242 {
4243 if (length > 8)
4244 {
cristy8182b072010-05-30 20:10:53 +00004245 image->x_resolution=(double) mng_get_long(p);
4246 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004247 if ((int) p[8] == PNG_RESOLUTION_METER)
4248 {
4249 image->units=PixelsPerCentimeterResolution;
4250 image->x_resolution=image->x_resolution/100.0f;
4251 image->y_resolution=image->y_resolution/100.0f;
4252 }
4253 }
glennrp0fe50b42010-11-16 03:52:51 +00004254
cristy3ed852e2009-09-05 21:47:34 +00004255 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4256 continue;
4257 }
4258
4259#if 0
4260 if (memcmp(type,mng_iCCP,4) == 0)
4261 {
glennrpfd05d622011-02-25 04:10:33 +00004262 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004263 if (length)
4264 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004265
cristy3ed852e2009-09-05 21:47:34 +00004266 continue;
4267 }
4268#endif
4269
4270 if (length)
4271 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4272
4273 if (memcmp(type,mng_IEND,4))
4274 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004275
cristy3ed852e2009-09-05 21:47:34 +00004276 break;
4277 }
4278
4279
4280 /* IEND found */
4281
4282 /*
4283 Finish up reading image data:
4284
4285 o read main image from color_blob.
4286
4287 o close color_blob.
4288
4289 o if (color_type has alpha)
4290 if alpha_encoding is PNG
4291 read secondary image from alpha_blob via ReadPNG
4292 if alpha_encoding is JPEG
4293 read secondary image from alpha_blob via ReadJPEG
4294
4295 o close alpha_blob.
4296
4297 o copy intensity of secondary image into
4298 opacity samples of main image.
4299
4300 o destroy the secondary image.
4301 */
4302
4303 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004304
cristy3ed852e2009-09-05 21:47:34 +00004305 if (logging != MagickFalse)
4306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4307 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004308
cristy3b6fd2e2011-05-20 12:53:50 +00004309 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004310 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004311
cristy3ed852e2009-09-05 21:47:34 +00004312 color_image_info->ping=MagickFalse; /* To do: avoid this */
4313 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004314
cristy3ed852e2009-09-05 21:47:34 +00004315 if (jng_image == (Image *) NULL)
4316 return((Image *) NULL);
4317
4318 (void) RelinquishUniqueFileResource(color_image->filename);
4319 color_image=DestroyImage(color_image);
4320 color_image_info=DestroyImageInfo(color_image_info);
4321
4322 if (jng_image == (Image *) NULL)
4323 return((Image *) NULL);
4324
4325 if (logging != MagickFalse)
4326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4327 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004328
cristy3ed852e2009-09-05 21:47:34 +00004329 image->rows=jng_height;
4330 image->columns=jng_width;
4331 length=image->columns*sizeof(PixelPacket);
glennrp0fe50b42010-11-16 03:52:51 +00004332
cristybb503372010-05-27 20:51:26 +00004333 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004334 {
4335 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4336 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4337 (void) CopyMagickMemory(q,s,length);
glennrp47b9dd52010-11-24 18:12:06 +00004338
cristy3ed852e2009-09-05 21:47:34 +00004339 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4340 break;
4341 }
glennrp0fe50b42010-11-16 03:52:51 +00004342
cristy3ed852e2009-09-05 21:47:34 +00004343 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004344
cristy3ed852e2009-09-05 21:47:34 +00004345 if (image_info->ping == MagickFalse)
4346 {
4347 if (jng_color_type >= 12)
4348 {
4349 if (jng_alpha_compression_method == 0)
4350 {
4351 png_byte
4352 data[5];
4353 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4354 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004355 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004356 (void) WriteBlob(alpha_image,4,data);
4357 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4358 }
glennrp0fe50b42010-11-16 03:52:51 +00004359
cristy3ed852e2009-09-05 21:47:34 +00004360 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004361
cristy3ed852e2009-09-05 21:47:34 +00004362 if (logging != MagickFalse)
4363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4364 " Reading opacity from alpha_blob.");
4365
cristy3b6fd2e2011-05-20 12:53:50 +00004366 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004367 "%s",alpha_image->filename);
4368
4369 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004370
cristy3ed852e2009-09-05 21:47:34 +00004371 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004372 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004373 {
4374 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4375 &image->exception);
4376 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004377
cristy3ed852e2009-09-05 21:47:34 +00004378 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00004379 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
glennrp4737d522011-04-29 03:33:42 +00004380 SetOpacityPixelComponent(q,(Quantum) QuantumRange-
4381 GetRedPixelComponent(s));
glennrp0fe50b42010-11-16 03:52:51 +00004382
cristy3ed852e2009-09-05 21:47:34 +00004383 else
cristybb503372010-05-27 20:51:26 +00004384 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00004385 {
cristy3da0b402011-05-29 21:13:36 +00004386 SetAlphaPixelComponent(q,GetRedPixelComponent(s));
glennrp7c7b3152011-04-26 04:01:27 +00004387 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
cristy3ed852e2009-09-05 21:47:34 +00004388 image->matte=MagickTrue;
4389 }
glennrp0fe50b42010-11-16 03:52:51 +00004390
cristy3ed852e2009-09-05 21:47:34 +00004391 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4392 break;
4393 }
4394 (void) RelinquishUniqueFileResource(alpha_image->filename);
4395 alpha_image=DestroyImage(alpha_image);
4396 alpha_image_info=DestroyImageInfo(alpha_image_info);
4397 if (jng_image != (Image *) NULL)
4398 jng_image=DestroyImage(jng_image);
4399 }
4400 }
4401
glennrp47b9dd52010-11-24 18:12:06 +00004402 /* Read the JNG image. */
4403
cristy3ed852e2009-09-05 21:47:34 +00004404 if (mng_info->mng_type == 0)
4405 {
4406 mng_info->mng_width=jng_width;
4407 mng_info->mng_height=jng_height;
4408 }
glennrp0fe50b42010-11-16 03:52:51 +00004409
cristy3ed852e2009-09-05 21:47:34 +00004410 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004411 {
4412 image->page.width=jng_width;
4413 image->page.height=jng_height;
4414 }
4415
cristy3ed852e2009-09-05 21:47:34 +00004416 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004417 {
4418 image->page.x=mng_info->x_off[mng_info->object_id];
4419 image->page.y=mng_info->y_off[mng_info->object_id];
4420 }
4421
cristy3ed852e2009-09-05 21:47:34 +00004422 else
glennrp0fe50b42010-11-16 03:52:51 +00004423 {
4424 image->page.y=mng_info->y_off[mng_info->object_id];
4425 }
4426
cristy3ed852e2009-09-05 21:47:34 +00004427 mng_info->image_found++;
4428 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4429 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004430
cristy3ed852e2009-09-05 21:47:34 +00004431 if (logging != MagickFalse)
4432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4433 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004434
cristy3ed852e2009-09-05 21:47:34 +00004435 return(image);
4436}
4437
4438/*
4439%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4440% %
4441% %
4442% %
4443% R e a d J N G I m a g e %
4444% %
4445% %
4446% %
4447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4448%
4449% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4450% (including the 8-byte signature) and returns it. It allocates the memory
4451% necessary for the new Image structure and returns a pointer to the new
4452% image.
4453%
4454% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4455%
4456% The format of the ReadJNGImage method is:
4457%
4458% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4459% *exception)
4460%
4461% A description of each parameter follows:
4462%
4463% o image_info: the image info.
4464%
4465% o exception: return any errors or warnings in this structure.
4466%
4467*/
4468
4469static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4470{
4471 Image
4472 *image,
4473 *previous;
4474
4475 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004476 have_mng_structure,
4477 logging,
cristy3ed852e2009-09-05 21:47:34 +00004478 status;
4479
4480 MngInfo
4481 *mng_info;
4482
4483 char
4484 magic_number[MaxTextExtent];
4485
cristy3ed852e2009-09-05 21:47:34 +00004486 size_t
4487 count;
4488
4489 /*
4490 Open image file.
4491 */
4492 assert(image_info != (const ImageInfo *) NULL);
4493 assert(image_info->signature == MagickSignature);
4494 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4495 assert(exception != (ExceptionInfo *) NULL);
4496 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004497 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004498 image=AcquireImage(image_info);
4499 mng_info=(MngInfo *) NULL;
4500 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004501
cristy3ed852e2009-09-05 21:47:34 +00004502 if (status == MagickFalse)
4503 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004504
cristy3ed852e2009-09-05 21:47:34 +00004505 if (LocaleCompare(image_info->magick,"JNG") != 0)
4506 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004507
glennrp47b9dd52010-11-24 18:12:06 +00004508 /* Verify JNG signature. */
4509
cristy3ed852e2009-09-05 21:47:34 +00004510 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004511
glennrp3b8763e2011-02-21 12:08:18 +00004512 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004513 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004514
glennrp47b9dd52010-11-24 18:12:06 +00004515 /* Allocate a MngInfo structure. */
4516
cristy3ed852e2009-09-05 21:47:34 +00004517 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004518 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004519
cristy3ed852e2009-09-05 21:47:34 +00004520 if (mng_info == (MngInfo *) NULL)
4521 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004522
glennrp47b9dd52010-11-24 18:12:06 +00004523 /* Initialize members of the MngInfo structure. */
4524
cristy3ed852e2009-09-05 21:47:34 +00004525 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4526 have_mng_structure=MagickTrue;
4527
4528 mng_info->image=image;
4529 previous=image;
4530 image=ReadOneJNGImage(mng_info,image_info,exception);
4531 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004532
cristy3ed852e2009-09-05 21:47:34 +00004533 if (image == (Image *) NULL)
4534 {
4535 if (IsImageObject(previous) != MagickFalse)
4536 {
4537 (void) CloseBlob(previous);
4538 (void) DestroyImageList(previous);
4539 }
glennrp0fe50b42010-11-16 03:52:51 +00004540
cristy3ed852e2009-09-05 21:47:34 +00004541 if (logging != MagickFalse)
4542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4543 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004544
cristy3ed852e2009-09-05 21:47:34 +00004545 return((Image *) NULL);
4546 }
4547 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004548
cristy3ed852e2009-09-05 21:47:34 +00004549 if (image->columns == 0 || image->rows == 0)
4550 {
4551 if (logging != MagickFalse)
4552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4553 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004554
cristy3ed852e2009-09-05 21:47:34 +00004555 ThrowReaderException(CorruptImageError,"CorruptImage");
4556 }
glennrp0fe50b42010-11-16 03:52:51 +00004557
cristy3ed852e2009-09-05 21:47:34 +00004558 if (logging != MagickFalse)
4559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004560
cristy3ed852e2009-09-05 21:47:34 +00004561 return(image);
4562}
4563#endif
4564
4565static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4566{
4567 char
4568 page_geometry[MaxTextExtent];
4569
4570 Image
4571 *image,
4572 *previous;
4573
cristy4383ec82011-01-05 15:42:32 +00004574 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004575 logging,
4576 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004577
cristy3ed852e2009-09-05 21:47:34 +00004578 volatile int
4579 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004580 object_id,
4581 term_chunk_found,
4582 skip_to_iend;
4583
cristybb503372010-05-27 20:51:26 +00004584 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004585 image_count=0;
4586
4587 MagickBooleanType
4588 status;
4589
4590 MagickOffsetType
4591 offset;
4592
4593 MngInfo
4594 *mng_info;
4595
4596 MngBox
4597 default_fb,
4598 fb,
4599 previous_fb;
4600
4601#if defined(MNG_INSERT_LAYERS)
4602 PixelPacket
4603 mng_background_color;
4604#endif
4605
4606 register unsigned char
4607 *p;
4608
cristybb503372010-05-27 20:51:26 +00004609 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004610 i;
4611
4612 size_t
4613 count;
4614
cristybb503372010-05-27 20:51:26 +00004615 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004616 loop_level;
4617
4618 volatile short
4619 skipping_loop;
4620
4621#if defined(MNG_INSERT_LAYERS)
4622 unsigned int
4623 mandatory_back=0;
4624#endif
4625
4626 volatile unsigned int
4627#ifdef MNG_OBJECT_BUFFERS
4628 mng_background_object=0,
4629#endif
4630 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4631
cristybb503372010-05-27 20:51:26 +00004632 size_t
cristy3ed852e2009-09-05 21:47:34 +00004633 default_frame_timeout,
4634 frame_timeout,
4635#if defined(MNG_INSERT_LAYERS)
4636 image_height,
4637 image_width,
4638#endif
4639 length;
4640
glennrp38ea0832010-06-02 18:50:28 +00004641 /* These delays are all measured in image ticks_per_second,
4642 * not in MNG ticks_per_second
4643 */
cristybb503372010-05-27 20:51:26 +00004644 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004645 default_frame_delay,
4646 final_delay,
4647 final_image_delay,
4648 frame_delay,
4649#if defined(MNG_INSERT_LAYERS)
4650 insert_layers,
4651#endif
4652 mng_iterations=1,
4653 simplicity=0,
4654 subframe_height=0,
4655 subframe_width=0;
4656
4657 previous_fb.top=0;
4658 previous_fb.bottom=0;
4659 previous_fb.left=0;
4660 previous_fb.right=0;
4661 default_fb.top=0;
4662 default_fb.bottom=0;
4663 default_fb.left=0;
4664 default_fb.right=0;
4665
glennrp47b9dd52010-11-24 18:12:06 +00004666 /* Open image file. */
4667
cristy3ed852e2009-09-05 21:47:34 +00004668 assert(image_info != (const ImageInfo *) NULL);
4669 assert(image_info->signature == MagickSignature);
4670 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4671 assert(exception != (ExceptionInfo *) NULL);
4672 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004673 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004674 image=AcquireImage(image_info);
4675 mng_info=(MngInfo *) NULL;
4676 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004677
cristy3ed852e2009-09-05 21:47:34 +00004678 if (status == MagickFalse)
4679 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004680
cristy3ed852e2009-09-05 21:47:34 +00004681 first_mng_object=MagickFalse;
4682 skipping_loop=(-1);
4683 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004684
4685 /* Allocate a MngInfo structure. */
4686
cristy73bd4a52010-10-05 11:24:23 +00004687 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004688
cristy3ed852e2009-09-05 21:47:34 +00004689 if (mng_info == (MngInfo *) NULL)
4690 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004691
glennrp47b9dd52010-11-24 18:12:06 +00004692 /* Initialize members of the MngInfo structure. */
4693
cristy3ed852e2009-09-05 21:47:34 +00004694 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4695 mng_info->image=image;
4696 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004697
4698 if (LocaleCompare(image_info->magick,"MNG") == 0)
4699 {
4700 char
4701 magic_number[MaxTextExtent];
4702
glennrp47b9dd52010-11-24 18:12:06 +00004703 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004704 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4705 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4706 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004707
4708 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004709 for (i=0; i < MNG_MAX_OBJECTS; i++)
4710 {
cristybb503372010-05-27 20:51:26 +00004711 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4712 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004713 }
4714 mng_info->exists[0]=MagickTrue;
4715 }
glennrp47b9dd52010-11-24 18:12:06 +00004716
cristy3ed852e2009-09-05 21:47:34 +00004717 first_mng_object=MagickTrue;
4718 mng_type=0;
4719#if defined(MNG_INSERT_LAYERS)
4720 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4721#endif
4722 default_frame_delay=0;
4723 default_frame_timeout=0;
4724 frame_delay=0;
4725 final_delay=1;
4726 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4727 object_id=0;
4728 skip_to_iend=MagickFalse;
4729 term_chunk_found=MagickFalse;
4730 mng_info->framing_mode=1;
4731#if defined(MNG_INSERT_LAYERS)
4732 mandatory_back=MagickFalse;
4733#endif
4734#if defined(MNG_INSERT_LAYERS)
4735 mng_background_color=image->background_color;
4736#endif
4737 default_fb=mng_info->frame;
4738 previous_fb=mng_info->frame;
4739 do
4740 {
4741 char
4742 type[MaxTextExtent];
4743
4744 if (LocaleCompare(image_info->magick,"MNG") == 0)
4745 {
4746 unsigned char
4747 *chunk;
4748
4749 /*
4750 Read a new chunk.
4751 */
4752 type[0]='\0';
4753 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4754 length=ReadBlobMSBLong(image);
4755 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4756
4757 if (logging != MagickFalse)
4758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004759 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4760 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004761
4762 if (length > PNG_UINT_31_MAX)
4763 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004764
cristy3ed852e2009-09-05 21:47:34 +00004765 if (count == 0)
4766 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004767
cristy3ed852e2009-09-05 21:47:34 +00004768 p=NULL;
4769 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004770
cristy3ed852e2009-09-05 21:47:34 +00004771 if (length)
4772 {
4773 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004774
cristy3ed852e2009-09-05 21:47:34 +00004775 if (chunk == (unsigned char *) NULL)
4776 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004777
cristybb503372010-05-27 20:51:26 +00004778 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004779 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004780
cristy3ed852e2009-09-05 21:47:34 +00004781 p=chunk;
4782 }
glennrp0fe50b42010-11-16 03:52:51 +00004783
cristy3ed852e2009-09-05 21:47:34 +00004784 (void) ReadBlobMSBLong(image); /* read crc word */
4785
4786#if !defined(JNG_SUPPORTED)
4787 if (memcmp(type,mng_JHDR,4) == 0)
4788 {
4789 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004790
cristy3ed852e2009-09-05 21:47:34 +00004791 if (mng_info->jhdr_warning == 0)
4792 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4793 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004794
cristy3ed852e2009-09-05 21:47:34 +00004795 mng_info->jhdr_warning++;
4796 }
4797#endif
4798 if (memcmp(type,mng_DHDR,4) == 0)
4799 {
4800 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004801
cristy3ed852e2009-09-05 21:47:34 +00004802 if (mng_info->dhdr_warning == 0)
4803 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4804 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004805
cristy3ed852e2009-09-05 21:47:34 +00004806 mng_info->dhdr_warning++;
4807 }
4808 if (memcmp(type,mng_MEND,4) == 0)
4809 break;
glennrp47b9dd52010-11-24 18:12:06 +00004810
cristy3ed852e2009-09-05 21:47:34 +00004811 if (skip_to_iend)
4812 {
4813 if (memcmp(type,mng_IEND,4) == 0)
4814 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004815
cristy3ed852e2009-09-05 21:47:34 +00004816 if (length)
4817 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004818
cristy3ed852e2009-09-05 21:47:34 +00004819 if (logging != MagickFalse)
4820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4821 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004822
cristy3ed852e2009-09-05 21:47:34 +00004823 continue;
4824 }
glennrp0fe50b42010-11-16 03:52:51 +00004825
cristy3ed852e2009-09-05 21:47:34 +00004826 if (memcmp(type,mng_MHDR,4) == 0)
4827 {
cristybb503372010-05-27 20:51:26 +00004828 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004829 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004830
cristybb503372010-05-27 20:51:26 +00004831 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004832 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004833
cristy3ed852e2009-09-05 21:47:34 +00004834 if (logging != MagickFalse)
4835 {
4836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004837 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004839 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004840 }
glennrp0fe50b42010-11-16 03:52:51 +00004841
cristy3ed852e2009-09-05 21:47:34 +00004842 p+=8;
cristy8182b072010-05-30 20:10:53 +00004843 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004844
cristy3ed852e2009-09-05 21:47:34 +00004845 if (mng_info->ticks_per_second == 0)
4846 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy3ed852e2009-09-05 21:47:34 +00004848 else
4849 default_frame_delay=1UL*image->ticks_per_second/
4850 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004851
cristy3ed852e2009-09-05 21:47:34 +00004852 frame_delay=default_frame_delay;
4853 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004854
cristy3ed852e2009-09-05 21:47:34 +00004855 if (length > 16)
4856 {
4857 p+=16;
cristy8182b072010-05-30 20:10:53 +00004858 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004859 }
glennrp0fe50b42010-11-16 03:52:51 +00004860
cristy3ed852e2009-09-05 21:47:34 +00004861 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004862
cristy3ed852e2009-09-05 21:47:34 +00004863 if ((simplicity != 0) && ((simplicity | 11) == 11))
4864 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004865
cristy3ed852e2009-09-05 21:47:34 +00004866 if ((simplicity != 0) && ((simplicity | 9) == 9))
4867 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004868
cristy3ed852e2009-09-05 21:47:34 +00004869#if defined(MNG_INSERT_LAYERS)
4870 if (mng_type != 3)
4871 insert_layers=MagickTrue;
4872#endif
4873 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4874 {
glennrp47b9dd52010-11-24 18:12:06 +00004875 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004876 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004877
cristy3ed852e2009-09-05 21:47:34 +00004878 if (GetNextImageInList(image) == (Image *) NULL)
4879 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004880
cristy3ed852e2009-09-05 21:47:34 +00004881 image=SyncNextImageInList(image);
4882 mng_info->image=image;
4883 }
4884
4885 if ((mng_info->mng_width > 65535L) ||
4886 (mng_info->mng_height > 65535L))
4887 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004888
cristy3b6fd2e2011-05-20 12:53:50 +00004889 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004890 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004891 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004892
cristy3ed852e2009-09-05 21:47:34 +00004893 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004894 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004895 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004896 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004897 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004898
cristy3ed852e2009-09-05 21:47:34 +00004899 for (i=0; i < MNG_MAX_OBJECTS; i++)
4900 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004901
cristy3ed852e2009-09-05 21:47:34 +00004902 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4903 continue;
4904 }
4905
4906 if (memcmp(type,mng_TERM,4) == 0)
4907 {
4908 int
4909 repeat=0;
4910
4911
4912 if (length)
4913 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004914
cristy3ed852e2009-09-05 21:47:34 +00004915 if (repeat == 3)
4916 {
cristy8182b072010-05-30 20:10:53 +00004917 final_delay=(png_uint_32) mng_get_long(&p[2]);
4918 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004919
cristy3ed852e2009-09-05 21:47:34 +00004920 if (mng_iterations == PNG_UINT_31_MAX)
4921 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004922
cristy3ed852e2009-09-05 21:47:34 +00004923 image->iterations=mng_iterations;
4924 term_chunk_found=MagickTrue;
4925 }
glennrp0fe50b42010-11-16 03:52:51 +00004926
cristy3ed852e2009-09-05 21:47:34 +00004927 if (logging != MagickFalse)
4928 {
4929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4930 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004931
cristy3ed852e2009-09-05 21:47:34 +00004932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004933 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004934
cristy3ed852e2009-09-05 21:47:34 +00004935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004936 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004937 }
glennrp0fe50b42010-11-16 03:52:51 +00004938
cristy3ed852e2009-09-05 21:47:34 +00004939 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4940 continue;
4941 }
4942 if (memcmp(type,mng_DEFI,4) == 0)
4943 {
4944 if (mng_type == 3)
4945 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4946 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4947 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004948
cristy3ed852e2009-09-05 21:47:34 +00004949 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004950
cristy3ed852e2009-09-05 21:47:34 +00004951 if (mng_type == 2 && object_id != 0)
4952 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4953 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4954 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004955
cristy3ed852e2009-09-05 21:47:34 +00004956 if (object_id > MNG_MAX_OBJECTS)
4957 {
4958 /*
4959 Instead ofsuing a warning we should allocate a larger
4960 MngInfo structure and continue.
4961 */
4962 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4963 CoderError,"object id too large","`%s'",image->filename);
4964 object_id=MNG_MAX_OBJECTS;
4965 }
glennrp0fe50b42010-11-16 03:52:51 +00004966
cristy3ed852e2009-09-05 21:47:34 +00004967 if (mng_info->exists[object_id])
4968 if (mng_info->frozen[object_id])
4969 {
4970 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4971 (void) ThrowMagickException(&image->exception,
4972 GetMagickModule(),CoderError,
4973 "DEFI cannot redefine a frozen MNG object","`%s'",
4974 image->filename);
4975 continue;
4976 }
glennrp0fe50b42010-11-16 03:52:51 +00004977
cristy3ed852e2009-09-05 21:47:34 +00004978 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004979
cristy3ed852e2009-09-05 21:47:34 +00004980 if (length > 2)
4981 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004982
cristy3ed852e2009-09-05 21:47:34 +00004983 /*
4984 Extract object offset info.
4985 */
4986 if (length > 11)
4987 {
glennrp0fe50b42010-11-16 03:52:51 +00004988 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4989 (p[5] << 16) | (p[6] << 8) | p[7]);
4990
4991 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4992 (p[9] << 16) | (p[10] << 8) | p[11]);
4993
cristy3ed852e2009-09-05 21:47:34 +00004994 if (logging != MagickFalse)
4995 {
4996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004997 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004998 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004999
cristy3ed852e2009-09-05 21:47:34 +00005000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005001 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005002 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005003 }
5004 }
glennrp0fe50b42010-11-16 03:52:51 +00005005
cristy3ed852e2009-09-05 21:47:34 +00005006 /*
5007 Extract object clipping info.
5008 */
5009 if (length > 27)
5010 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5011 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005012
cristy3ed852e2009-09-05 21:47:34 +00005013 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5014 continue;
5015 }
5016 if (memcmp(type,mng_bKGD,4) == 0)
5017 {
5018 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005019
cristy3ed852e2009-09-05 21:47:34 +00005020 if (length > 5)
5021 {
5022 mng_info->mng_global_bkgd.red=
5023 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005024
cristy3ed852e2009-09-05 21:47:34 +00005025 mng_info->mng_global_bkgd.green=
5026 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005027
cristy3ed852e2009-09-05 21:47:34 +00005028 mng_info->mng_global_bkgd.blue=
5029 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005030
cristy3ed852e2009-09-05 21:47:34 +00005031 mng_info->have_global_bkgd=MagickTrue;
5032 }
glennrp0fe50b42010-11-16 03:52:51 +00005033
cristy3ed852e2009-09-05 21:47:34 +00005034 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5035 continue;
5036 }
5037 if (memcmp(type,mng_BACK,4) == 0)
5038 {
5039#if defined(MNG_INSERT_LAYERS)
5040 if (length > 6)
5041 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005042
cristy3ed852e2009-09-05 21:47:34 +00005043 else
5044 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005045
cristy3ed852e2009-09-05 21:47:34 +00005046 if (mandatory_back && length > 5)
5047 {
5048 mng_background_color.red=
5049 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005050
cristy3ed852e2009-09-05 21:47:34 +00005051 mng_background_color.green=
5052 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005053
cristy3ed852e2009-09-05 21:47:34 +00005054 mng_background_color.blue=
5055 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005056
cristy3ed852e2009-09-05 21:47:34 +00005057 mng_background_color.opacity=OpaqueOpacity;
5058 }
glennrp0fe50b42010-11-16 03:52:51 +00005059
cristy3ed852e2009-09-05 21:47:34 +00005060#ifdef MNG_OBJECT_BUFFERS
5061 if (length > 8)
5062 mng_background_object=(p[7] << 8) | p[8];
5063#endif
5064#endif
5065 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5066 continue;
5067 }
glennrp47b9dd52010-11-24 18:12:06 +00005068
cristy3ed852e2009-09-05 21:47:34 +00005069 if (memcmp(type,mng_PLTE,4) == 0)
5070 {
glennrp47b9dd52010-11-24 18:12:06 +00005071 /* Read global PLTE. */
5072
cristy3ed852e2009-09-05 21:47:34 +00005073 if (length && (length < 769))
5074 {
5075 if (mng_info->global_plte == (png_colorp) NULL)
5076 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5077 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005078
cristybb503372010-05-27 20:51:26 +00005079 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005080 {
5081 mng_info->global_plte[i].red=p[3*i];
5082 mng_info->global_plte[i].green=p[3*i+1];
5083 mng_info->global_plte[i].blue=p[3*i+2];
5084 }
glennrp0fe50b42010-11-16 03:52:51 +00005085
cristy35ef8242010-06-03 16:24:13 +00005086 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005087 }
5088#ifdef MNG_LOOSE
5089 for ( ; i < 256; i++)
5090 {
5091 mng_info->global_plte[i].red=i;
5092 mng_info->global_plte[i].green=i;
5093 mng_info->global_plte[i].blue=i;
5094 }
glennrp0fe50b42010-11-16 03:52:51 +00005095
cristy3ed852e2009-09-05 21:47:34 +00005096 if (length)
5097 mng_info->global_plte_length=256;
5098#endif
5099 else
5100 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005101
cristy3ed852e2009-09-05 21:47:34 +00005102 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5103 continue;
5104 }
glennrp47b9dd52010-11-24 18:12:06 +00005105
cristy3ed852e2009-09-05 21:47:34 +00005106 if (memcmp(type,mng_tRNS,4) == 0)
5107 {
5108 /* read global tRNS */
5109
5110 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005111 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005112 mng_info->global_trns[i]=p[i];
5113
5114#ifdef MNG_LOOSE
5115 for ( ; i < 256; i++)
5116 mng_info->global_trns[i]=255;
5117#endif
cristy12560f32010-06-03 16:51:08 +00005118 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005119 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5120 continue;
5121 }
5122 if (memcmp(type,mng_gAMA,4) == 0)
5123 {
5124 if (length == 4)
5125 {
cristybb503372010-05-27 20:51:26 +00005126 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005127 igamma;
5128
cristy8182b072010-05-30 20:10:53 +00005129 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005130 mng_info->global_gamma=((float) igamma)*0.00001;
5131 mng_info->have_global_gama=MagickTrue;
5132 }
glennrp0fe50b42010-11-16 03:52:51 +00005133
cristy3ed852e2009-09-05 21:47:34 +00005134 else
5135 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005136
cristy3ed852e2009-09-05 21:47:34 +00005137 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5138 continue;
5139 }
5140
5141 if (memcmp(type,mng_cHRM,4) == 0)
5142 {
glennrp47b9dd52010-11-24 18:12:06 +00005143 /* Read global cHRM */
5144
cristy3ed852e2009-09-05 21:47:34 +00005145 if (length == 32)
5146 {
cristy8182b072010-05-30 20:10:53 +00005147 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5148 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5149 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005150 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005151 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005152 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005153 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005154 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005155 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005156 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005157 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005158 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005159 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005160 mng_info->have_global_chrm=MagickTrue;
5161 }
5162 else
5163 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005164
cristy3ed852e2009-09-05 21:47:34 +00005165 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5166 continue;
5167 }
glennrp47b9dd52010-11-24 18:12:06 +00005168
cristy3ed852e2009-09-05 21:47:34 +00005169 if (memcmp(type,mng_sRGB,4) == 0)
5170 {
5171 /*
5172 Read global sRGB.
5173 */
5174 if (length)
5175 {
glennrpe610a072010-08-05 17:08:46 +00005176 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005177 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005178 mng_info->have_global_srgb=MagickTrue;
5179 }
5180 else
5181 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005182
cristy3ed852e2009-09-05 21:47:34 +00005183 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5184 continue;
5185 }
glennrp47b9dd52010-11-24 18:12:06 +00005186
cristy3ed852e2009-09-05 21:47:34 +00005187 if (memcmp(type,mng_iCCP,4) == 0)
5188 {
glennrpfd05d622011-02-25 04:10:33 +00005189 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005190
5191 /*
5192 Read global iCCP.
5193 */
5194 if (length)
5195 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005196
cristy3ed852e2009-09-05 21:47:34 +00005197 continue;
5198 }
glennrp47b9dd52010-11-24 18:12:06 +00005199
cristy3ed852e2009-09-05 21:47:34 +00005200 if (memcmp(type,mng_FRAM,4) == 0)
5201 {
5202 if (mng_type == 3)
5203 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5204 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5205 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005206
cristy3ed852e2009-09-05 21:47:34 +00005207 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5208 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005209
cristy3ed852e2009-09-05 21:47:34 +00005210 frame_delay=default_frame_delay;
5211 frame_timeout=default_frame_timeout;
5212 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005213
cristy3ed852e2009-09-05 21:47:34 +00005214 if (length)
5215 if (p[0])
5216 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005217
cristy3ed852e2009-09-05 21:47:34 +00005218 if (logging != MagickFalse)
5219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5220 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005221
cristy3ed852e2009-09-05 21:47:34 +00005222 if (length > 6)
5223 {
glennrp47b9dd52010-11-24 18:12:06 +00005224 /* Note the delay and frame clipping boundaries. */
5225
cristy3ed852e2009-09-05 21:47:34 +00005226 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005227
cristybb503372010-05-27 20:51:26 +00005228 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005229 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005230
cristy3ed852e2009-09-05 21:47:34 +00005231 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005232
cristybb503372010-05-27 20:51:26 +00005233 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005234 {
5235 int
5236 change_delay,
5237 change_timeout,
5238 change_clipping;
5239
5240 change_delay=(*p++);
5241 change_timeout=(*p++);
5242 change_clipping=(*p++);
5243 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005244
cristy3ed852e2009-09-05 21:47:34 +00005245 if (change_delay)
5246 {
cristy8182b072010-05-30 20:10:53 +00005247 frame_delay=1UL*image->ticks_per_second*
5248 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005249
cristy8182b072010-05-30 20:10:53 +00005250 if (mng_info->ticks_per_second != 0)
5251 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005252
glennrpbb010dd2010-06-01 13:07:15 +00005253 else
5254 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005255
cristy3ed852e2009-09-05 21:47:34 +00005256 if (change_delay == 2)
5257 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005258
cristy3ed852e2009-09-05 21:47:34 +00005259 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005260
cristy3ed852e2009-09-05 21:47:34 +00005261 if (logging != MagickFalse)
5262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005263 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005264 }
glennrp47b9dd52010-11-24 18:12:06 +00005265
cristy3ed852e2009-09-05 21:47:34 +00005266 if (change_timeout)
5267 {
glennrpbb010dd2010-06-01 13:07:15 +00005268 frame_timeout=1UL*image->ticks_per_second*
5269 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005270
glennrpbb010dd2010-06-01 13:07:15 +00005271 if (mng_info->ticks_per_second != 0)
5272 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005273
glennrpbb010dd2010-06-01 13:07:15 +00005274 else
5275 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005276
cristy3ed852e2009-09-05 21:47:34 +00005277 if (change_delay == 2)
5278 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005279
cristy3ed852e2009-09-05 21:47:34 +00005280 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005281
cristy3ed852e2009-09-05 21:47:34 +00005282 if (logging != MagickFalse)
5283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005284 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005285 }
glennrp47b9dd52010-11-24 18:12:06 +00005286
cristy3ed852e2009-09-05 21:47:34 +00005287 if (change_clipping)
5288 {
5289 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5290 p+=17;
5291 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005292
cristy3ed852e2009-09-05 21:47:34 +00005293 if (logging != MagickFalse)
5294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005295 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005296 (double) fb.left,(double) fb.right,(double) fb.top,
5297 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005298
cristy3ed852e2009-09-05 21:47:34 +00005299 if (change_clipping == 2)
5300 default_fb=fb;
5301 }
5302 }
5303 }
5304 mng_info->clip=fb;
5305 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005306
cristybb503372010-05-27 20:51:26 +00005307 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005308 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005309
cristybb503372010-05-27 20:51:26 +00005310 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005311 -mng_info->clip.top);
5312 /*
5313 Insert a background layer behind the frame if framing_mode is 4.
5314 */
5315#if defined(MNG_INSERT_LAYERS)
5316 if (logging != MagickFalse)
5317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005318 " subframe_width=%.20g, subframe_height=%.20g",(double)
5319 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005320
cristy3ed852e2009-09-05 21:47:34 +00005321 if (insert_layers && (mng_info->framing_mode == 4) &&
5322 (subframe_width) && (subframe_height))
5323 {
glennrp47b9dd52010-11-24 18:12:06 +00005324 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00005325 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5326 {
5327 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005328
cristy3ed852e2009-09-05 21:47:34 +00005329 if (GetNextImageInList(image) == (Image *) NULL)
5330 {
5331 image=DestroyImageList(image);
5332 MngInfoFreeStruct(mng_info,&have_mng_structure);
5333 return((Image *) NULL);
5334 }
glennrp47b9dd52010-11-24 18:12:06 +00005335
cristy3ed852e2009-09-05 21:47:34 +00005336 image=SyncNextImageInList(image);
5337 }
glennrp0fe50b42010-11-16 03:52:51 +00005338
cristy3ed852e2009-09-05 21:47:34 +00005339 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005340
cristy3ed852e2009-09-05 21:47:34 +00005341 if (term_chunk_found)
5342 {
5343 image->start_loop=MagickTrue;
5344 image->iterations=mng_iterations;
5345 term_chunk_found=MagickFalse;
5346 }
glennrp0fe50b42010-11-16 03:52:51 +00005347
cristy3ed852e2009-09-05 21:47:34 +00005348 else
5349 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005350
cristy3ed852e2009-09-05 21:47:34 +00005351 image->columns=subframe_width;
5352 image->rows=subframe_height;
5353 image->page.width=subframe_width;
5354 image->page.height=subframe_height;
5355 image->page.x=mng_info->clip.left;
5356 image->page.y=mng_info->clip.top;
5357 image->background_color=mng_background_color;
5358 image->matte=MagickFalse;
5359 image->delay=0;
5360 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005361
cristy3ed852e2009-09-05 21:47:34 +00005362 if (logging != MagickFalse)
5363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005364 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005365 (double) mng_info->clip.left,(double) mng_info->clip.right,
5366 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005367 }
5368#endif
5369 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5370 continue;
5371 }
5372 if (memcmp(type,mng_CLIP,4) == 0)
5373 {
5374 unsigned int
5375 first_object,
5376 last_object;
5377
5378 /*
5379 Read CLIP.
5380 */
5381 first_object=(p[0] << 8) | p[1];
5382 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 for (i=(int) first_object; i <= (int) last_object; i++)
5385 {
5386 if (mng_info->exists[i] && !mng_info->frozen[i])
5387 {
5388 MngBox
5389 box;
5390
5391 box=mng_info->object_clip[i];
5392 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5393 }
5394 }
glennrp47b9dd52010-11-24 18:12:06 +00005395
cristy3ed852e2009-09-05 21:47:34 +00005396 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5397 continue;
5398 }
5399 if (memcmp(type,mng_SAVE,4) == 0)
5400 {
5401 for (i=1; i < MNG_MAX_OBJECTS; i++)
5402 if (mng_info->exists[i])
5403 {
5404 mng_info->frozen[i]=MagickTrue;
5405#ifdef MNG_OBJECT_BUFFERS
5406 if (mng_info->ob[i] != (MngBuffer *) NULL)
5407 mng_info->ob[i]->frozen=MagickTrue;
5408#endif
5409 }
glennrp0fe50b42010-11-16 03:52:51 +00005410
cristy3ed852e2009-09-05 21:47:34 +00005411 if (length)
5412 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005413
cristy3ed852e2009-09-05 21:47:34 +00005414 continue;
5415 }
5416
5417 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5418 {
glennrp47b9dd52010-11-24 18:12:06 +00005419 /* Read DISC or SEEK. */
5420
cristy3ed852e2009-09-05 21:47:34 +00005421 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5422 {
5423 for (i=1; i < MNG_MAX_OBJECTS; i++)
5424 MngInfoDiscardObject(mng_info,i);
5425 }
glennrp0fe50b42010-11-16 03:52:51 +00005426
cristy3ed852e2009-09-05 21:47:34 +00005427 else
5428 {
cristybb503372010-05-27 20:51:26 +00005429 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005430 j;
5431
cristybb503372010-05-27 20:51:26 +00005432 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005433 {
5434 i=p[j] << 8 | p[j+1];
5435 MngInfoDiscardObject(mng_info,i);
5436 }
5437 }
glennrp0fe50b42010-11-16 03:52:51 +00005438
cristy3ed852e2009-09-05 21:47:34 +00005439 if (length)
5440 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005441
cristy3ed852e2009-09-05 21:47:34 +00005442 continue;
5443 }
glennrp47b9dd52010-11-24 18:12:06 +00005444
cristy3ed852e2009-09-05 21:47:34 +00005445 if (memcmp(type,mng_MOVE,4) == 0)
5446 {
cristybb503372010-05-27 20:51:26 +00005447 size_t
cristy3ed852e2009-09-05 21:47:34 +00005448 first_object,
5449 last_object;
5450
glennrp47b9dd52010-11-24 18:12:06 +00005451 /* read MOVE */
5452
cristy3ed852e2009-09-05 21:47:34 +00005453 first_object=(p[0] << 8) | p[1];
5454 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005455 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005456 {
5457 if (mng_info->exists[i] && !mng_info->frozen[i])
5458 {
5459 MngPair
5460 new_pair;
5461
5462 MngPair
5463 old_pair;
5464
5465 old_pair.a=mng_info->x_off[i];
5466 old_pair.b=mng_info->y_off[i];
5467 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5468 mng_info->x_off[i]=new_pair.a;
5469 mng_info->y_off[i]=new_pair.b;
5470 }
5471 }
glennrp47b9dd52010-11-24 18:12:06 +00005472
cristy3ed852e2009-09-05 21:47:34 +00005473 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5474 continue;
5475 }
5476
5477 if (memcmp(type,mng_LOOP,4) == 0)
5478 {
cristybb503372010-05-27 20:51:26 +00005479 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005480 loop_level=chunk[0];
5481 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005482
5483 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005484 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005485
cristy3ed852e2009-09-05 21:47:34 +00005486 if (logging != MagickFalse)
5487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005488 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5489 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005490
cristy3ed852e2009-09-05 21:47:34 +00005491 if (loop_iters == 0)
5492 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005493
cristy3ed852e2009-09-05 21:47:34 +00005494 else
5495 {
5496 mng_info->loop_jump[loop_level]=TellBlob(image);
5497 mng_info->loop_count[loop_level]=loop_iters;
5498 }
glennrp0fe50b42010-11-16 03:52:51 +00005499
cristy3ed852e2009-09-05 21:47:34 +00005500 mng_info->loop_iteration[loop_level]=0;
5501 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5502 continue;
5503 }
glennrp47b9dd52010-11-24 18:12:06 +00005504
cristy3ed852e2009-09-05 21:47:34 +00005505 if (memcmp(type,mng_ENDL,4) == 0)
5506 {
5507 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005508
cristy3ed852e2009-09-05 21:47:34 +00005509 if (skipping_loop > 0)
5510 {
5511 if (skipping_loop == loop_level)
5512 {
5513 /*
5514 Found end of zero-iteration loop.
5515 */
5516 skipping_loop=(-1);
5517 mng_info->loop_active[loop_level]=0;
5518 }
5519 }
glennrp47b9dd52010-11-24 18:12:06 +00005520
cristy3ed852e2009-09-05 21:47:34 +00005521 else
5522 {
5523 if (mng_info->loop_active[loop_level] == 1)
5524 {
5525 mng_info->loop_count[loop_level]--;
5526 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005527
cristy3ed852e2009-09-05 21:47:34 +00005528 if (logging != MagickFalse)
5529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005530 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005531 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005532 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005533
cristy3ed852e2009-09-05 21:47:34 +00005534 if (mng_info->loop_count[loop_level] != 0)
5535 {
5536 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5537 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005538
cristy3ed852e2009-09-05 21:47:34 +00005539 if (offset < 0)
5540 ThrowReaderException(CorruptImageError,
5541 "ImproperImageHeader");
5542 }
glennrp47b9dd52010-11-24 18:12:06 +00005543
cristy3ed852e2009-09-05 21:47:34 +00005544 else
5545 {
5546 short
5547 last_level;
5548
5549 /*
5550 Finished loop.
5551 */
5552 mng_info->loop_active[loop_level]=0;
5553 last_level=(-1);
5554 for (i=0; i < loop_level; i++)
5555 if (mng_info->loop_active[i] == 1)
5556 last_level=(short) i;
5557 loop_level=last_level;
5558 }
5559 }
5560 }
glennrp47b9dd52010-11-24 18:12:06 +00005561
cristy3ed852e2009-09-05 21:47:34 +00005562 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5563 continue;
5564 }
glennrp47b9dd52010-11-24 18:12:06 +00005565
cristy3ed852e2009-09-05 21:47:34 +00005566 if (memcmp(type,mng_CLON,4) == 0)
5567 {
5568 if (mng_info->clon_warning == 0)
5569 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5570 CoderError,"CLON is not implemented yet","`%s'",
5571 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005572
cristy3ed852e2009-09-05 21:47:34 +00005573 mng_info->clon_warning++;
5574 }
glennrp47b9dd52010-11-24 18:12:06 +00005575
cristy3ed852e2009-09-05 21:47:34 +00005576 if (memcmp(type,mng_MAGN,4) == 0)
5577 {
5578 png_uint_16
5579 magn_first,
5580 magn_last,
5581 magn_mb,
5582 magn_ml,
5583 magn_mr,
5584 magn_mt,
5585 magn_mx,
5586 magn_my,
5587 magn_methx,
5588 magn_methy;
5589
5590 if (length > 1)
5591 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005592
cristy3ed852e2009-09-05 21:47:34 +00005593 else
5594 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005595
cristy3ed852e2009-09-05 21:47:34 +00005596 if (length > 3)
5597 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005598
cristy3ed852e2009-09-05 21:47:34 +00005599 else
5600 magn_last=magn_first;
5601#ifndef MNG_OBJECT_BUFFERS
5602 if (magn_first || magn_last)
5603 if (mng_info->magn_warning == 0)
5604 {
5605 (void) ThrowMagickException(&image->exception,
5606 GetMagickModule(),CoderError,
5607 "MAGN is not implemented yet for nonzero objects",
5608 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005609
cristy3ed852e2009-09-05 21:47:34 +00005610 mng_info->magn_warning++;
5611 }
5612#endif
5613 if (length > 4)
5614 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005615
cristy3ed852e2009-09-05 21:47:34 +00005616 else
5617 magn_methx=0;
5618
5619 if (length > 6)
5620 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005621
cristy3ed852e2009-09-05 21:47:34 +00005622 else
5623 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005624
cristy3ed852e2009-09-05 21:47:34 +00005625 if (magn_mx == 0)
5626 magn_mx=1;
5627
5628 if (length > 8)
5629 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005630
cristy3ed852e2009-09-05 21:47:34 +00005631 else
5632 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005633
cristy3ed852e2009-09-05 21:47:34 +00005634 if (magn_my == 0)
5635 magn_my=1;
5636
5637 if (length > 10)
5638 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005639
cristy3ed852e2009-09-05 21:47:34 +00005640 else
5641 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005642
cristy3ed852e2009-09-05 21:47:34 +00005643 if (magn_ml == 0)
5644 magn_ml=1;
5645
5646 if (length > 12)
5647 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005648
cristy3ed852e2009-09-05 21:47:34 +00005649 else
5650 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005651
cristy3ed852e2009-09-05 21:47:34 +00005652 if (magn_mr == 0)
5653 magn_mr=1;
5654
5655 if (length > 14)
5656 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005657
cristy3ed852e2009-09-05 21:47:34 +00005658 else
5659 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005660
cristy3ed852e2009-09-05 21:47:34 +00005661 if (magn_mt == 0)
5662 magn_mt=1;
5663
5664 if (length > 16)
5665 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005666
cristy3ed852e2009-09-05 21:47:34 +00005667 else
5668 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005669
cristy3ed852e2009-09-05 21:47:34 +00005670 if (magn_mb == 0)
5671 magn_mb=1;
5672
5673 if (length > 17)
5674 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005675
cristy3ed852e2009-09-05 21:47:34 +00005676 else
5677 magn_methy=magn_methx;
5678
glennrp47b9dd52010-11-24 18:12:06 +00005679
cristy3ed852e2009-09-05 21:47:34 +00005680 if (magn_methx > 5 || magn_methy > 5)
5681 if (mng_info->magn_warning == 0)
5682 {
5683 (void) ThrowMagickException(&image->exception,
5684 GetMagickModule(),CoderError,
5685 "Unknown MAGN method in MNG datastream","`%s'",
5686 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005687
cristy3ed852e2009-09-05 21:47:34 +00005688 mng_info->magn_warning++;
5689 }
5690#ifdef MNG_OBJECT_BUFFERS
5691 /* Magnify existing objects in the range magn_first to magn_last */
5692#endif
5693 if (magn_first == 0 || magn_last == 0)
5694 {
5695 /* Save the magnification factors for object 0 */
5696 mng_info->magn_mb=magn_mb;
5697 mng_info->magn_ml=magn_ml;
5698 mng_info->magn_mr=magn_mr;
5699 mng_info->magn_mt=magn_mt;
5700 mng_info->magn_mx=magn_mx;
5701 mng_info->magn_my=magn_my;
5702 mng_info->magn_methx=magn_methx;
5703 mng_info->magn_methy=magn_methy;
5704 }
5705 }
glennrp47b9dd52010-11-24 18:12:06 +00005706
cristy3ed852e2009-09-05 21:47:34 +00005707 if (memcmp(type,mng_PAST,4) == 0)
5708 {
5709 if (mng_info->past_warning == 0)
5710 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5711 CoderError,"PAST is not implemented yet","`%s'",
5712 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005713
cristy3ed852e2009-09-05 21:47:34 +00005714 mng_info->past_warning++;
5715 }
glennrp47b9dd52010-11-24 18:12:06 +00005716
cristy3ed852e2009-09-05 21:47:34 +00005717 if (memcmp(type,mng_SHOW,4) == 0)
5718 {
5719 if (mng_info->show_warning == 0)
5720 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5721 CoderError,"SHOW is not implemented yet","`%s'",
5722 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005723
cristy3ed852e2009-09-05 21:47:34 +00005724 mng_info->show_warning++;
5725 }
glennrp47b9dd52010-11-24 18:12:06 +00005726
cristy3ed852e2009-09-05 21:47:34 +00005727 if (memcmp(type,mng_sBIT,4) == 0)
5728 {
5729 if (length < 4)
5730 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005731
cristy3ed852e2009-09-05 21:47:34 +00005732 else
5733 {
5734 mng_info->global_sbit.gray=p[0];
5735 mng_info->global_sbit.red=p[0];
5736 mng_info->global_sbit.green=p[1];
5737 mng_info->global_sbit.blue=p[2];
5738 mng_info->global_sbit.alpha=p[3];
5739 mng_info->have_global_sbit=MagickTrue;
5740 }
5741 }
5742 if (memcmp(type,mng_pHYs,4) == 0)
5743 {
5744 if (length > 8)
5745 {
5746 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005747 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005748 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005749 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005750 mng_info->global_phys_unit_type=p[8];
5751 mng_info->have_global_phys=MagickTrue;
5752 }
glennrp47b9dd52010-11-24 18:12:06 +00005753
cristy3ed852e2009-09-05 21:47:34 +00005754 else
5755 mng_info->have_global_phys=MagickFalse;
5756 }
5757 if (memcmp(type,mng_pHYg,4) == 0)
5758 {
5759 if (mng_info->phyg_warning == 0)
5760 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5761 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005762
cristy3ed852e2009-09-05 21:47:34 +00005763 mng_info->phyg_warning++;
5764 }
5765 if (memcmp(type,mng_BASI,4) == 0)
5766 {
5767 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005768
cristy3ed852e2009-09-05 21:47:34 +00005769 if (mng_info->basi_warning == 0)
5770 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5771 CoderError,"BASI is not implemented yet","`%s'",
5772 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005773
cristy3ed852e2009-09-05 21:47:34 +00005774 mng_info->basi_warning++;
5775#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005776 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005777 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005778 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005779 (p[6] << 8) | p[7]);
5780 basi_color_type=p[8];
5781 basi_compression_method=p[9];
5782 basi_filter_type=p[10];
5783 basi_interlace_method=p[11];
5784 if (length > 11)
5785 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005786
cristy3ed852e2009-09-05 21:47:34 +00005787 else
5788 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005789
cristy3ed852e2009-09-05 21:47:34 +00005790 if (length > 13)
5791 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005792
cristy3ed852e2009-09-05 21:47:34 +00005793 else
5794 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005795
cristy3ed852e2009-09-05 21:47:34 +00005796 if (length > 15)
5797 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005798
cristy3ed852e2009-09-05 21:47:34 +00005799 else
5800 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005801
cristy3ed852e2009-09-05 21:47:34 +00005802 if (length > 17)
5803 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005804
cristy3ed852e2009-09-05 21:47:34 +00005805 else
5806 {
5807 if (basi_sample_depth == 16)
5808 basi_alpha=65535L;
5809 else
5810 basi_alpha=255;
5811 }
glennrp47b9dd52010-11-24 18:12:06 +00005812
cristy3ed852e2009-09-05 21:47:34 +00005813 if (length > 19)
5814 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005815
cristy3ed852e2009-09-05 21:47:34 +00005816 else
5817 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005818
cristy3ed852e2009-09-05 21:47:34 +00005819#endif
5820 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5821 continue;
5822 }
glennrp47b9dd52010-11-24 18:12:06 +00005823
cristy3ed852e2009-09-05 21:47:34 +00005824 if (memcmp(type,mng_IHDR,4)
5825#if defined(JNG_SUPPORTED)
5826 && memcmp(type,mng_JHDR,4)
5827#endif
5828 )
5829 {
5830 /* Not an IHDR or JHDR chunk */
5831 if (length)
5832 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005833
cristy3ed852e2009-09-05 21:47:34 +00005834 continue;
5835 }
5836/* Process IHDR */
5837 if (logging != MagickFalse)
5838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5839 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005840
cristy3ed852e2009-09-05 21:47:34 +00005841 mng_info->exists[object_id]=MagickTrue;
5842 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005843
cristy3ed852e2009-09-05 21:47:34 +00005844 if (mng_info->invisible[object_id])
5845 {
5846 if (logging != MagickFalse)
5847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5848 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005849
cristy3ed852e2009-09-05 21:47:34 +00005850 skip_to_iend=MagickTrue;
5851 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5852 continue;
5853 }
5854#if defined(MNG_INSERT_LAYERS)
5855 if (length < 8)
5856 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005857
cristy8182b072010-05-30 20:10:53 +00005858 image_width=(size_t) mng_get_long(p);
5859 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005860#endif
5861 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5862
5863 /*
5864 Insert a transparent background layer behind the entire animation
5865 if it is not full screen.
5866 */
5867#if defined(MNG_INSERT_LAYERS)
5868 if (insert_layers && mng_type && first_mng_object)
5869 {
5870 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5871 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005872 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005873 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005874 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005875 {
5876 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5877 {
5878 /*
5879 Allocate next image structure.
5880 */
5881 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005882
cristy3ed852e2009-09-05 21:47:34 +00005883 if (GetNextImageInList(image) == (Image *) NULL)
5884 {
5885 image=DestroyImageList(image);
5886 MngInfoFreeStruct(mng_info,&have_mng_structure);
5887 return((Image *) NULL);
5888 }
glennrp47b9dd52010-11-24 18:12:06 +00005889
cristy3ed852e2009-09-05 21:47:34 +00005890 image=SyncNextImageInList(image);
5891 }
5892 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005893
cristy3ed852e2009-09-05 21:47:34 +00005894 if (term_chunk_found)
5895 {
5896 image->start_loop=MagickTrue;
5897 image->iterations=mng_iterations;
5898 term_chunk_found=MagickFalse;
5899 }
glennrp47b9dd52010-11-24 18:12:06 +00005900
cristy3ed852e2009-09-05 21:47:34 +00005901 else
5902 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005903
5904 /* Make a background rectangle. */
5905
cristy3ed852e2009-09-05 21:47:34 +00005906 image->delay=0;
5907 image->columns=mng_info->mng_width;
5908 image->rows=mng_info->mng_height;
5909 image->page.width=mng_info->mng_width;
5910 image->page.height=mng_info->mng_height;
5911 image->page.x=0;
5912 image->page.y=0;
5913 image->background_color=mng_background_color;
5914 (void) SetImageBackgroundColor(image);
5915 if (logging != MagickFalse)
5916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005917 " Inserted transparent background layer, W=%.20g, H=%.20g",
5918 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005919 }
5920 }
5921 /*
5922 Insert a background layer behind the upcoming image if
5923 framing_mode is 3, and we haven't already inserted one.
5924 */
5925 if (insert_layers && (mng_info->framing_mode == 3) &&
5926 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5927 (simplicity & 0x08)))
5928 {
5929 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5930 {
5931 /*
5932 Allocate next image structure.
5933 */
5934 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005935
cristy3ed852e2009-09-05 21:47:34 +00005936 if (GetNextImageInList(image) == (Image *) NULL)
5937 {
5938 image=DestroyImageList(image);
5939 MngInfoFreeStruct(mng_info,&have_mng_structure);
5940 return((Image *) NULL);
5941 }
glennrp47b9dd52010-11-24 18:12:06 +00005942
cristy3ed852e2009-09-05 21:47:34 +00005943 image=SyncNextImageInList(image);
5944 }
glennrp0fe50b42010-11-16 03:52:51 +00005945
cristy3ed852e2009-09-05 21:47:34 +00005946 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005947
cristy3ed852e2009-09-05 21:47:34 +00005948 if (term_chunk_found)
5949 {
5950 image->start_loop=MagickTrue;
5951 image->iterations=mng_iterations;
5952 term_chunk_found=MagickFalse;
5953 }
glennrp0fe50b42010-11-16 03:52:51 +00005954
cristy3ed852e2009-09-05 21:47:34 +00005955 else
5956 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005957
cristy3ed852e2009-09-05 21:47:34 +00005958 image->delay=0;
5959 image->columns=subframe_width;
5960 image->rows=subframe_height;
5961 image->page.width=subframe_width;
5962 image->page.height=subframe_height;
5963 image->page.x=mng_info->clip.left;
5964 image->page.y=mng_info->clip.top;
5965 image->background_color=mng_background_color;
5966 image->matte=MagickFalse;
5967 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005968
cristy3ed852e2009-09-05 21:47:34 +00005969 if (logging != MagickFalse)
5970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005971 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005972 (double) mng_info->clip.left,(double) mng_info->clip.right,
5973 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005974 }
5975#endif /* MNG_INSERT_LAYERS */
5976 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005977
cristy3ed852e2009-09-05 21:47:34 +00005978 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5979 {
5980 /*
5981 Allocate next image structure.
5982 */
5983 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005984
cristy3ed852e2009-09-05 21:47:34 +00005985 if (GetNextImageInList(image) == (Image *) NULL)
5986 {
5987 image=DestroyImageList(image);
5988 MngInfoFreeStruct(mng_info,&have_mng_structure);
5989 return((Image *) NULL);
5990 }
glennrp47b9dd52010-11-24 18:12:06 +00005991
cristy3ed852e2009-09-05 21:47:34 +00005992 image=SyncNextImageInList(image);
5993 }
5994 mng_info->image=image;
5995 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5996 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005997
cristy3ed852e2009-09-05 21:47:34 +00005998 if (status == MagickFalse)
5999 break;
glennrp0fe50b42010-11-16 03:52:51 +00006000
cristy3ed852e2009-09-05 21:47:34 +00006001 if (term_chunk_found)
6002 {
6003 image->start_loop=MagickTrue;
6004 term_chunk_found=MagickFalse;
6005 }
glennrp0fe50b42010-11-16 03:52:51 +00006006
cristy3ed852e2009-09-05 21:47:34 +00006007 else
6008 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006009
cristy3ed852e2009-09-05 21:47:34 +00006010 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6011 {
6012 image->delay=frame_delay;
6013 frame_delay=default_frame_delay;
6014 }
glennrp0fe50b42010-11-16 03:52:51 +00006015
cristy3ed852e2009-09-05 21:47:34 +00006016 else
6017 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006018
cristy3ed852e2009-09-05 21:47:34 +00006019 image->page.width=mng_info->mng_width;
6020 image->page.height=mng_info->mng_height;
6021 image->page.x=mng_info->x_off[object_id];
6022 image->page.y=mng_info->y_off[object_id];
6023 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006024
cristy3ed852e2009-09-05 21:47:34 +00006025 /*
6026 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6027 */
glennrp47b9dd52010-11-24 18:12:06 +00006028
cristy3ed852e2009-09-05 21:47:34 +00006029 if (logging != MagickFalse)
6030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6031 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6032 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006033
cristybb503372010-05-27 20:51:26 +00006034 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006035
cristy3ed852e2009-09-05 21:47:34 +00006036 if (offset < 0)
6037 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6038 }
6039
6040 previous=image;
6041 mng_info->image=image;
6042 mng_info->mng_type=mng_type;
6043 mng_info->object_id=object_id;
6044
6045 if (memcmp(type,mng_IHDR,4) == 0)
6046 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006047
cristy3ed852e2009-09-05 21:47:34 +00006048#if defined(JNG_SUPPORTED)
6049 else
6050 image=ReadOneJNGImage(mng_info,image_info,exception);
6051#endif
6052
6053 if (image == (Image *) NULL)
6054 {
6055 if (IsImageObject(previous) != MagickFalse)
6056 {
6057 (void) DestroyImageList(previous);
6058 (void) CloseBlob(previous);
6059 }
glennrp47b9dd52010-11-24 18:12:06 +00006060
cristy3ed852e2009-09-05 21:47:34 +00006061 MngInfoFreeStruct(mng_info,&have_mng_structure);
6062 return((Image *) NULL);
6063 }
glennrp0fe50b42010-11-16 03:52:51 +00006064
cristy3ed852e2009-09-05 21:47:34 +00006065 if (image->columns == 0 || image->rows == 0)
6066 {
6067 (void) CloseBlob(image);
6068 image=DestroyImageList(image);
6069 MngInfoFreeStruct(mng_info,&have_mng_structure);
6070 return((Image *) NULL);
6071 }
glennrp0fe50b42010-11-16 03:52:51 +00006072
cristy3ed852e2009-09-05 21:47:34 +00006073 mng_info->image=image;
6074
6075 if (mng_type)
6076 {
6077 MngBox
6078 crop_box;
6079
6080 if (mng_info->magn_methx || mng_info->magn_methy)
6081 {
6082 png_uint_32
6083 magnified_height,
6084 magnified_width;
6085
6086 if (logging != MagickFalse)
6087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6088 " Processing MNG MAGN chunk");
6089
6090 if (mng_info->magn_methx == 1)
6091 {
6092 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006093
cristy3ed852e2009-09-05 21:47:34 +00006094 if (image->columns > 1)
6095 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006096
cristy3ed852e2009-09-05 21:47:34 +00006097 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006098 magnified_width += (png_uint_32)
6099 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006100 }
glennrp47b9dd52010-11-24 18:12:06 +00006101
cristy3ed852e2009-09-05 21:47:34 +00006102 else
6103 {
cristy4e5bc842010-06-09 13:56:01 +00006104 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006105
cristy3ed852e2009-09-05 21:47:34 +00006106 if (image->columns > 1)
6107 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006108
cristy3ed852e2009-09-05 21:47:34 +00006109 if (image->columns > 2)
6110 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006111
cristy3ed852e2009-09-05 21:47:34 +00006112 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006113 magnified_width += (png_uint_32)
6114 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006115 }
glennrp47b9dd52010-11-24 18:12:06 +00006116
cristy3ed852e2009-09-05 21:47:34 +00006117 if (mng_info->magn_methy == 1)
6118 {
6119 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006120
cristy3ed852e2009-09-05 21:47:34 +00006121 if (image->rows > 1)
6122 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006123
cristy3ed852e2009-09-05 21:47:34 +00006124 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006125 magnified_height += (png_uint_32)
6126 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006127 }
glennrp47b9dd52010-11-24 18:12:06 +00006128
cristy3ed852e2009-09-05 21:47:34 +00006129 else
6130 {
cristy4e5bc842010-06-09 13:56:01 +00006131 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006132
cristy3ed852e2009-09-05 21:47:34 +00006133 if (image->rows > 1)
6134 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006135
cristy3ed852e2009-09-05 21:47:34 +00006136 if (image->rows > 2)
6137 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006138
cristy3ed852e2009-09-05 21:47:34 +00006139 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006140 magnified_height += (png_uint_32)
6141 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006142 }
glennrp47b9dd52010-11-24 18:12:06 +00006143
cristy3ed852e2009-09-05 21:47:34 +00006144 if (magnified_height > image->rows ||
6145 magnified_width > image->columns)
6146 {
6147 Image
6148 *large_image;
6149
6150 int
6151 yy;
6152
cristybb503372010-05-27 20:51:26 +00006153 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006154 m,
6155 y;
6156
cristybb503372010-05-27 20:51:26 +00006157 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006158 x;
6159
6160 register PixelPacket
6161 *n,
6162 *q;
6163
6164 PixelPacket
6165 *next,
6166 *prev;
6167
6168 png_uint_16
6169 magn_methx,
6170 magn_methy;
6171
glennrp47b9dd52010-11-24 18:12:06 +00006172 /* Allocate next image structure. */
6173
cristy3ed852e2009-09-05 21:47:34 +00006174 if (logging != MagickFalse)
6175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6176 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006177
cristy3ed852e2009-09-05 21:47:34 +00006178 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00006179
cristy3ed852e2009-09-05 21:47:34 +00006180 if (GetNextImageInList(image) == (Image *) NULL)
6181 {
6182 image=DestroyImageList(image);
6183 MngInfoFreeStruct(mng_info,&have_mng_structure);
6184 return((Image *) NULL);
6185 }
6186
6187 large_image=SyncNextImageInList(image);
6188
6189 large_image->columns=magnified_width;
6190 large_image->rows=magnified_height;
6191
6192 magn_methx=mng_info->magn_methx;
6193 magn_methy=mng_info->magn_methy;
6194
glennrp3faa9a32011-04-23 14:00:25 +00006195#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006196#define QM unsigned short
6197 if (magn_methx != 1 || magn_methy != 1)
6198 {
6199 /*
6200 Scale pixels to unsigned shorts to prevent
6201 overflow of intermediate values of interpolations
6202 */
cristybb503372010-05-27 20:51:26 +00006203 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006204 {
6205 q=GetAuthenticPixels(image,0,y,image->columns,1,
6206 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006207
cristybb503372010-05-27 20:51:26 +00006208 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006209 {
glennrp7c7b3152011-04-26 04:01:27 +00006210 SetRedPixelComponent(q,ScaleQuantumToShort(
cristy3b6fd2e2011-05-20 12:53:50 +00006211 GetRedPixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006212 SetGreenPixelComponent(q,ScaleQuantumToShort(
cristy3b6fd2e2011-05-20 12:53:50 +00006213 GetGreenPixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006214 SetBluePixelComponent(q,ScaleQuantumToShort(
cristy3b6fd2e2011-05-20 12:53:50 +00006215 GetBluePixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006216 SetOpacityPixelComponent(q,ScaleQuantumToShort(
cristy3b6fd2e2011-05-20 12:53:50 +00006217 GetOpacityPixelComponent(q)));
cristy3ed852e2009-09-05 21:47:34 +00006218 q++;
6219 }
glennrp47b9dd52010-11-24 18:12:06 +00006220
cristy3ed852e2009-09-05 21:47:34 +00006221 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6222 break;
6223 }
6224 }
6225#else
6226#define QM Quantum
6227#endif
6228
6229 if (image->matte != MagickFalse)
6230 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006231
cristy3ed852e2009-09-05 21:47:34 +00006232 else
6233 {
6234 large_image->background_color.opacity=OpaqueOpacity;
6235 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006236
cristy3ed852e2009-09-05 21:47:34 +00006237 if (magn_methx == 4)
6238 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006239
cristy3ed852e2009-09-05 21:47:34 +00006240 if (magn_methx == 5)
6241 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006242
cristy3ed852e2009-09-05 21:47:34 +00006243 if (magn_methy == 4)
6244 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006245
cristy3ed852e2009-09-05 21:47:34 +00006246 if (magn_methy == 5)
6247 magn_methy=3;
6248 }
6249
6250 /* magnify the rows into the right side of the large image */
6251
6252 if (logging != MagickFalse)
6253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006254 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006255 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006256 yy=0;
6257 length=(size_t) image->columns;
6258 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
6259 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006260
cristy3ed852e2009-09-05 21:47:34 +00006261 if ((prev == (PixelPacket *) NULL) ||
6262 (next == (PixelPacket *) NULL))
6263 {
6264 image=DestroyImageList(image);
6265 MngInfoFreeStruct(mng_info,&have_mng_structure);
6266 ThrowReaderException(ResourceLimitError,
6267 "MemoryAllocationFailed");
6268 }
glennrp47b9dd52010-11-24 18:12:06 +00006269
cristy3ed852e2009-09-05 21:47:34 +00006270 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6271 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006272
cristybb503372010-05-27 20:51:26 +00006273 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006274 {
6275 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006276 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006277
cristybb503372010-05-27 20:51:26 +00006278 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6279 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006280
cristybb503372010-05-27 20:51:26 +00006281 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6282 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006283
cristybb503372010-05-27 20:51:26 +00006284 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006285 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006286
cristy3ed852e2009-09-05 21:47:34 +00006287 else
cristybb503372010-05-27 20:51:26 +00006288 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006289
cristy3ed852e2009-09-05 21:47:34 +00006290 n=prev;
6291 prev=next;
6292 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006293
cristybb503372010-05-27 20:51:26 +00006294 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006295 {
6296 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6297 exception);
6298 (void) CopyMagickMemory(next,n,length);
6299 }
glennrp47b9dd52010-11-24 18:12:06 +00006300
cristy3ed852e2009-09-05 21:47:34 +00006301 for (i=0; i < m; i++, yy++)
6302 {
6303 register PixelPacket
6304 *pixels;
6305
cristybb503372010-05-27 20:51:26 +00006306 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006307 pixels=prev;
6308 n=next;
6309 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006310 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00006311 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00006312
cristybb503372010-05-27 20:51:26 +00006313 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006314 {
glennrpfd05d622011-02-25 04:10:33 +00006315 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006316 /*
6317 if (image->storage_class == PseudoClass)
6318 {
6319 }
6320 */
6321
6322 if (magn_methy <= 1)
6323 {
glennrpbb4f99d2011-05-22 11:13:17 +00006324 /* replicate previous */
cristy8d74fc12011-05-25 23:02:47 +00006325 SetRGBOPixelComponents(q,(pixels));
cristy3ed852e2009-09-05 21:47:34 +00006326 }
glennrp47b9dd52010-11-24 18:12:06 +00006327
cristy3ed852e2009-09-05 21:47:34 +00006328 else if (magn_methy == 2 || magn_methy == 4)
6329 {
6330 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006331 {
cristy8d74fc12011-05-25 23:02:47 +00006332 SetRGBOPixelComponents(q,(pixels));
glennrpbb4f99d2011-05-22 11:13:17 +00006333 }
glennrp47b9dd52010-11-24 18:12:06 +00006334
cristy3ed852e2009-09-05 21:47:34 +00006335 else
6336 {
6337 /* Interpolate */
glennrpbb4f99d2011-05-22 11:13:17 +00006338 SetRedPixelComponent(q,
6339 ((QM) (((ssize_t)
6340 (2*i*(GetRedPixelComponent(n)
6341 -GetRedPixelComponent(pixels)+m))/
6342 ((ssize_t) (m*2))
6343 +GetRedPixelComponent(pixels)))));
6344 SetGreenPixelComponent(q,
6345 ((QM) (((ssize_t)
6346 (2*i*(GetGreenPixelComponent(n)
6347 -GetGreenPixelComponent(pixels)+m))/
6348 ((ssize_t) (m*2))
6349 +GetGreenPixelComponent(pixels)))));
6350 SetBluePixelComponent(q,
6351 ((QM) (((ssize_t)
6352 (2*i*(GetBluePixelComponent(n)
6353 -GetBluePixelComponent(pixels)+m))/
6354 ((ssize_t) (m*2))
6355 +GetBluePixelComponent(pixels)))));
glennrp47b9dd52010-11-24 18:12:06 +00006356
cristy3ed852e2009-09-05 21:47:34 +00006357 if (image->matte != MagickFalse)
glennrpbb4f99d2011-05-22 11:13:17 +00006358 SetOpacityPixelComponent(q,
6359 ((QM) (((ssize_t)
6360 (2*i*(GetOpacityPixelComponent(n)
6361 -GetOpacityPixelComponent(pixels)+m))
6362 /((ssize_t) (m*2))+
6363 GetOpacityPixelComponent(pixels)))));
cristy3ed852e2009-09-05 21:47:34 +00006364 }
glennrp47b9dd52010-11-24 18:12:06 +00006365
cristy3ed852e2009-09-05 21:47:34 +00006366 if (magn_methy == 4)
6367 {
6368 /* Replicate nearest */
6369 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006370 SetOpacityPixelComponent(q,
6371 (*pixels).opacity+0);
cristy3ed852e2009-09-05 21:47:34 +00006372 else
glennrpbb4f99d2011-05-22 11:13:17 +00006373 SetOpacityPixelComponent(q,
6374 (*n).opacity+0);
cristy3ed852e2009-09-05 21:47:34 +00006375 }
6376 }
glennrp47b9dd52010-11-24 18:12:06 +00006377
cristy3ed852e2009-09-05 21:47:34 +00006378 else /* if (magn_methy == 3 || magn_methy == 5) */
6379 {
6380 /* Replicate nearest */
6381 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006382 {
cristy8d74fc12011-05-25 23:02:47 +00006383 SetRGBOPixelComponents(q,(pixels));
glennrpbb4f99d2011-05-22 11:13:17 +00006384 }
glennrp47b9dd52010-11-24 18:12:06 +00006385
cristy3ed852e2009-09-05 21:47:34 +00006386 else
glennrpbb4f99d2011-05-22 11:13:17 +00006387 {
cristy8d74fc12011-05-25 23:02:47 +00006388 SetRGBOPixelComponents(q,(n));
glennrpbb4f99d2011-05-22 11:13:17 +00006389 }
glennrp47b9dd52010-11-24 18:12:06 +00006390
cristy3ed852e2009-09-05 21:47:34 +00006391 if (magn_methy == 5)
6392 {
glennrpbb4f99d2011-05-22 11:13:17 +00006393 SetOpacityPixelComponent(q,
6394 (QM) (((ssize_t) (2*i*
6395 (GetOpacityPixelComponent(n)
6396 -GetOpacityPixelComponent(pixels))
6397 +m))/((ssize_t) (m*2))
6398 +GetOpacityPixelComponent(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006399 }
6400 }
6401 n++;
6402 q++;
6403 pixels++;
6404 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006405
cristy3ed852e2009-09-05 21:47:34 +00006406 if (SyncAuthenticPixels(large_image,exception) == 0)
6407 break;
glennrp47b9dd52010-11-24 18:12:06 +00006408
cristy3ed852e2009-09-05 21:47:34 +00006409 } /* i */
6410 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006411
cristy3ed852e2009-09-05 21:47:34 +00006412 prev=(PixelPacket *) RelinquishMagickMemory(prev);
6413 next=(PixelPacket *) RelinquishMagickMemory(next);
6414
6415 length=image->columns;
6416
6417 if (logging != MagickFalse)
6418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6419 " Delete original image");
6420
6421 DeleteImageFromList(&image);
6422
6423 image=large_image;
6424
6425 mng_info->image=image;
6426
6427 /* magnify the columns */
6428 if (logging != MagickFalse)
6429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006430 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006431
cristybb503372010-05-27 20:51:26 +00006432 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006433 {
6434 register PixelPacket
6435 *pixels;
6436
6437 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6438 pixels=q+(image->columns-length);
6439 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00006440
cristybb503372010-05-27 20:51:26 +00006441 for (x=(ssize_t) (image->columns-length);
6442 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006443 {
glennrp7c7b3152011-04-26 04:01:27 +00006444 /* To do: Rewrite using Get/Set***PixelComponent() */
6445
cristybb503372010-05-27 20:51:26 +00006446 if (x == (ssize_t) (image->columns-length))
6447 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006448
cristybb503372010-05-27 20:51:26 +00006449 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6450 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006451
cristybb503372010-05-27 20:51:26 +00006452 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6453 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006454
cristybb503372010-05-27 20:51:26 +00006455 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006456 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006457
cristy3ed852e2009-09-05 21:47:34 +00006458 else
cristybb503372010-05-27 20:51:26 +00006459 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006460
cristy3ed852e2009-09-05 21:47:34 +00006461 for (i=0; i < m; i++)
6462 {
6463 if (magn_methx <= 1)
6464 {
6465 /* replicate previous */
cristy8d74fc12011-05-25 23:02:47 +00006466 SetRGBOPixelComponents(q,(pixels));
cristy3ed852e2009-09-05 21:47:34 +00006467 }
glennrp47b9dd52010-11-24 18:12:06 +00006468
cristy3ed852e2009-09-05 21:47:34 +00006469 else if (magn_methx == 2 || magn_methx == 4)
6470 {
6471 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006472 {
cristy8d74fc12011-05-25 23:02:47 +00006473 SetRGBOPixelComponents(q,(pixels));
glennrpbb4f99d2011-05-22 11:13:17 +00006474 }
glennrp47b9dd52010-11-24 18:12:06 +00006475
glennrpbb4f99d2011-05-22 11:13:17 +00006476 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00006477 else
6478 {
6479 /* Interpolate */
glennrpbb4f99d2011-05-22 11:13:17 +00006480 SetRedPixelComponent(q,
6481 (QM) ((2*i*(
6482 GetRedPixelComponent(n)
6483 -GetRedPixelComponent(pixels))+m)
6484 /((ssize_t) (m*2))+
6485 GetRedPixelComponent(pixels)));
6486
6487 SetGreenPixelComponent(q,
6488 (QM) ((2*i*(
6489 GetGreenPixelComponent(n)
6490 -GetGreenPixelComponent(pixels))+m)
6491 /((ssize_t) (m*2))+
6492 GetGreenPixelComponent(pixels)));
6493
6494 SetBluePixelComponent(q,
6495 (QM) ((2*i*(
6496 GetBluePixelComponent(n)
6497 -GetBluePixelComponent(pixels))+m)
6498 /((ssize_t) (m*2))+
6499 GetBluePixelComponent(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006500 if (image->matte != MagickFalse)
glennrpbb4f99d2011-05-22 11:13:17 +00006501 SetOpacityPixelComponent(q,
6502 (QM) ((2*i*(
6503 GetOpacityPixelComponent(n)
6504 -GetOpacityPixelComponent(pixels))+m)
6505 /((ssize_t) (m*2))+
6506 GetOpacityPixelComponent(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006507 }
glennrp47b9dd52010-11-24 18:12:06 +00006508
cristy3ed852e2009-09-05 21:47:34 +00006509 if (magn_methx == 4)
6510 {
6511 /* Replicate nearest */
6512 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006513 {
6514 SetOpacityPixelComponent(q,
6515 GetOpacityPixelComponent(pixels)+0);
6516 }
cristy3ed852e2009-09-05 21:47:34 +00006517 else
glennrpbb4f99d2011-05-22 11:13:17 +00006518 {
6519 SetOpacityPixelComponent(q,
6520 GetOpacityPixelComponent(n)+0);
6521 }
cristy3ed852e2009-09-05 21:47:34 +00006522 }
6523 }
glennrp47b9dd52010-11-24 18:12:06 +00006524
cristy3ed852e2009-09-05 21:47:34 +00006525 else /* if (magn_methx == 3 || magn_methx == 5) */
6526 {
6527 /* Replicate nearest */
6528 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006529 {
cristy8d74fc12011-05-25 23:02:47 +00006530 SetRGBOPixelComponents(q,(pixels));
glennrpbb4f99d2011-05-22 11:13:17 +00006531 }
glennrp47b9dd52010-11-24 18:12:06 +00006532
cristy3ed852e2009-09-05 21:47:34 +00006533 else
glennrpbb4f99d2011-05-22 11:13:17 +00006534 {
cristy8d74fc12011-05-25 23:02:47 +00006535 SetRGBOPixelComponents(q,(n));
glennrpbb4f99d2011-05-22 11:13:17 +00006536 }
glennrp47b9dd52010-11-24 18:12:06 +00006537
cristy3ed852e2009-09-05 21:47:34 +00006538 if (magn_methx == 5)
6539 {
6540 /* Interpolate */
glennrpbb4f99d2011-05-22 11:13:17 +00006541 SetOpacityPixelComponent(q,
6542 (QM) ((2*i*( GetOpacityPixelComponent(n)
6543 -GetOpacityPixelComponent(pixels))+m)/
6544 ((ssize_t) (m*2))
6545 +GetOpacityPixelComponent(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006546 }
6547 }
6548 q++;
6549 }
6550 n++;
6551 p++;
6552 }
glennrp47b9dd52010-11-24 18:12:06 +00006553
cristy3ed852e2009-09-05 21:47:34 +00006554 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6555 break;
6556 }
glennrp3faa9a32011-04-23 14:00:25 +00006557#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006558 if (magn_methx != 1 || magn_methy != 1)
6559 {
6560 /*
6561 Rescale pixels to Quantum
6562 */
cristybb503372010-05-27 20:51:26 +00006563 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006564 {
6565 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006566
cristybb503372010-05-27 20:51:26 +00006567 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006568 {
glennrp7c7b3152011-04-26 04:01:27 +00006569 SetRedPixelComponent(q,ScaleShortToQuantum(
cristy3b6fd2e2011-05-20 12:53:50 +00006570 GetRedPixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006571 SetGreenPixelComponent(q,ScaleShortToQuantum(
cristy3b6fd2e2011-05-20 12:53:50 +00006572 GetGreenPixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006573 SetBluePixelComponent(q,ScaleShortToQuantum(
cristy3b6fd2e2011-05-20 12:53:50 +00006574 GetBluePixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006575 SetOpacityPixelComponent(q,ScaleShortToQuantum(
cristy3b6fd2e2011-05-20 12:53:50 +00006576 GetOpacityPixelComponent(q)));
cristy3ed852e2009-09-05 21:47:34 +00006577 q++;
6578 }
glennrp47b9dd52010-11-24 18:12:06 +00006579
cristy3ed852e2009-09-05 21:47:34 +00006580 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6581 break;
6582 }
6583 }
6584#endif
6585 if (logging != MagickFalse)
6586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6587 " Finished MAGN processing");
6588 }
6589 }
6590
6591 /*
6592 Crop_box is with respect to the upper left corner of the MNG.
6593 */
6594 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6595 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6596 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6597 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6598 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6599 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6600 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6601 if ((crop_box.left != (mng_info->image_box.left
6602 +mng_info->x_off[object_id])) ||
6603 (crop_box.right != (mng_info->image_box.right
6604 +mng_info->x_off[object_id])) ||
6605 (crop_box.top != (mng_info->image_box.top
6606 +mng_info->y_off[object_id])) ||
6607 (crop_box.bottom != (mng_info->image_box.bottom
6608 +mng_info->y_off[object_id])))
6609 {
6610 if (logging != MagickFalse)
6611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6612 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006613
cristy3ed852e2009-09-05 21:47:34 +00006614 if ((crop_box.left < crop_box.right) &&
6615 (crop_box.top < crop_box.bottom))
6616 {
6617 Image
6618 *im;
6619
6620 RectangleInfo
6621 crop_info;
6622
6623 /*
6624 Crop_info is with respect to the upper left corner of
6625 the image.
6626 */
6627 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6628 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006629 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6630 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006631 image->page.width=image->columns;
6632 image->page.height=image->rows;
6633 image->page.x=0;
6634 image->page.y=0;
6635 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006636
cristy3ed852e2009-09-05 21:47:34 +00006637 if (im != (Image *) NULL)
6638 {
6639 image->columns=im->columns;
6640 image->rows=im->rows;
6641 im=DestroyImage(im);
6642 image->page.width=image->columns;
6643 image->page.height=image->rows;
6644 image->page.x=crop_box.left;
6645 image->page.y=crop_box.top;
6646 }
6647 }
glennrp47b9dd52010-11-24 18:12:06 +00006648
cristy3ed852e2009-09-05 21:47:34 +00006649 else
6650 {
6651 /*
6652 No pixels in crop area. The MNG spec still requires
6653 a layer, though, so make a single transparent pixel in
6654 the top left corner.
6655 */
6656 image->columns=1;
6657 image->rows=1;
6658 image->colors=2;
6659 (void) SetImageBackgroundColor(image);
6660 image->page.width=1;
6661 image->page.height=1;
6662 image->page.x=0;
6663 image->page.y=0;
6664 }
6665 }
6666#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6667 image=mng_info->image;
6668#endif
6669 }
6670
glennrp2b013e42010-11-24 16:55:50 +00006671#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6672 /* PNG does not handle depths greater than 16 so reduce it even
6673 * if lossy
6674 */
6675 if (image->depth > 16)
6676 image->depth=16;
6677#endif
6678
glennrp3faa9a32011-04-23 14:00:25 +00006679#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006680 if (LosslessReduceDepthOK(image) != MagickFalse)
6681 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006682#endif
glennrpd6afd542010-11-19 01:53:05 +00006683
cristy3ed852e2009-09-05 21:47:34 +00006684 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006685
cristy3ed852e2009-09-05 21:47:34 +00006686 if (image_info->number_scenes != 0)
6687 {
6688 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006689 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006690 break;
6691 }
glennrpd6afd542010-11-19 01:53:05 +00006692
cristy3ed852e2009-09-05 21:47:34 +00006693 if (logging != MagickFalse)
6694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6695 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006696
cristy3ed852e2009-09-05 21:47:34 +00006697 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006698
cristy3ed852e2009-09-05 21:47:34 +00006699 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006700
cristy3ed852e2009-09-05 21:47:34 +00006701 if (logging != MagickFalse)
6702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6703 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006704
cristy3ed852e2009-09-05 21:47:34 +00006705#if defined(MNG_INSERT_LAYERS)
6706 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6707 (mng_info->mng_height))
6708 {
6709 /*
6710 Insert a background layer if nothing else was found.
6711 */
6712 if (logging != MagickFalse)
6713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6714 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006715
cristy3ed852e2009-09-05 21:47:34 +00006716 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6717 {
6718 /*
6719 Allocate next image structure.
6720 */
6721 AcquireNextImage(image_info,image);
6722 if (GetNextImageInList(image) == (Image *) NULL)
6723 {
6724 image=DestroyImageList(image);
6725 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006726
cristy3ed852e2009-09-05 21:47:34 +00006727 if (logging != MagickFalse)
6728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6729 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006730
cristy3ed852e2009-09-05 21:47:34 +00006731 return((Image *) NULL);
6732 }
6733 image=SyncNextImageInList(image);
6734 }
6735 image->columns=mng_info->mng_width;
6736 image->rows=mng_info->mng_height;
6737 image->page.width=mng_info->mng_width;
6738 image->page.height=mng_info->mng_height;
6739 image->page.x=0;
6740 image->page.y=0;
6741 image->background_color=mng_background_color;
6742 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006743
cristy3ed852e2009-09-05 21:47:34 +00006744 if (image_info->ping == MagickFalse)
6745 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006746
cristy3ed852e2009-09-05 21:47:34 +00006747 mng_info->image_found++;
6748 }
6749#endif
6750 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006751
cristy3ed852e2009-09-05 21:47:34 +00006752 if (mng_iterations == 1)
6753 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006754
cristy3ed852e2009-09-05 21:47:34 +00006755 while (GetPreviousImageInList(image) != (Image *) NULL)
6756 {
6757 image_count++;
6758 if (image_count > 10*mng_info->image_found)
6759 {
6760 if (logging != MagickFalse)
6761 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006762
cristy3ed852e2009-09-05 21:47:34 +00006763 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6764 CoderError,"Linked list is corrupted, beginning of list not found",
6765 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006766
cristy3ed852e2009-09-05 21:47:34 +00006767 return((Image *) NULL);
6768 }
glennrp0fe50b42010-11-16 03:52:51 +00006769
cristy3ed852e2009-09-05 21:47:34 +00006770 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006771
cristy3ed852e2009-09-05 21:47:34 +00006772 if (GetNextImageInList(image) == (Image *) NULL)
6773 {
6774 if (logging != MagickFalse)
6775 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006776
cristy3ed852e2009-09-05 21:47:34 +00006777 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6778 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6779 image_info->filename);
6780 }
6781 }
glennrp47b9dd52010-11-24 18:12:06 +00006782
cristy3ed852e2009-09-05 21:47:34 +00006783 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6784 GetNextImageInList(image) ==
6785 (Image *) NULL)
6786 {
6787 if (logging != MagickFalse)
6788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6789 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006790
cristy3ed852e2009-09-05 21:47:34 +00006791 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6792 CoderError,"image->next for first image is NULL but shouldn't be.",
6793 "`%s'",image_info->filename);
6794 }
glennrp47b9dd52010-11-24 18:12:06 +00006795
cristy3ed852e2009-09-05 21:47:34 +00006796 if (mng_info->image_found == 0)
6797 {
6798 if (logging != MagickFalse)
6799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6800 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006801
cristy3ed852e2009-09-05 21:47:34 +00006802 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6803 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006804
cristy3ed852e2009-09-05 21:47:34 +00006805 if (image != (Image *) NULL)
6806 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006807
cristy3ed852e2009-09-05 21:47:34 +00006808 MngInfoFreeStruct(mng_info,&have_mng_structure);
6809 return((Image *) NULL);
6810 }
6811
6812 if (mng_info->ticks_per_second)
6813 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6814 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006815
cristy3ed852e2009-09-05 21:47:34 +00006816 else
6817 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006818
cristy3ed852e2009-09-05 21:47:34 +00006819 /* Find final nonzero image delay */
6820 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006821
cristy3ed852e2009-09-05 21:47:34 +00006822 while (GetNextImageInList(image) != (Image *) NULL)
6823 {
6824 if (image->delay)
6825 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006826
cristy3ed852e2009-09-05 21:47:34 +00006827 image=GetNextImageInList(image);
6828 }
glennrp0fe50b42010-11-16 03:52:51 +00006829
cristy3ed852e2009-09-05 21:47:34 +00006830 if (final_delay < final_image_delay)
6831 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006832
cristy3ed852e2009-09-05 21:47:34 +00006833 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006834
cristy3ed852e2009-09-05 21:47:34 +00006835 if (logging != MagickFalse)
6836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006837 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6838 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006839
cristy3ed852e2009-09-05 21:47:34 +00006840 if (logging != MagickFalse)
6841 {
6842 int
6843 scene;
6844
6845 scene=0;
6846 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006847
cristy3ed852e2009-09-05 21:47:34 +00006848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6849 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006850
cristy3ed852e2009-09-05 21:47:34 +00006851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006852 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006853
cristy3ed852e2009-09-05 21:47:34 +00006854 while (GetNextImageInList(image) != (Image *) NULL)
6855 {
6856 image=GetNextImageInList(image);
6857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006858 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006859 }
6860 }
6861
6862 image=GetFirstImageInList(image);
6863#ifdef MNG_COALESCE_LAYERS
6864 if (insert_layers)
6865 {
6866 Image
6867 *next_image,
6868 *next;
6869
cristybb503372010-05-27 20:51:26 +00006870 size_t
cristy3ed852e2009-09-05 21:47:34 +00006871 scene;
6872
6873 if (logging != MagickFalse)
6874 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006875
cristy3ed852e2009-09-05 21:47:34 +00006876 scene=image->scene;
6877 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006878
cristy3ed852e2009-09-05 21:47:34 +00006879 if (next_image == (Image *) NULL)
6880 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006881
cristy3ed852e2009-09-05 21:47:34 +00006882 image=DestroyImageList(image);
6883 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006884
cristy3ed852e2009-09-05 21:47:34 +00006885 for (next=image; next != (Image *) NULL; next=next_image)
6886 {
6887 next->page.width=mng_info->mng_width;
6888 next->page.height=mng_info->mng_height;
6889 next->page.x=0;
6890 next->page.y=0;
6891 next->scene=scene++;
6892 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006893
cristy3ed852e2009-09-05 21:47:34 +00006894 if (next_image == (Image *) NULL)
6895 break;
glennrp47b9dd52010-11-24 18:12:06 +00006896
cristy3ed852e2009-09-05 21:47:34 +00006897 if (next->delay == 0)
6898 {
6899 scene--;
6900 next_image->previous=GetPreviousImageInList(next);
6901 if (GetPreviousImageInList(next) == (Image *) NULL)
6902 image=next_image;
6903 else
6904 next->previous->next=next_image;
6905 next=DestroyImage(next);
6906 }
6907 }
6908 }
6909#endif
6910
6911 while (GetNextImageInList(image) != (Image *) NULL)
6912 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006913
cristy3ed852e2009-09-05 21:47:34 +00006914 image->dispose=BackgroundDispose;
6915
6916 if (logging != MagickFalse)
6917 {
6918 int
6919 scene;
6920
6921 scene=0;
6922 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006923
cristy3ed852e2009-09-05 21:47:34 +00006924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6925 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006926
cristy3ed852e2009-09-05 21:47:34 +00006927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006928 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6929 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006930
cristy3ed852e2009-09-05 21:47:34 +00006931 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006932 {
6933 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006934
cristyf2faecf2010-05-28 19:19:36 +00006935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006936 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6937 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006938 }
6939 }
glennrp47b9dd52010-11-24 18:12:06 +00006940
cristy3ed852e2009-09-05 21:47:34 +00006941 image=GetFirstImageInList(image);
6942 MngInfoFreeStruct(mng_info,&have_mng_structure);
6943 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006944
cristy3ed852e2009-09-05 21:47:34 +00006945 if (logging != MagickFalse)
6946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006947
cristy3ed852e2009-09-05 21:47:34 +00006948 return(GetFirstImageInList(image));
6949}
glennrp25c1e2b2010-03-25 01:39:56 +00006950#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006951static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6952{
6953 printf("Your PNG library is too old: You have libpng-%s\n",
6954 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006955
cristy3ed852e2009-09-05 21:47:34 +00006956 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6957 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006958
cristy3ed852e2009-09-05 21:47:34 +00006959 return(Image *) NULL;
6960}
glennrp47b9dd52010-11-24 18:12:06 +00006961
cristy3ed852e2009-09-05 21:47:34 +00006962static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6963{
6964 return(ReadPNGImage(image_info,exception));
6965}
glennrp25c1e2b2010-03-25 01:39:56 +00006966#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006967#endif
6968
6969/*
6970%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6971% %
6972% %
6973% %
6974% R e g i s t e r P N G I m a g e %
6975% %
6976% %
6977% %
6978%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6979%
6980% RegisterPNGImage() adds properties for the PNG image format to
6981% the list of supported formats. The properties include the image format
6982% tag, a method to read and/or write the format, whether the format
6983% supports the saving of more than one frame to the same file or blob,
6984% whether the format supports native in-memory I/O, and a brief
6985% description of the format.
6986%
6987% The format of the RegisterPNGImage method is:
6988%
cristybb503372010-05-27 20:51:26 +00006989% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006990%
6991*/
cristybb503372010-05-27 20:51:26 +00006992ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006993{
6994 char
6995 version[MaxTextExtent];
6996
6997 MagickInfo
6998 *entry;
6999
7000 static const char
7001 *PNGNote=
7002 {
7003 "See http://www.libpng.org/ for details about the PNG format."
7004 },
glennrp47b9dd52010-11-24 18:12:06 +00007005
cristy3ed852e2009-09-05 21:47:34 +00007006 *JNGNote=
7007 {
7008 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7009 "format."
7010 },
glennrp47b9dd52010-11-24 18:12:06 +00007011
cristy3ed852e2009-09-05 21:47:34 +00007012 *MNGNote=
7013 {
7014 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7015 "format."
7016 };
7017
7018 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007019
cristy3ed852e2009-09-05 21:47:34 +00007020#if defined(PNG_LIBPNG_VER_STRING)
7021 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7022 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007023
cristy3ed852e2009-09-05 21:47:34 +00007024 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7025 {
7026 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7027 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7028 MaxTextExtent);
7029 }
7030#endif
glennrp47b9dd52010-11-24 18:12:06 +00007031
cristy3ed852e2009-09-05 21:47:34 +00007032 entry=SetMagickInfo("MNG");
7033 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007034
cristy3ed852e2009-09-05 21:47:34 +00007035#if defined(MAGICKCORE_PNG_DELEGATE)
7036 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7037 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7038#endif
glennrp47b9dd52010-11-24 18:12:06 +00007039
cristy3ed852e2009-09-05 21:47:34 +00007040 entry->magick=(IsImageFormatHandler *) IsMNG;
7041 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007042
cristy3ed852e2009-09-05 21:47:34 +00007043 if (*version != '\0')
7044 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007045
cristy3ed852e2009-09-05 21:47:34 +00007046 entry->module=ConstantString("PNG");
7047 entry->note=ConstantString(MNGNote);
7048 (void) RegisterMagickInfo(entry);
7049
7050 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007051
cristy3ed852e2009-09-05 21:47:34 +00007052#if defined(MAGICKCORE_PNG_DELEGATE)
7053 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7054 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7055#endif
glennrp47b9dd52010-11-24 18:12:06 +00007056
cristy3ed852e2009-09-05 21:47:34 +00007057 entry->magick=(IsImageFormatHandler *) IsPNG;
7058 entry->adjoin=MagickFalse;
7059 entry->description=ConstantString("Portable Network Graphics");
7060 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007061
cristy3ed852e2009-09-05 21:47:34 +00007062 if (*version != '\0')
7063 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007064
cristy3ed852e2009-09-05 21:47:34 +00007065 entry->note=ConstantString(PNGNote);
7066 (void) RegisterMagickInfo(entry);
7067
7068 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007069
cristy3ed852e2009-09-05 21:47:34 +00007070#if defined(MAGICKCORE_PNG_DELEGATE)
7071 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7072 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7073#endif
glennrp47b9dd52010-11-24 18:12:06 +00007074
cristy3ed852e2009-09-05 21:47:34 +00007075 entry->magick=(IsImageFormatHandler *) IsPNG;
7076 entry->adjoin=MagickFalse;
7077 entry->description=ConstantString(
7078 "8-bit indexed with optional binary transparency");
7079 entry->module=ConstantString("PNG");
7080 (void) RegisterMagickInfo(entry);
7081
7082 entry=SetMagickInfo("PNG24");
7083 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007084
cristy3ed852e2009-09-05 21:47:34 +00007085#if defined(ZLIB_VERSION)
7086 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7087 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007088
cristy3ed852e2009-09-05 21:47:34 +00007089 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7090 {
7091 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7092 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7093 }
7094#endif
glennrp47b9dd52010-11-24 18:12:06 +00007095
cristy3ed852e2009-09-05 21:47:34 +00007096 if (*version != '\0')
7097 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007098
cristy3ed852e2009-09-05 21:47:34 +00007099#if defined(MAGICKCORE_PNG_DELEGATE)
7100 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7101 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7102#endif
glennrp47b9dd52010-11-24 18:12:06 +00007103
cristy3ed852e2009-09-05 21:47:34 +00007104 entry->magick=(IsImageFormatHandler *) IsPNG;
7105 entry->adjoin=MagickFalse;
7106 entry->description=ConstantString("opaque 24-bit RGB");
7107 entry->module=ConstantString("PNG");
7108 (void) RegisterMagickInfo(entry);
7109
7110 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007111
cristy3ed852e2009-09-05 21:47:34 +00007112#if defined(MAGICKCORE_PNG_DELEGATE)
7113 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7114 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7115#endif
glennrp47b9dd52010-11-24 18:12:06 +00007116
cristy3ed852e2009-09-05 21:47:34 +00007117 entry->magick=(IsImageFormatHandler *) IsPNG;
7118 entry->adjoin=MagickFalse;
7119 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7120 entry->module=ConstantString("PNG");
7121 (void) RegisterMagickInfo(entry);
7122
7123 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007124
cristy3ed852e2009-09-05 21:47:34 +00007125#if defined(JNG_SUPPORTED)
7126#if defined(MAGICKCORE_PNG_DELEGATE)
7127 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7128 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7129#endif
7130#endif
glennrp47b9dd52010-11-24 18:12:06 +00007131
cristy3ed852e2009-09-05 21:47:34 +00007132 entry->magick=(IsImageFormatHandler *) IsJNG;
7133 entry->adjoin=MagickFalse;
7134 entry->description=ConstantString("JPEG Network Graphics");
7135 entry->module=ConstantString("PNG");
7136 entry->note=ConstantString(JNGNote);
7137 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007138
cristy18b17442009-10-25 18:36:48 +00007139#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007140 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007141#endif
glennrp47b9dd52010-11-24 18:12:06 +00007142
cristy3ed852e2009-09-05 21:47:34 +00007143 return(MagickImageCoderSignature);
7144}
7145
7146/*
7147%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7148% %
7149% %
7150% %
7151% U n r e g i s t e r P N G I m a g e %
7152% %
7153% %
7154% %
7155%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7156%
7157% UnregisterPNGImage() removes format registrations made by the
7158% PNG module from the list of supported formats.
7159%
7160% The format of the UnregisterPNGImage method is:
7161%
7162% UnregisterPNGImage(void)
7163%
7164*/
7165ModuleExport void UnregisterPNGImage(void)
7166{
7167 (void) UnregisterMagickInfo("MNG");
7168 (void) UnregisterMagickInfo("PNG");
7169 (void) UnregisterMagickInfo("PNG8");
7170 (void) UnregisterMagickInfo("PNG24");
7171 (void) UnregisterMagickInfo("PNG32");
7172 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007173
cristy3ed852e2009-09-05 21:47:34 +00007174#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007175 if (ping_semaphore != (SemaphoreInfo *) NULL)
7176 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007177#endif
7178}
7179
7180#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007181#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007182/*
7183%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7184% %
7185% %
7186% %
7187% W r i t e M N G I m a g e %
7188% %
7189% %
7190% %
7191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7192%
7193% WriteMNGImage() writes an image in the Portable Network Graphics
7194% Group's "Multiple-image Network Graphics" encoded image format.
7195%
7196% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7197%
7198% The format of the WriteMNGImage method is:
7199%
7200% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
7201%
7202% A description of each parameter follows.
7203%
7204% o image_info: the image info.
7205%
7206% o image: The image.
7207%
7208%
7209% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7210% "To do" under ReadPNGImage):
7211%
cristy3ed852e2009-09-05 21:47:34 +00007212% Preserve all unknown and not-yet-handled known chunks found in input
7213% PNG file and copy them into output PNG files according to the PNG
7214% copying rules.
7215%
7216% Write the iCCP chunk at MNG level when (icc profile length > 0)
7217%
7218% Improve selection of color type (use indexed-colour or indexed-colour
7219% with tRNS when 256 or fewer unique RGBA values are present).
7220%
7221% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7222% This will be complicated if we limit ourselves to generating MNG-LC
7223% files. For now we ignore disposal method 3 and simply overlay the next
7224% image on it.
7225%
7226% Check for identical PLTE's or PLTE/tRNS combinations and use a
7227% global MNG PLTE or PLTE/tRNS combination when appropriate.
7228% [mostly done 15 June 1999 but still need to take care of tRNS]
7229%
7230% Check for identical sRGB and replace with a global sRGB (and remove
7231% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7232% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7233% local gAMA/cHRM with local sRGB if appropriate).
7234%
7235% Check for identical sBIT chunks and write global ones.
7236%
7237% Provide option to skip writing the signature tEXt chunks.
7238%
7239% Use signatures to detect identical objects and reuse the first
7240% instance of such objects instead of writing duplicate objects.
7241%
7242% Use a smaller-than-32k value of compression window size when
7243% appropriate.
7244%
7245% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7246% ancillary text chunks and save profiles.
7247%
7248% Provide an option to force LC files (to ensure exact framing rate)
7249% instead of VLC.
7250%
7251% Provide an option to force VLC files instead of LC, even when offsets
7252% are present. This will involve expanding the embedded images with a
7253% transparent region at the top and/or left.
7254*/
7255
cristy3ed852e2009-09-05 21:47:34 +00007256static void
glennrpcf002022011-01-30 02:38:15 +00007257Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007258 png_info *ping_info, unsigned char *profile_type, unsigned char
7259 *profile_description, unsigned char *profile_data, png_uint_32 length)
7260{
cristy3ed852e2009-09-05 21:47:34 +00007261 png_textp
7262 text;
7263
cristybb503372010-05-27 20:51:26 +00007264 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007265 i;
7266
7267 unsigned char
7268 *sp;
7269
7270 png_charp
7271 dp;
7272
7273 png_uint_32
7274 allocated_length,
7275 description_length;
7276
7277 unsigned char
7278 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007279
cristy3ed852e2009-09-05 21:47:34 +00007280 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7281 return;
7282
7283 if (image_info->verbose)
7284 {
glennrp0fe50b42010-11-16 03:52:51 +00007285 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7286 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007287 }
glennrp0fe50b42010-11-16 03:52:51 +00007288
cristy3ed852e2009-09-05 21:47:34 +00007289 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7290 description_length=(png_uint_32) strlen((const char *) profile_description);
7291 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7292 + description_length);
7293 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7294 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7295 text[0].key[0]='\0';
7296 (void) ConcatenateMagickString(text[0].key,
7297 "Raw profile type ",MaxTextExtent);
7298 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7299 sp=profile_data;
7300 dp=text[0].text;
7301 *dp++='\n';
7302 (void) CopyMagickString(dp,(const char *) profile_description,
7303 allocated_length);
7304 dp+=description_length;
7305 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007306 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007307 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007308 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007309
cristybb503372010-05-27 20:51:26 +00007310 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007311 {
7312 if (i%36 == 0)
7313 *dp++='\n';
7314 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7315 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7316 }
glennrp47b9dd52010-11-24 18:12:06 +00007317
cristy3ed852e2009-09-05 21:47:34 +00007318 *dp++='\n';
7319 *dp='\0';
7320 text[0].text_length=(png_size_t) (dp-text[0].text);
7321 text[0].compression=image_info->compression == NoCompression ||
7322 (image_info->compression == UndefinedCompression &&
7323 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007324
cristy3ed852e2009-09-05 21:47:34 +00007325 if (text[0].text_length <= allocated_length)
7326 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007327
cristy3ed852e2009-09-05 21:47:34 +00007328 png_free(ping,text[0].text);
7329 png_free(ping,text[0].key);
7330 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007331}
7332
glennrpcf002022011-01-30 02:38:15 +00007333static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007334 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007335{
7336 char
7337 *name;
7338
7339 const StringInfo
7340 *profile;
7341
7342 unsigned char
7343 *data;
7344
7345 png_uint_32 length;
7346
7347 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007348
7349 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7350 {
cristy3ed852e2009-09-05 21:47:34 +00007351 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007352
cristy3ed852e2009-09-05 21:47:34 +00007353 if (profile != (const StringInfo *) NULL)
7354 {
7355 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007356 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007357
glennrp47b9dd52010-11-24 18:12:06 +00007358 if (LocaleNCompare(name,string,11) == 0)
7359 {
7360 if (logging != MagickFalse)
7361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7362 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007363
glennrpcf002022011-01-30 02:38:15 +00007364 ping_profile=CloneStringInfo(profile);
7365 data=GetStringInfoDatum(ping_profile),
7366 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007367 data[4]=data[3];
7368 data[3]=data[2];
7369 data[2]=data[1];
7370 data[1]=data[0];
7371 (void) WriteBlobMSBULong(image,length-5); /* data length */
7372 (void) WriteBlob(image,length-1,data+1);
7373 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007374 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007375 }
cristy3ed852e2009-09-05 21:47:34 +00007376 }
glennrp47b9dd52010-11-24 18:12:06 +00007377
cristy3ed852e2009-09-05 21:47:34 +00007378 name=GetNextImageProfile(image);
7379 }
glennrp47b9dd52010-11-24 18:12:06 +00007380
cristy3ed852e2009-09-05 21:47:34 +00007381 return(MagickTrue);
7382}
7383
glennrpb9cfe272010-12-21 15:08:06 +00007384
cristy3ed852e2009-09-05 21:47:34 +00007385/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007386static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7387 const ImageInfo *IMimage_info,Image *IMimage)
7388{
7389 Image
7390 *image;
7391
7392 ImageInfo
7393 *image_info;
7394
cristy3ed852e2009-09-05 21:47:34 +00007395 char
7396 s[2];
7397
7398 const char
7399 *name,
7400 *property,
7401 *value;
7402
7403 const StringInfo
7404 *profile;
7405
cristy3ed852e2009-09-05 21:47:34 +00007406 int
cristy3ed852e2009-09-05 21:47:34 +00007407 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007408 pass;
7409
glennrpe9c26dc2010-05-30 01:56:35 +00007410 png_byte
7411 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007412
glennrp39992b42010-11-14 00:03:43 +00007413 png_color
7414 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007415
glennrp5af765f2010-03-30 11:12:18 +00007416 png_color_16
7417 ping_background,
7418 ping_trans_color;
7419
cristy3ed852e2009-09-05 21:47:34 +00007420 png_info
7421 *ping_info;
7422
7423 png_struct
7424 *ping;
7425
glennrp5af765f2010-03-30 11:12:18 +00007426 png_uint_32
7427 ping_height,
7428 ping_width;
7429
cristybb503372010-05-27 20:51:26 +00007430 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007431 y;
7432
7433 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007434 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007435 logging,
glennrp58e01762011-01-07 15:28:54 +00007436 matte,
7437
glennrpda8f3a72011-02-27 23:54:12 +00007438 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007439 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007440 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007441 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007442 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007443 ping_have_bKGD,
7444 ping_have_pHYs,
7445 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007446
7447 ping_exclude_bKGD,
7448 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007449 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007450 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007451 ping_exclude_gAMA,
7452 ping_exclude_iCCP,
7453 /* ping_exclude_iTXt, */
7454 ping_exclude_oFFs,
7455 ping_exclude_pHYs,
7456 ping_exclude_sRGB,
7457 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007458 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007459 ping_exclude_vpAg,
7460 ping_exclude_zCCP, /* hex-encoded iCCP */
7461 ping_exclude_zTXt,
7462
glennrp8d3d6e52011-04-19 04:39:51 +00007463 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007464 ping_need_colortype_warning,
7465
glennrp82b3c532011-03-22 19:20:54 +00007466 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007467 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007468 tried_333,
7469 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007470
7471 QuantumInfo
7472 *quantum_info;
7473
cristybb503372010-05-27 20:51:26 +00007474 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007475 i,
7476 x;
7477
7478 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007479 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007480
glennrp5af765f2010-03-30 11:12:18 +00007481 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007482 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007483 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007484 ping_color_type,
7485 ping_interlace_method,
7486 ping_compression_method,
7487 ping_filter_method,
7488 ping_num_trans;
7489
cristybb503372010-05-27 20:51:26 +00007490 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007491 image_depth,
7492 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007493
cristybb503372010-05-27 20:51:26 +00007494 size_t
cristy3ed852e2009-09-05 21:47:34 +00007495 quality,
7496 rowbytes,
7497 save_image_depth;
7498
glennrpdfd70802010-11-14 01:23:35 +00007499 int
glennrpfd05d622011-02-25 04:10:33 +00007500 j,
glennrpf09bded2011-01-08 01:15:59 +00007501 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007502 number_opaque,
7503 number_semitransparent,
7504 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007505 ping_pHYs_unit_type;
7506
7507 png_uint_32
7508 ping_pHYs_x_resolution,
7509 ping_pHYs_y_resolution;
7510
cristy3ed852e2009-09-05 21:47:34 +00007511 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007512 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007513
glennrpb9cfe272010-12-21 15:08:06 +00007514 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7515 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007516 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007517 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007518
cristy3ed852e2009-09-05 21:47:34 +00007519#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007520 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007521#endif
7522
glennrp5af765f2010-03-30 11:12:18 +00007523 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007524 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007525 ping_color_type=0,
7526 ping_interlace_method=0,
7527 ping_compression_method=0,
7528 ping_filter_method=0,
7529 ping_num_trans = 0;
7530
7531 ping_background.red = 0;
7532 ping_background.green = 0;
7533 ping_background.blue = 0;
7534 ping_background.gray = 0;
7535 ping_background.index = 0;
7536
7537 ping_trans_color.red=0;
7538 ping_trans_color.green=0;
7539 ping_trans_color.blue=0;
7540 ping_trans_color.gray=0;
7541
glennrpdfd70802010-11-14 01:23:35 +00007542 ping_pHYs_unit_type = 0;
7543 ping_pHYs_x_resolution = 0;
7544 ping_pHYs_y_resolution = 0;
7545
glennrpda8f3a72011-02-27 23:54:12 +00007546 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007547 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007548 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007549 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007550 ping_have_bKGD=MagickFalse;
7551 ping_have_pHYs=MagickFalse;
7552 ping_have_tRNS=MagickFalse;
7553
glennrp0e8ea192010-12-24 18:00:33 +00007554 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7555 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007556 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007557 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007558 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007559 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7560 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7561 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7562 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7563 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7564 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007565 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007566 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7567 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7568 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7569
glennrp8d3d6e52011-04-19 04:39:51 +00007570 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007571 ping_need_colortype_warning = MagickFalse;
7572
glennrp8bb3a022010-12-13 20:40:04 +00007573 number_opaque = 0;
7574 number_semitransparent = 0;
7575 number_transparent = 0;
7576
glennrpfd05d622011-02-25 04:10:33 +00007577 if (logging != MagickFalse)
7578 {
7579 if (image->storage_class == UndefinedClass)
7580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7581 " storage_class=UndefinedClass");
7582 if (image->storage_class == DirectClass)
7583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7584 " storage_class=DirectClass");
7585 if (image->storage_class == PseudoClass)
7586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7587 " storage_class=PseudoClass");
7588 }
glennrp28af3712011-04-06 18:07:30 +00007589
glennrpc6c391a2011-04-27 02:23:56 +00007590 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007591 {
glennrpc6c391a2011-04-27 02:23:56 +00007592 if (image->storage_class != PseudoClass && image->colormap != NULL)
7593 {
7594 /* Free the bogus colormap; it can cause trouble later */
7595 if (logging != MagickFalse)
7596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7597 " Freeing bogus colormap");
7598 (void *) RelinquishMagickMemory(image->colormap);
7599 image->colormap=NULL;
7600 }
glennrp28af3712011-04-06 18:07:30 +00007601 }
glennrpbb4f99d2011-05-22 11:13:17 +00007602
cristy3ed852e2009-09-05 21:47:34 +00007603 if (image->colorspace != RGBColorspace)
7604 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007605
glennrp3241bd02010-12-12 04:36:28 +00007606 /*
7607 Sometimes we get PseudoClass images whose RGB values don't match
7608 the colors in the colormap. This code syncs the RGB values.
7609 */
7610 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7611 (void) SyncImage(image);
7612
glennrpa6a06632011-01-19 15:15:34 +00007613#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7614 if (image->depth > 8)
7615 {
7616 if (logging != MagickFalse)
7617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7618 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7619
7620 image->depth=8;
7621 }
7622#endif
7623
glennrp8e58efd2011-05-20 12:16:29 +00007624 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007625 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7626 {
glennrp8e58efd2011-05-20 12:16:29 +00007627 register PixelPacket
7628 *r;
7629
7630 ExceptionInfo
7631 *exception;
7632
7633 exception=(&image->exception);
7634
7635 if (image->depth > 8)
7636 {
7637#if MAGICKCORE_QUANTUM_DEPTH > 16
7638 /* Scale to 16-bit */
7639 LBR16RGBOPixelPacketComponent(image->background_color);
7640
7641 for (y=0; y < (ssize_t) image->rows; y++)
7642 {
7643 r=GetAuthenticPixels(image,0,y,image->columns,1,
7644 exception);
7645
7646 if (r == (PixelPacket *) NULL)
7647 break;
7648
7649 for (x=0; x < (ssize_t) image->columns; x++)
7650 {
7651 LBR16RGBOPixelComponent(r);
7652 r++;
7653 }
glennrpbb4f99d2011-05-22 11:13:17 +00007654
glennrp8e58efd2011-05-20 12:16:29 +00007655 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7656 break;
7657 }
7658
7659 if (image->storage_class == PseudoClass && image->colormap != NULL)
7660 {
cristy3e08f112011-05-24 13:19:30 +00007661 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007662 {
7663 LBR16RGBOPixelPacketComponent(image->colormap[i]);
7664 }
7665 }
7666#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7667 }
7668
7669 else if (image->depth > 4)
7670 {
7671#if MAGICKCORE_QUANTUM_DEPTH > 8
7672 /* Scale to 8-bit */
7673 LBR08RGBOPixelPacketComponent(image->background_color);
7674
7675 for (y=0; y < (ssize_t) image->rows; y++)
7676 {
7677 r=GetAuthenticPixels(image,0,y,image->columns,1,
7678 exception);
7679
7680 if (r == (PixelPacket *) NULL)
7681 break;
7682
7683 for (x=0; x < (ssize_t) image->columns; x++)
7684 {
7685 LBR08RGBOPixelComponent(r);
7686 r++;
7687 }
glennrpbb4f99d2011-05-22 11:13:17 +00007688
glennrp8e58efd2011-05-20 12:16:29 +00007689 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7690 break;
7691 }
7692
7693 if (image->storage_class == PseudoClass && image->colormap != NULL)
7694 {
cristy3e08f112011-05-24 13:19:30 +00007695 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007696 {
7697 LBR08RGBOPixelPacketComponent(image->colormap[i]);
7698 }
7699 }
7700#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7701 }
7702 else
7703 if (image->depth > 2)
7704 {
7705 /* Scale to 4-bit */
7706 LBR04RGBOPixelPacketComponent(image->background_color);
7707
7708 for (y=0; y < (ssize_t) image->rows; y++)
7709 {
7710 r=GetAuthenticPixels(image,0,y,image->columns,1,
7711 exception);
7712
7713 if (r == (PixelPacket *) NULL)
7714 break;
7715
7716 for (x=0; x < (ssize_t) image->columns; x++)
7717 {
7718 LBR04RGBOPixelComponent(r);
7719 r++;
7720 }
glennrpbb4f99d2011-05-22 11:13:17 +00007721
glennrp8e58efd2011-05-20 12:16:29 +00007722 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7723 break;
7724 }
7725
7726 if (image->storage_class == PseudoClass && image->colormap != NULL)
7727 {
cristy3e08f112011-05-24 13:19:30 +00007728 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007729 {
7730 LBR04RGBOPixelPacketComponent(image->colormap[i]);
7731 }
7732 }
7733 }
7734
7735 else if (image->depth > 1)
7736 {
7737 /* Scale to 2-bit */
7738 LBR02RGBOPixelPacketComponent(image->background_color);
7739
7740 for (y=0; y < (ssize_t) image->rows; y++)
7741 {
7742 r=GetAuthenticPixels(image,0,y,image->columns,1,
7743 exception);
7744
7745 if (r == (PixelPacket *) NULL)
7746 break;
7747
7748 for (x=0; x < (ssize_t) image->columns; x++)
7749 {
7750 LBR02RGBOPixelComponent(r);
7751 r++;
7752 }
glennrpbb4f99d2011-05-22 11:13:17 +00007753
glennrp8e58efd2011-05-20 12:16:29 +00007754 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7755 break;
7756 }
7757
7758 if (image->storage_class == PseudoClass && image->colormap != NULL)
7759 {
cristy3e08f112011-05-24 13:19:30 +00007760 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007761 {
7762 LBR02RGBOPixelPacketComponent(image->colormap[i]);
7763 }
7764 }
7765 }
7766 else
7767 {
7768 /* Scale to 1-bit */
7769 LBR01RGBOPixelPacketComponent(image->background_color);
7770
7771 for (y=0; y < (ssize_t) image->rows; y++)
7772 {
7773 r=GetAuthenticPixels(image,0,y,image->columns,1,
7774 exception);
7775
7776 if (r == (PixelPacket *) NULL)
7777 break;
7778
7779 for (x=0; x < (ssize_t) image->columns; x++)
7780 {
7781 LBR01RGBOPixelComponent(r);
7782 r++;
7783 }
glennrpbb4f99d2011-05-22 11:13:17 +00007784
glennrp8e58efd2011-05-20 12:16:29 +00007785 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7786 break;
7787 }
7788
7789 if (image->storage_class == PseudoClass && image->colormap != NULL)
7790 {
cristy3e08f112011-05-24 13:19:30 +00007791 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007792 {
7793 LBR01RGBOPixelPacketComponent(image->colormap[i]);
7794 }
7795 }
7796 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007797 }
7798
glennrp67b9c1a2011-04-22 18:47:36 +00007799 /* To do: set to next higher multiple of 8 */
7800 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007801 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007802
glennrp2b013e42010-11-24 16:55:50 +00007803#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7804 /* PNG does not handle depths greater than 16 so reduce it even
7805 * if lossy
7806 */
glennrp8e58efd2011-05-20 12:16:29 +00007807 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007808 image->depth=16;
7809#endif
7810
glennrp3faa9a32011-04-23 14:00:25 +00007811#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007812 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007813 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007814 image->depth = 8;
7815#endif
7816
glennrpc8c2f062011-02-25 19:00:33 +00007817 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007818 * we reduce the transparency to binary and run again, then if there
7819 * 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 +00007820 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7821 * palette. Then (To do) we take care of a final reduction that is only
7822 * needed if there are still 256 colors present and one of them has both
7823 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007824 */
glennrp82b3c532011-03-22 19:20:54 +00007825
glennrp8ca51ad2011-05-12 21:22:32 +00007826 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007827 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007828 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007829
glennrp8ca51ad2011-05-12 21:22:32 +00007830 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007831 {
7832 /* BUILD_PALETTE
7833 *
7834 * Sometimes we get DirectClass images that have 256 colors or fewer.
7835 * This code will build a colormap.
7836 *
7837 * Also, sometimes we get PseudoClass images with an out-of-date
7838 * colormap. This code will replace the colormap with a new one.
7839 * Sometimes we get PseudoClass images that have more than 256 colors.
7840 * This code will delete the colormap and change the image to
7841 * DirectClass.
7842 *
7843 * If image->matte is MagickFalse, we ignore the opacity channel
7844 * even though it sometimes contains left-over non-opaque values.
7845 *
7846 * Also we gather some information (number of opaque, transparent,
7847 * and semitransparent pixels, and whether the image has any non-gray
7848 * pixels or only black-and-white pixels) that we might need later.
7849 *
7850 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7851 * we need to check for bogus non-opaque values, at least.
7852 */
glennrp3c218112010-11-27 15:31:26 +00007853
glennrpd71e86a2011-02-24 01:28:37 +00007854 ExceptionInfo
7855 *exception;
glennrp3c218112010-11-27 15:31:26 +00007856
glennrpd71e86a2011-02-24 01:28:37 +00007857 int
7858 n;
glennrp3c218112010-11-27 15:31:26 +00007859
glennrpd71e86a2011-02-24 01:28:37 +00007860 PixelPacket
7861 opaque[260],
7862 semitransparent[260],
7863 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007864
glennrpd71e86a2011-02-24 01:28:37 +00007865 register IndexPacket
7866 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007867
glennrpd71e86a2011-02-24 01:28:37 +00007868 register const PixelPacket
7869 *s,
7870 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007871
glennrpfd05d622011-02-25 04:10:33 +00007872 register PixelPacket
7873 *r;
7874
glennrpd71e86a2011-02-24 01:28:37 +00007875 if (logging != MagickFalse)
7876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7877 " Enter BUILD_PALETTE:");
7878
7879 if (logging != MagickFalse)
7880 {
glennrp03812ae2010-12-24 01:31:34 +00007881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007882 " image->columns=%.20g",(double) image->columns);
7883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7884 " image->rows=%.20g",(double) image->rows);
7885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7886 " image->matte=%.20g",(double) image->matte);
7887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7888 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007889
glennrpfd05d622011-02-25 04:10:33 +00007890 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007891 {
7892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007893 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007895 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007896
glennrpd71e86a2011-02-24 01:28:37 +00007897 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007898 {
glennrpd71e86a2011-02-24 01:28:37 +00007899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7900 " %d (%d,%d,%d,%d)",
7901 (int) i,
7902 (int) image->colormap[i].red,
7903 (int) image->colormap[i].green,
7904 (int) image->colormap[i].blue,
7905 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007906 }
glennrp2cc891a2010-12-24 13:44:32 +00007907
glennrpd71e86a2011-02-24 01:28:37 +00007908 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7909 {
7910 if (i > 255)
7911 {
7912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7913 " %d (%d,%d,%d,%d)",
7914 (int) i,
7915 (int) image->colormap[i].red,
7916 (int) image->colormap[i].green,
7917 (int) image->colormap[i].blue,
7918 (int) image->colormap[i].opacity);
7919 }
7920 }
glennrp03812ae2010-12-24 01:31:34 +00007921 }
glennrp7ddcc222010-12-11 05:01:05 +00007922
glennrpd71e86a2011-02-24 01:28:37 +00007923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7924 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007925
glennrpd71e86a2011-02-24 01:28:37 +00007926 if (image->colors == 0)
7927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7928 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007929
glennrp8d3d6e52011-04-19 04:39:51 +00007930 if (ping_preserve_colormap == MagickFalse)
7931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7932 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007933 }
7934
7935 exception=(&image->exception);
7936
7937 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007938 number_opaque = 0;
7939 number_semitransparent = 0;
7940 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007941
7942 for (y=0; y < (ssize_t) image->rows; y++)
7943 {
7944 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7945
7946 if (q == (PixelPacket *) NULL)
7947 break;
7948
7949 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007950 {
glennrp4737d522011-04-29 03:33:42 +00007951 if (image->matte == MagickFalse ||
7952 GetOpacityPixelComponent(q) == OpaqueOpacity)
glennrpd71e86a2011-02-24 01:28:37 +00007953 {
7954 if (number_opaque < 259)
7955 {
7956 if (number_opaque == 0)
7957 {
cristy8d74fc12011-05-25 23:02:47 +00007958 GetRGBPixelComponents(q, opaque);
glennrpd71e86a2011-02-24 01:28:37 +00007959 opaque[0].opacity=OpaqueOpacity;
7960 number_opaque=1;
7961 }
glennrp2cc891a2010-12-24 13:44:32 +00007962
glennrpd71e86a2011-02-24 01:28:37 +00007963 for (i=0; i< (ssize_t) number_opaque; i++)
7964 {
glennrp0e68fac2011-04-26 04:51:47 +00007965 if (IsColorEqual(q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00007966 break;
7967 }
glennrp7ddcc222010-12-11 05:01:05 +00007968
glennrpd71e86a2011-02-24 01:28:37 +00007969 if (i == (ssize_t) number_opaque &&
7970 number_opaque < 259)
7971 {
7972 number_opaque++;
cristy8d74fc12011-05-25 23:02:47 +00007973 GetRGBPixelComponents(q, opaque+i);
glennrpca7ad3a2011-04-26 04:44:54 +00007974 opaque[i].opacity=OpaqueOpacity;
glennrpd71e86a2011-02-24 01:28:37 +00007975 }
7976 }
7977 }
7978 else if (q->opacity == TransparentOpacity)
7979 {
7980 if (number_transparent < 259)
7981 {
7982 if (number_transparent == 0)
7983 {
cristy8d74fc12011-05-25 23:02:47 +00007984 GetRGBOPixelComponents(q, transparent);
glennrpa18d5bc2011-04-23 14:51:34 +00007985 ping_trans_color.red=
7986 (unsigned short) GetRedPixelComponent(q);
7987 ping_trans_color.green=
7988 (unsigned short) GetGreenPixelComponent(q);
7989 ping_trans_color.blue=
7990 (unsigned short) GetBluePixelComponent(q);
7991 ping_trans_color.gray=
7992 (unsigned short) GetRedPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007993 number_transparent = 1;
7994 }
7995
7996 for (i=0; i< (ssize_t) number_transparent; i++)
7997 {
glennrp0e68fac2011-04-26 04:51:47 +00007998 if (IsColorEqual(q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00007999 break;
8000 }
8001
8002 if (i == (ssize_t) number_transparent &&
8003 number_transparent < 259)
8004 {
8005 number_transparent++;
cristy8d74fc12011-05-25 23:02:47 +00008006 GetRGBOPixelComponents(q, transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008007 }
8008 }
8009 }
8010 else
8011 {
8012 if (number_semitransparent < 259)
8013 {
8014 if (number_semitransparent == 0)
8015 {
cristy8d74fc12011-05-25 23:02:47 +00008016 GetRGBOPixelComponents(q, semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008017 number_semitransparent = 1;
8018 }
8019
8020 for (i=0; i< (ssize_t) number_semitransparent; i++)
8021 {
glennrp0e68fac2011-04-26 04:51:47 +00008022 if (IsColorEqual(q, semitransparent+i)
glennrpca7ad3a2011-04-26 04:44:54 +00008023 && GetOpacityPixelComponent(q) ==
8024 semitransparent[i].opacity)
glennrpd71e86a2011-02-24 01:28:37 +00008025 break;
8026 }
8027
8028 if (i == (ssize_t) number_semitransparent &&
8029 number_semitransparent < 259)
8030 {
8031 number_semitransparent++;
cristy8d74fc12011-05-25 23:02:47 +00008032 GetRGBOPixelComponents(q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008033 }
8034 }
8035 }
8036 q++;
8037 }
8038 }
8039
8040 if (ping_exclude_bKGD == MagickFalse)
8041 {
8042 /* Add the background color to the palette, if it
8043 * isn't already there.
8044 */
glennrpc6c391a2011-04-27 02:23:56 +00008045 if (logging != MagickFalse)
8046 {
8047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8048 " Check colormap for background (%d,%d,%d)",
8049 (int) image->background_color.red,
8050 (int) image->background_color.green,
8051 (int) image->background_color.blue);
8052 }
glennrpd71e86a2011-02-24 01:28:37 +00008053 for (i=0; i<number_opaque; i++)
8054 {
glennrpca7ad3a2011-04-26 04:44:54 +00008055 if (opaque[i].red == image->background_color.red &&
8056 opaque[i].green == image->background_color.green &&
8057 opaque[i].blue == image->background_color.blue)
8058 break;
glennrpd71e86a2011-02-24 01:28:37 +00008059 }
glennrpd71e86a2011-02-24 01:28:37 +00008060 if (number_opaque < 259 && i == number_opaque)
8061 {
glennrp8e045c82011-04-27 16:40:27 +00008062 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008063 ping_background.index = i;
8064 if (logging != MagickFalse)
8065 {
8066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8067 " background_color index is %d",(int) i);
8068 }
8069
glennrpd71e86a2011-02-24 01:28:37 +00008070 }
glennrpa080bc32011-03-11 18:03:44 +00008071 else if (logging != MagickFalse)
8072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8073 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008074 }
8075
8076 image_colors=number_opaque+number_transparent+number_semitransparent;
8077
glennrpa080bc32011-03-11 18:03:44 +00008078 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8079 {
8080 /* No room for the background color; remove it. */
8081 number_opaque--;
8082 image_colors--;
8083 }
8084
glennrpd71e86a2011-02-24 01:28:37 +00008085 if (logging != MagickFalse)
8086 {
8087 if (image_colors > 256)
8088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8089 " image has more than 256 colors");
8090
8091 else
8092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8093 " image has %d colors",image_colors);
8094 }
8095
glennrp8d3d6e52011-04-19 04:39:51 +00008096 if (ping_preserve_colormap != MagickFalse)
8097 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008098
glennrpfd05d622011-02-25 04:10:33 +00008099 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008100 {
8101 ping_have_color=MagickFalse;
8102 ping_have_non_bw=MagickFalse;
8103
8104 if(image_colors > 256)
8105 {
8106 for (y=0; y < (ssize_t) image->rows; y++)
8107 {
8108 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8109
8110 if (q == (PixelPacket *) NULL)
8111 break;
8112
8113 /* Worst case is black-and-white; we are looking at every
8114 * pixel twice.
8115 */
8116
8117 if (ping_have_color == MagickFalse)
8118 {
8119 s=q;
8120 for (x=0; x < (ssize_t) image->columns; x++)
8121 {
glennrpa18d5bc2011-04-23 14:51:34 +00008122 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
8123 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
glennrpd71e86a2011-02-24 01:28:37 +00008124 {
8125 ping_have_color=MagickTrue;
8126 ping_have_non_bw=MagickTrue;
8127 break;
8128 }
8129 s++;
8130 }
8131 }
8132
8133 if (ping_have_non_bw == MagickFalse)
8134 {
8135 s=q;
8136 for (x=0; x < (ssize_t) image->columns; x++)
8137 {
glennrpa18d5bc2011-04-23 14:51:34 +00008138 if (GetRedPixelComponent(s) != 0 &&
8139 GetRedPixelComponent(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008140 {
8141 ping_have_non_bw=MagickTrue;
8142 }
8143 s++;
8144 }
8145 }
8146 }
glennrpbb4f99d2011-05-22 11:13:17 +00008147 }
8148 }
glennrpd71e86a2011-02-24 01:28:37 +00008149
8150 if (image_colors < 257)
8151 {
8152 PixelPacket
8153 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008154
glennrpd71e86a2011-02-24 01:28:37 +00008155 /*
8156 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008157 */
8158
glennrpd71e86a2011-02-24 01:28:37 +00008159 if (logging != MagickFalse)
8160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8161 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008162
glennrpd71e86a2011-02-24 01:28:37 +00008163 /* Sort palette, transparent first */;
8164
8165 n = 0;
8166
8167 for (i=0; i<number_transparent; i++)
8168 colormap[n++] = transparent[i];
8169
8170 for (i=0; i<number_semitransparent; i++)
8171 colormap[n++] = semitransparent[i];
8172
8173 for (i=0; i<number_opaque; i++)
8174 colormap[n++] = opaque[i];
8175
glennrpc6c391a2011-04-27 02:23:56 +00008176 ping_background.index +=
8177 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008178
glennrpd71e86a2011-02-24 01:28:37 +00008179 /* image_colors < 257; search the colormap instead of the pixels
8180 * to get ping_have_color and ping_have_non_bw
8181 */
8182 for (i=0; i<n; i++)
8183 {
8184 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008185 {
glennrpd71e86a2011-02-24 01:28:37 +00008186 if (colormap[i].red != colormap[i].green ||
8187 colormap[i].red != colormap[i].blue)
8188 {
8189 ping_have_color=MagickTrue;
8190 ping_have_non_bw=MagickTrue;
8191 break;
8192 }
8193 }
8194
8195 if (ping_have_non_bw == MagickFalse)
8196 {
8197 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008198 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008199 }
glennrp8bb3a022010-12-13 20:40:04 +00008200 }
8201
glennrpd71e86a2011-02-24 01:28:37 +00008202 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8203 (number_transparent == 0 && number_semitransparent == 0)) &&
8204 (((mng_info->write_png_colortype-1) ==
8205 PNG_COLOR_TYPE_PALETTE) ||
8206 (mng_info->write_png_colortype == 0)))
8207 {
glennrp6185c532011-01-14 17:58:40 +00008208 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008209 {
glennrpd71e86a2011-02-24 01:28:37 +00008210 if (n != (ssize_t) image_colors)
8211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8212 " image_colors (%d) and n (%d) don't match",
8213 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008214
glennrpd71e86a2011-02-24 01:28:37 +00008215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8216 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008217 }
glennrp03812ae2010-12-24 01:31:34 +00008218
glennrpd71e86a2011-02-24 01:28:37 +00008219 image->colors = image_colors;
8220
8221 if (AcquireImageColormap(image,image_colors) ==
8222 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008223 ThrowWriterException(ResourceLimitError,
8224 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008225
8226 for (i=0; i< (ssize_t) image_colors; i++)
8227 image->colormap[i] = colormap[i];
8228
8229 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008230 {
glennrpd71e86a2011-02-24 01:28:37 +00008231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8232 " image->colors=%d (%d)",
8233 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008234
glennrpd71e86a2011-02-24 01:28:37 +00008235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8236 " Update the pixel indexes");
8237 }
glennrp6185c532011-01-14 17:58:40 +00008238
glennrpfd05d622011-02-25 04:10:33 +00008239 /* Sync the pixel indices with the new colormap */
8240
glennrpd71e86a2011-02-24 01:28:37 +00008241 for (y=0; y < (ssize_t) image->rows; y++)
8242 {
8243 q=GetAuthenticPixels(image,0,y,image->columns,1,
8244 exception);
glennrp6185c532011-01-14 17:58:40 +00008245
glennrpd71e86a2011-02-24 01:28:37 +00008246 if (q == (PixelPacket *) NULL)
8247 break;
glennrp6185c532011-01-14 17:58:40 +00008248
glennrpd71e86a2011-02-24 01:28:37 +00008249 indexes=GetAuthenticIndexQueue(image);
glennrpbb4f99d2011-05-22 11:13:17 +00008250
glennrpd71e86a2011-02-24 01:28:37 +00008251 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008252 {
glennrpd71e86a2011-02-24 01:28:37 +00008253 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008254 {
glennrpd71e86a2011-02-24 01:28:37 +00008255 if ((image->matte == MagickFalse ||
glennrpbb4f99d2011-05-22 11:13:17 +00008256 image->colormap[i].opacity ==
glennrpca7ad3a2011-04-26 04:44:54 +00008257 GetOpacityPixelComponent(q)) &&
glennrpbb4f99d2011-05-22 11:13:17 +00008258 image->colormap[i].red ==
glennrpca7ad3a2011-04-26 04:44:54 +00008259 GetRedPixelComponent(q) &&
glennrpbb4f99d2011-05-22 11:13:17 +00008260 image->colormap[i].green ==
glennrpca7ad3a2011-04-26 04:44:54 +00008261 GetGreenPixelComponent(q) &&
glennrpbb4f99d2011-05-22 11:13:17 +00008262 image->colormap[i].blue ==
glennrpca7ad3a2011-04-26 04:44:54 +00008263 GetBluePixelComponent(q))
glennrp6185c532011-01-14 17:58:40 +00008264 {
cristy9fff7b42011-04-29 01:09:31 +00008265 SetIndexPixelComponent(indexes+x,i);
glennrpd71e86a2011-02-24 01:28:37 +00008266 break;
glennrp6185c532011-01-14 17:58:40 +00008267 }
glennrp6185c532011-01-14 17:58:40 +00008268 }
glennrpd71e86a2011-02-24 01:28:37 +00008269 q++;
8270 }
glennrp6185c532011-01-14 17:58:40 +00008271
glennrpd71e86a2011-02-24 01:28:37 +00008272 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8273 break;
8274 }
8275 }
8276 }
8277
8278 if (logging != MagickFalse)
8279 {
8280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8281 " image->colors=%d", (int) image->colors);
8282
8283 if (image->colormap != NULL)
8284 {
8285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8286 " i (red,green,blue,opacity)");
8287
8288 for (i=0; i < (ssize_t) image->colors; i++)
8289 {
cristy72988482011-03-29 16:34:38 +00008290 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008291 {
8292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8293 " %d (%d,%d,%d,%d)",
8294 (int) i,
8295 (int) image->colormap[i].red,
8296 (int) image->colormap[i].green,
8297 (int) image->colormap[i].blue,
8298 (int) image->colormap[i].opacity);
8299 }
glennrp6185c532011-01-14 17:58:40 +00008300 }
8301 }
glennrp03812ae2010-12-24 01:31:34 +00008302
glennrpd71e86a2011-02-24 01:28:37 +00008303 if (number_transparent < 257)
8304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8305 " number_transparent = %d",
8306 number_transparent);
8307 else
glennrp03812ae2010-12-24 01:31:34 +00008308
glennrpd71e86a2011-02-24 01:28:37 +00008309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8310 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008311
glennrpd71e86a2011-02-24 01:28:37 +00008312 if (number_opaque < 257)
8313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8314 " number_opaque = %d",
8315 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008316
glennrpd71e86a2011-02-24 01:28:37 +00008317 else
8318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8319 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008320
glennrpd71e86a2011-02-24 01:28:37 +00008321 if (number_semitransparent < 257)
8322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8323 " number_semitransparent = %d",
8324 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008325
glennrpd71e86a2011-02-24 01:28:37 +00008326 else
8327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8328 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008329
glennrpd71e86a2011-02-24 01:28:37 +00008330 if (ping_have_non_bw == MagickFalse)
8331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8332 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008333
glennrpd71e86a2011-02-24 01:28:37 +00008334 else if (ping_have_color == MagickFalse)
8335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8336 " All pixels and the background are gray");
8337
8338 else
8339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8340 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008341
glennrp03812ae2010-12-24 01:31:34 +00008342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8343 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008344 }
glennrpfd05d622011-02-25 04:10:33 +00008345
glennrpc8c2f062011-02-25 19:00:33 +00008346 if (mng_info->write_png8 == MagickFalse)
8347 break;
glennrpfd05d622011-02-25 04:10:33 +00008348
glennrpc8c2f062011-02-25 19:00:33 +00008349 /* Make any reductions necessary for the PNG8 format */
8350 if (image_colors <= 256 &&
8351 image_colors != 0 && image->colormap != NULL &&
8352 number_semitransparent == 0 &&
8353 number_transparent <= 1)
8354 break;
8355
8356 /* PNG8 can't have semitransparent colors so we threshold the
8357 * opacity to 0 or OpaqueOpacity
8358 */
8359 if (number_semitransparent != 0)
8360 {
8361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8362 " Thresholding the alpha channel to binary");
8363
8364 for (y=0; y < (ssize_t) image->rows; y++)
8365 {
8366 r=GetAuthenticPixels(image,0,y,image->columns,1,
8367 exception);
8368
8369 if (r == (PixelPacket *) NULL)
8370 break;
8371
8372 for (x=0; x < (ssize_t) image->columns; x++)
8373 {
glennrp8ca51ad2011-05-12 21:22:32 +00008374 if (GetOpacityPixelComponent(r) > TransparentOpacity/2)
8375 {
8376 SetOpacityPixelComponent(r,TransparentOpacity);
cristy8d74fc12011-05-25 23:02:47 +00008377 SetRGBPixelComponents(r,&image->background_color);
glennrp8ca51ad2011-05-12 21:22:32 +00008378 }
8379 else
8380 SetOpacityPixelComponent(r,OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00008381 r++;
8382 }
glennrpbb4f99d2011-05-22 11:13:17 +00008383
glennrpc8c2f062011-02-25 19:00:33 +00008384 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8385 break;
8386
8387 if (image_colors != 0 && image_colors <= 256 &&
8388 image->colormap != NULL)
8389 for (i=0; i<image_colors; i++)
8390 image->colormap[i].opacity =
glennrp77110c32011-05-03 05:25:16 +00008391 (image->colormap[i].opacity > TransparentOpacity/2 ?
8392 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00008393 }
8394 continue;
8395 }
8396
8397 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008398 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8399 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8400 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008401 */
glennrpd3371642011-03-22 19:42:23 +00008402 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8403 {
8404 if (logging != MagickFalse)
8405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8406 " Quantizing the background color to 4-4-4");
8407
8408 tried_444 = MagickTrue;
8409
glennrp8e58efd2011-05-20 12:16:29 +00008410 LBR04RGBPixelPacketComponent(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008411
8412 if (logging != MagickFalse)
8413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8414 " Quantizing the pixel colors to 4-4-4");
8415
8416 if (image->colormap == NULL)
8417 {
8418 for (y=0; y < (ssize_t) image->rows; y++)
8419 {
8420 r=GetAuthenticPixels(image,0,y,image->columns,1,
8421 exception);
8422
8423 if (r == (PixelPacket *) NULL)
8424 break;
8425
8426 for (x=0; x < (ssize_t) image->columns; x++)
8427 {
glennrp8ca51ad2011-05-12 21:22:32 +00008428 if (GetOpacityPixelComponent(r) == OpaqueOpacity)
glennrp8e58efd2011-05-20 12:16:29 +00008429 LBR04RGBPixelComponent(r);
glennrpd3371642011-03-22 19:42:23 +00008430 r++;
8431 }
glennrpbb4f99d2011-05-22 11:13:17 +00008432
glennrpd3371642011-03-22 19:42:23 +00008433 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8434 break;
8435 }
8436 }
8437
8438 else /* Should not reach this; colormap already exists and
8439 must be <= 256 */
8440 {
8441 if (logging != MagickFalse)
8442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8443 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008444
glennrpd3371642011-03-22 19:42:23 +00008445 for (i=0; i<image_colors; i++)
8446 {
glennrp8e58efd2011-05-20 12:16:29 +00008447 LBR04RGBPixelPacketComponent(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008448 }
8449 }
8450 continue;
8451 }
8452
glennrp82b3c532011-03-22 19:20:54 +00008453 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8454 {
8455 if (logging != MagickFalse)
8456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8457 " Quantizing the background color to 3-3-3");
8458
8459 tried_333 = MagickTrue;
8460
glennrp8e58efd2011-05-20 12:16:29 +00008461 LBR03RGBPixelPacketComponent(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008462
8463 if (logging != MagickFalse)
8464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008465 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008466
8467 if (image->colormap == NULL)
8468 {
8469 for (y=0; y < (ssize_t) image->rows; y++)
8470 {
8471 r=GetAuthenticPixels(image,0,y,image->columns,1,
8472 exception);
8473
8474 if (r == (PixelPacket *) NULL)
8475 break;
8476
8477 for (x=0; x < (ssize_t) image->columns; x++)
8478 {
glennrp8ca51ad2011-05-12 21:22:32 +00008479 if (GetOpacityPixelComponent(r) == OpaqueOpacity)
glennrp8e58efd2011-05-20 12:16:29 +00008480 LBR03RGBPixelComponent(r);
glennrp82b3c532011-03-22 19:20:54 +00008481 r++;
8482 }
glennrpbb4f99d2011-05-22 11:13:17 +00008483
glennrp82b3c532011-03-22 19:20:54 +00008484 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8485 break;
8486 }
8487 }
8488
8489 else /* Should not reach this; colormap already exists and
8490 must be <= 256 */
8491 {
8492 if (logging != MagickFalse)
8493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008494 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008495 for (i=0; i<image_colors; i++)
8496 {
glennrp8e58efd2011-05-20 12:16:29 +00008497 LBR03RGBPixelPacketComponent(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008498 }
glennrpd3371642011-03-22 19:42:23 +00008499 }
8500 continue;
glennrp82b3c532011-03-22 19:20:54 +00008501 }
glennrpc8c2f062011-02-25 19:00:33 +00008502
glennrp8ca51ad2011-05-12 21:22:32 +00008503 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008504 {
8505 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008507 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008508
glennrp8ca51ad2011-05-12 21:22:32 +00008509 tried_332 = MagickTrue;
8510
glennrp3faa9a32011-04-23 14:00:25 +00008511 /* Red and green were already done so we only quantize the blue
8512 * channel
8513 */
8514
glennrp8e58efd2011-05-20 12:16:29 +00008515 LBR02BluePixelPacketComponent(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008516
glennrpc8c2f062011-02-25 19:00:33 +00008517 if (logging != MagickFalse)
8518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008519 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008520
glennrpc8c2f062011-02-25 19:00:33 +00008521 if (image->colormap == NULL)
8522 {
8523 for (y=0; y < (ssize_t) image->rows; y++)
8524 {
8525 r=GetAuthenticPixels(image,0,y,image->columns,1,
8526 exception);
8527
8528 if (r == (PixelPacket *) NULL)
8529 break;
8530
8531 for (x=0; x < (ssize_t) image->columns; x++)
8532 {
glennrp8ca51ad2011-05-12 21:22:32 +00008533 if (GetOpacityPixelComponent(r) == OpaqueOpacity)
glennrp8e58efd2011-05-20 12:16:29 +00008534 LBR02BluePixelComponent(r);
glennrp52a479c2011-02-26 21:14:38 +00008535 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008536 }
glennrpbb4f99d2011-05-22 11:13:17 +00008537
glennrpc8c2f062011-02-25 19:00:33 +00008538 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8539 break;
8540 }
8541 }
glennrpfd05d622011-02-25 04:10:33 +00008542
glennrpc8c2f062011-02-25 19:00:33 +00008543 else /* Should not reach this; colormap already exists and
8544 must be <= 256 */
8545 {
8546 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008548 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008549 for (i=0; i<image_colors; i++)
8550 {
glennrp8e58efd2011-05-20 12:16:29 +00008551 LBR02BluePixelPacketComponent(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008552 }
8553 }
8554 continue;
8555 }
8556 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008557
8558 if (image_colors == 0 || image_colors > 256)
8559 {
8560 /* Take care of special case with 256 colors + 1 transparent
8561 * color. We don't need to quantize to 2-3-2-1; we only need to
8562 * eliminate one color, so we'll merge the two darkest red
8563 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8564 */
8565 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8566 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8567 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8568 {
8569 image->background_color.red=ScaleCharToQuantum(0x24);
8570 }
glennrpbb4f99d2011-05-22 11:13:17 +00008571
glennrp8ca51ad2011-05-12 21:22:32 +00008572 if (image->colormap == NULL)
8573 {
8574 for (y=0; y < (ssize_t) image->rows; y++)
8575 {
8576 r=GetAuthenticPixels(image,0,y,image->columns,1,
8577 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008578
glennrp8ca51ad2011-05-12 21:22:32 +00008579 if (r == (PixelPacket *) NULL)
8580 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008581
glennrp8ca51ad2011-05-12 21:22:32 +00008582 for (x=0; x < (ssize_t) image->columns; x++)
8583 {
8584 if (ScaleQuantumToChar(GetRedPixelComponent(r)) == 0x49 &&
8585 ScaleQuantumToChar(GetGreenPixelComponent(r)) == 0x00 &&
8586 ScaleQuantumToChar(GetBluePixelComponent(r)) == 0x00 &&
8587 GetOpacityPixelComponent(r) == OpaqueOpacity)
8588 {
8589 SetRedPixelComponent(r,ScaleCharToQuantum(0x24));
8590 }
8591 r++;
8592 }
glennrpbb4f99d2011-05-22 11:13:17 +00008593
glennrp8ca51ad2011-05-12 21:22:32 +00008594 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8595 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008596
glennrp8ca51ad2011-05-12 21:22:32 +00008597 }
8598 }
8599
8600 else
8601 {
8602 for (i=0; i<image_colors; i++)
8603 {
8604 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8605 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8606 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8607 {
8608 image->colormap[i].red=ScaleCharToQuantum(0x24);
8609 }
8610 }
8611 }
8612 }
glennrpd71e86a2011-02-24 01:28:37 +00008613 }
glennrpfd05d622011-02-25 04:10:33 +00008614 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008615
glennrpfd05d622011-02-25 04:10:33 +00008616 /* If we are excluding the tRNS chunk and there is transparency,
8617 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8618 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008619 */
glennrp0e8ea192010-12-24 18:00:33 +00008620 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8621 (number_transparent != 0 || number_semitransparent != 0))
8622 {
glennrpd17915c2011-04-29 14:24:22 +00008623 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008624
8625 if (ping_have_color == MagickFalse)
8626 mng_info->write_png_colortype = 5;
8627
8628 else
8629 mng_info->write_png_colortype = 7;
8630
glennrp8d579662011-02-23 02:05:02 +00008631 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008632 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008633 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008634
glennrp0e8ea192010-12-24 18:00:33 +00008635 }
8636
glennrpfd05d622011-02-25 04:10:33 +00008637 /* See if cheap transparency is possible. It is only possible
8638 * when there is a single transparent color, no semitransparent
8639 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008640 * as the transparent color. We only need this information if
8641 * we are writing a PNG with colortype 0 or 2, and we have not
8642 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008643 */
glennrp5a39f372011-02-25 04:52:16 +00008644 if (number_transparent == 1 &&
8645 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008646 {
8647 ping_have_cheap_transparency = MagickTrue;
8648
8649 if (number_semitransparent != 0)
8650 ping_have_cheap_transparency = MagickFalse;
8651
8652 else if (image_colors == 0 || image_colors > 256 ||
8653 image->colormap == NULL)
8654 {
8655 ExceptionInfo
8656 *exception;
8657
8658 register const PixelPacket
8659 *q;
8660
8661 exception=(&image->exception);
8662
8663 for (y=0; y < (ssize_t) image->rows; y++)
8664 {
8665 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8666
8667 if (q == (PixelPacket *) NULL)
8668 break;
8669
8670 for (x=0; x < (ssize_t) image->columns; x++)
8671 {
8672 if (q->opacity != TransparentOpacity &&
glennrp7c7b3152011-04-26 04:01:27 +00008673 (unsigned short) GetRedPixelComponent(q) ==
8674 ping_trans_color.red &&
8675 (unsigned short) GetGreenPixelComponent(q) ==
8676 ping_trans_color.green &&
8677 (unsigned short) GetBluePixelComponent(q) ==
8678 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008679 {
8680 ping_have_cheap_transparency = MagickFalse;
8681 break;
8682 }
8683
8684 q++;
8685 }
glennrpbb4f99d2011-05-22 11:13:17 +00008686
glennrpfd05d622011-02-25 04:10:33 +00008687 if (ping_have_cheap_transparency == MagickFalse)
8688 break;
8689 }
8690 }
8691 else
8692 {
glennrp67b9c1a2011-04-22 18:47:36 +00008693 /* Assuming that image->colormap[0] is the one transparent color
8694 * and that all others are opaque.
8695 */
glennrpfd05d622011-02-25 04:10:33 +00008696 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008697 for (i=1; i<image_colors; i++)
8698 if (image->colormap[i].red == image->colormap[0].red &&
8699 image->colormap[i].green == image->colormap[0].green &&
8700 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008701 {
glennrp67b9c1a2011-04-22 18:47:36 +00008702 ping_have_cheap_transparency = MagickFalse;
8703 break;
glennrpfd05d622011-02-25 04:10:33 +00008704 }
8705 }
glennrpbb4f99d2011-05-22 11:13:17 +00008706
glennrpfd05d622011-02-25 04:10:33 +00008707 if (logging != MagickFalse)
8708 {
8709 if (ping_have_cheap_transparency == MagickFalse)
8710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8711 " Cheap transparency is not possible.");
8712
8713 else
8714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8715 " Cheap transparency is possible.");
8716 }
8717 }
8718 else
8719 ping_have_cheap_transparency = MagickFalse;
8720
glennrp8640fb52010-11-23 15:48:26 +00008721 image_depth=image->depth;
8722
glennrp26c990a2010-11-23 02:23:20 +00008723 quantum_info = (QuantumInfo *) NULL;
8724 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008725 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008726 image_matte=image->matte;
8727
glennrp0fe50b42010-11-16 03:52:51 +00008728 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008729 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008730
glennrp52a479c2011-02-26 21:14:38 +00008731 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8732 (image->colors == 0 || image->colormap == NULL))
8733 {
glennrp52a479c2011-02-26 21:14:38 +00008734 image_info=DestroyImageInfo(image_info);
8735 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008736 (void) ThrowMagickException(&IMimage->exception,
8737 GetMagickModule(),CoderError,
8738 "Cannot write PNG8 or color-type 3; colormap is NULL",
8739 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008740#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8741 UnlockSemaphoreInfo(ping_semaphore);
8742#endif
8743 return(MagickFalse);
8744 }
8745
cristy3ed852e2009-09-05 21:47:34 +00008746 /*
8747 Allocate the PNG structures
8748 */
8749#ifdef PNG_USER_MEM_SUPPORTED
8750 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008751 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8752 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008753
cristy3ed852e2009-09-05 21:47:34 +00008754#else
8755 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008756 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008757
cristy3ed852e2009-09-05 21:47:34 +00008758#endif
8759 if (ping == (png_struct *) NULL)
8760 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008761
cristy3ed852e2009-09-05 21:47:34 +00008762 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008763
cristy3ed852e2009-09-05 21:47:34 +00008764 if (ping_info == (png_info *) NULL)
8765 {
8766 png_destroy_write_struct(&ping,(png_info **) NULL);
8767 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8768 }
glennrp0fe50b42010-11-16 03:52:51 +00008769
cristy3ed852e2009-09-05 21:47:34 +00008770 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008771 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008772
glennrp5af765f2010-03-30 11:12:18 +00008773 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008774 {
8775 /*
8776 PNG write failed.
8777 */
8778#ifdef PNG_DEBUG
8779 if (image_info->verbose)
8780 (void) printf("PNG write has failed.\n");
8781#endif
8782 png_destroy_write_struct(&ping,&ping_info);
8783#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008784 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008785#endif
glennrpda8f3a72011-02-27 23:54:12 +00008786 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008787 (void) CloseBlob(image);
8788 image_info=DestroyImageInfo(image_info);
8789 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008790 return(MagickFalse);
8791 }
8792 /*
8793 Prepare PNG for writing.
8794 */
8795#if defined(PNG_MNG_FEATURES_SUPPORTED)
8796 if (mng_info->write_mng)
8797 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008798
cristy3ed852e2009-09-05 21:47:34 +00008799#else
8800# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8801 if (mng_info->write_mng)
8802 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008803
cristy3ed852e2009-09-05 21:47:34 +00008804# endif
8805#endif
glennrp2b013e42010-11-24 16:55:50 +00008806
cristy3ed852e2009-09-05 21:47:34 +00008807 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008808
cristy4e5bc842010-06-09 13:56:01 +00008809 ping_width=(png_uint_32) image->columns;
8810 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008811
cristy3ed852e2009-09-05 21:47:34 +00008812 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8813 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008814
cristy3ed852e2009-09-05 21:47:34 +00008815 if (mng_info->write_png_depth != 0)
8816 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008817
cristy3ed852e2009-09-05 21:47:34 +00008818 /* Adjust requested depth to next higher valid depth if necessary */
8819 if (image_depth > 8)
8820 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008821
cristy3ed852e2009-09-05 21:47:34 +00008822 if ((image_depth > 4) && (image_depth < 8))
8823 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008824
cristy3ed852e2009-09-05 21:47:34 +00008825 if (image_depth == 3)
8826 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008827
cristy3ed852e2009-09-05 21:47:34 +00008828 if (logging != MagickFalse)
8829 {
8830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008831 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008833 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008835 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008837 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008839 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008840 }
glennrp8640fb52010-11-23 15:48:26 +00008841
cristy3ed852e2009-09-05 21:47:34 +00008842 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008843 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008844
glennrp26f37912010-12-23 16:22:42 +00008845
cristy3ed852e2009-09-05 21:47:34 +00008846#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008847 if (ping_exclude_pHYs == MagickFalse)
8848 {
cristy3ed852e2009-09-05 21:47:34 +00008849 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8850 (!mng_info->write_mng || !mng_info->equal_physs))
8851 {
glennrp0fe50b42010-11-16 03:52:51 +00008852 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8854 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008855
8856 if (image->units == PixelsPerInchResolution)
8857 {
glennrpdfd70802010-11-14 01:23:35 +00008858 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008859 ping_pHYs_x_resolution=
8860 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8861 ping_pHYs_y_resolution=
8862 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008863 }
glennrpdfd70802010-11-14 01:23:35 +00008864
cristy3ed852e2009-09-05 21:47:34 +00008865 else if (image->units == PixelsPerCentimeterResolution)
8866 {
glennrpdfd70802010-11-14 01:23:35 +00008867 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008868 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8869 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008870 }
glennrp991d11d2010-11-12 21:55:28 +00008871
cristy3ed852e2009-09-05 21:47:34 +00008872 else
8873 {
glennrpdfd70802010-11-14 01:23:35 +00008874 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8875 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8876 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008877 }
glennrp991d11d2010-11-12 21:55:28 +00008878
glennrp823b55c2011-03-14 18:46:46 +00008879 if (logging != MagickFalse)
8880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8881 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8882 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8883 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008884 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008885 }
glennrp26f37912010-12-23 16:22:42 +00008886 }
cristy3ed852e2009-09-05 21:47:34 +00008887#endif
glennrpa521b2f2010-10-29 04:11:03 +00008888
glennrp26f37912010-12-23 16:22:42 +00008889 if (ping_exclude_bKGD == MagickFalse)
8890 {
glennrpa521b2f2010-10-29 04:11:03 +00008891 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008892 {
glennrpa521b2f2010-10-29 04:11:03 +00008893 unsigned int
8894 mask;
cristy3ed852e2009-09-05 21:47:34 +00008895
glennrpa521b2f2010-10-29 04:11:03 +00008896 mask=0xffff;
8897 if (ping_bit_depth == 8)
8898 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008899
glennrpa521b2f2010-10-29 04:11:03 +00008900 if (ping_bit_depth == 4)
8901 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008902
glennrpa521b2f2010-10-29 04:11:03 +00008903 if (ping_bit_depth == 2)
8904 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008905
glennrpa521b2f2010-10-29 04:11:03 +00008906 if (ping_bit_depth == 1)
8907 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008908
glennrpa521b2f2010-10-29 04:11:03 +00008909 ping_background.red=(png_uint_16)
8910 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008911
glennrpa521b2f2010-10-29 04:11:03 +00008912 ping_background.green=(png_uint_16)
8913 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008914
glennrpa521b2f2010-10-29 04:11:03 +00008915 ping_background.blue=(png_uint_16)
8916 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00008917
8918 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00008919 }
cristy3ed852e2009-09-05 21:47:34 +00008920
glennrp0fe50b42010-11-16 03:52:51 +00008921 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008922 {
8923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8924 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00008925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8926 " background_color index is %d",
8927 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008928
8929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8930 " ping_bit_depth=%d",ping_bit_depth);
8931 }
glennrp0fe50b42010-11-16 03:52:51 +00008932
8933 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008934 }
glennrp0fe50b42010-11-16 03:52:51 +00008935
cristy3ed852e2009-09-05 21:47:34 +00008936 /*
8937 Select the color type.
8938 */
8939 matte=image_matte;
8940 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008941
glennrp1273f7b2011-02-24 03:20:30 +00008942 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008943 {
glennrp0fe50b42010-11-16 03:52:51 +00008944
glennrpfd05d622011-02-25 04:10:33 +00008945 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008946 for reducing the sample depth from 8. */
8947
glennrp0fe50b42010-11-16 03:52:51 +00008948 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008949
glennrp8bb3a022010-12-13 20:40:04 +00008950 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008951
8952 /*
8953 Set image palette.
8954 */
8955 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8956
glennrp0fe50b42010-11-16 03:52:51 +00008957 if (logging != MagickFalse)
8958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8959 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008960 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008961
8962 for (i=0; i < (ssize_t) number_colors; i++)
8963 {
8964 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8965 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8966 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8967 if (logging != MagickFalse)
8968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008969#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008970 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008971#else
8972 " %5ld (%5d,%5d,%5d)",
8973#endif
glennrp0fe50b42010-11-16 03:52:51 +00008974 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8975
8976 }
glennrp2b013e42010-11-24 16:55:50 +00008977
glennrp8bb3a022010-12-13 20:40:04 +00008978 ping_have_PLTE=MagickTrue;
8979 image_depth=ping_bit_depth;
8980 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008981
glennrp58e01762011-01-07 15:28:54 +00008982 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008983 {
glennrp0fe50b42010-11-16 03:52:51 +00008984 /*
8985 Identify which colormap entry is transparent.
8986 */
8987 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008988 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008989
glennrp8bb3a022010-12-13 20:40:04 +00008990 for (i=0; i < (ssize_t) number_transparent; i++)
8991 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008992
glennrp0fe50b42010-11-16 03:52:51 +00008993
glennrp2cc891a2010-12-24 13:44:32 +00008994 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008995 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008996
8997 if (ping_num_trans == 0)
8998 ping_have_tRNS=MagickFalse;
8999
glennrp8bb3a022010-12-13 20:40:04 +00009000 else
9001 ping_have_tRNS=MagickTrue;
9002 }
glennrp0fe50b42010-11-16 03:52:51 +00009003
glennrp1273f7b2011-02-24 03:20:30 +00009004 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009005 {
glennrp1273f7b2011-02-24 03:20:30 +00009006 /*
9007 * Identify which colormap entry is the background color.
9008 */
9009
glennrp4f25bd02011-01-01 18:51:28 +00009010 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9011 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9012 break;
glennrp0fe50b42010-11-16 03:52:51 +00009013
glennrp4f25bd02011-01-01 18:51:28 +00009014 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009015
9016 if (logging != MagickFalse)
9017 {
9018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9019 " background_color index is %d",
9020 (int) ping_background.index);
9021 }
glennrp4f25bd02011-01-01 18:51:28 +00009022 }
cristy3ed852e2009-09-05 21:47:34 +00009023 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009024
cristy3ed852e2009-09-05 21:47:34 +00009025 else if (mng_info->write_png24)
9026 {
9027 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009028 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009029 }
glennrp0fe50b42010-11-16 03:52:51 +00009030
cristy3ed852e2009-09-05 21:47:34 +00009031 else if (mng_info->write_png32)
9032 {
9033 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009034 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009035 }
glennrp0fe50b42010-11-16 03:52:51 +00009036
glennrp8bb3a022010-12-13 20:40:04 +00009037 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009038 {
glennrp5af765f2010-03-30 11:12:18 +00009039 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009040
glennrp8bb3a022010-12-13 20:40:04 +00009041 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009042 {
glennrp5af765f2010-03-30 11:12:18 +00009043 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009044
glennrp5af765f2010-03-30 11:12:18 +00009045 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9046 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009047 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009048
glennrp8bb3a022010-12-13 20:40:04 +00009049 else
9050 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009051
9052 if (logging != MagickFalse)
9053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9054 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009055 }
glennrp0fe50b42010-11-16 03:52:51 +00009056
glennrp7c4c9e62011-03-21 20:23:32 +00009057 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009058 {
9059 if (logging != MagickFalse)
9060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009061 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009062
glennrpd6bf1612010-12-17 17:28:54 +00009063 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009064 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009065
glennrpd6bf1612010-12-17 17:28:54 +00009066 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009067 {
glennrp5af765f2010-03-30 11:12:18 +00009068 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009069 image_matte=MagickFalse;
9070 }
glennrp0fe50b42010-11-16 03:52:51 +00009071
glennrpd6bf1612010-12-17 17:28:54 +00009072 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009073 {
glennrp5af765f2010-03-30 11:12:18 +00009074 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009075 image_matte=MagickTrue;
9076 }
glennrp0fe50b42010-11-16 03:52:51 +00009077
glennrp5aa37f62011-01-02 03:07:57 +00009078 if (image_info->type == PaletteType ||
9079 image_info->type == PaletteMatteType)
9080 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9081
glennrp7c4c9e62011-03-21 20:23:32 +00009082 if (mng_info->write_png_colortype == 0 &&
9083 (image_info->type == UndefinedType ||
9084 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009085 {
glennrp5aa37f62011-01-02 03:07:57 +00009086 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009087 {
glennrp5aa37f62011-01-02 03:07:57 +00009088 if (image_matte == MagickFalse)
9089 {
9090 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9091 image_matte=MagickFalse;
9092 }
glennrp0fe50b42010-11-16 03:52:51 +00009093
glennrp0b206f52011-01-07 04:55:32 +00009094 else
glennrp5aa37f62011-01-02 03:07:57 +00009095 {
9096 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9097 image_matte=MagickTrue;
9098 }
9099 }
9100 else
glennrp8bb3a022010-12-13 20:40:04 +00009101 {
glennrp5aa37f62011-01-02 03:07:57 +00009102 if (image_matte == MagickFalse)
9103 {
9104 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9105 image_matte=MagickFalse;
9106 }
glennrp8bb3a022010-12-13 20:40:04 +00009107
glennrp0b206f52011-01-07 04:55:32 +00009108 else
glennrp5aa37f62011-01-02 03:07:57 +00009109 {
9110 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9111 image_matte=MagickTrue;
9112 }
9113 }
glennrp0fe50b42010-11-16 03:52:51 +00009114 }
glennrp5aa37f62011-01-02 03:07:57 +00009115
cristy3ed852e2009-09-05 21:47:34 +00009116 }
glennrp0fe50b42010-11-16 03:52:51 +00009117
cristy3ed852e2009-09-05 21:47:34 +00009118 if (logging != MagickFalse)
9119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009120 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009121
glennrp5af765f2010-03-30 11:12:18 +00009122 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009123 {
9124 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9125 ping_color_type == PNG_COLOR_TYPE_RGB ||
9126 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9127 ping_bit_depth=8;
9128 }
cristy3ed852e2009-09-05 21:47:34 +00009129
glennrpd6bf1612010-12-17 17:28:54 +00009130 old_bit_depth=ping_bit_depth;
9131
glennrp5af765f2010-03-30 11:12:18 +00009132 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009133 {
glennrp8d579662011-02-23 02:05:02 +00009134 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9135 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009136 }
glennrp8640fb52010-11-23 15:48:26 +00009137
glennrp5af765f2010-03-30 11:12:18 +00009138 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009139 {
cristy35ef8242010-06-03 16:24:13 +00009140 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009141 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009142
9143 if (image->colors == 0)
9144 {
glennrp0fe50b42010-11-16 03:52:51 +00009145 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00009146 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00009147 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009148 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009149 }
9150
cristy35ef8242010-06-03 16:24:13 +00009151 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009152 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009153 }
glennrp2b013e42010-11-24 16:55:50 +00009154
glennrpd6bf1612010-12-17 17:28:54 +00009155 if (logging != MagickFalse)
9156 {
9157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9158 " Number of colors: %.20g",(double) image_colors);
9159
9160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9161 " Tentative PNG bit depth: %d",ping_bit_depth);
9162 }
9163
9164 if (ping_bit_depth < (int) mng_info->write_png_depth)
9165 ping_bit_depth = mng_info->write_png_depth;
9166 }
glennrp2cc891a2010-12-24 13:44:32 +00009167
glennrp5af765f2010-03-30 11:12:18 +00009168 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009169
cristy3ed852e2009-09-05 21:47:34 +00009170 if (logging != MagickFalse)
9171 {
9172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009173 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009174
cristy3ed852e2009-09-05 21:47:34 +00009175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009176 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009177
cristy3ed852e2009-09-05 21:47:34 +00009178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009179 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009180
cristy3ed852e2009-09-05 21:47:34 +00009181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009182
glennrp8640fb52010-11-23 15:48:26 +00009183 " image->depth: %.20g",(double) image->depth);
9184
9185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009186 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009187 }
9188
glennrp58e01762011-01-07 15:28:54 +00009189 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009190 {
glennrp4f25bd02011-01-01 18:51:28 +00009191 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009192 {
glennrp7c4c9e62011-03-21 20:23:32 +00009193 if (mng_info->write_png_colortype == 0)
9194 {
9195 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009196
glennrp7c4c9e62011-03-21 20:23:32 +00009197 if (ping_have_color != MagickFalse)
9198 ping_color_type=PNG_COLOR_TYPE_RGBA;
9199 }
glennrp4f25bd02011-01-01 18:51:28 +00009200
9201 /*
9202 * Determine if there is any transparent color.
9203 */
9204 if (number_transparent + number_semitransparent == 0)
9205 {
9206 /*
9207 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9208 */
glennrpa6a06632011-01-19 15:15:34 +00009209
glennrp4f25bd02011-01-01 18:51:28 +00009210 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009211
9212 if (mng_info->write_png_colortype == 0)
9213 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009214 }
9215
9216 else
9217 {
9218 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009219 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009220
9221 mask=0xffff;
9222
9223 if (ping_bit_depth == 8)
9224 mask=0x00ff;
9225
9226 if (ping_bit_depth == 4)
9227 mask=0x000f;
9228
9229 if (ping_bit_depth == 2)
9230 mask=0x0003;
9231
9232 if (ping_bit_depth == 1)
9233 mask=0x0001;
9234
9235 ping_trans_color.red=(png_uint_16)
9236 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9237
9238 ping_trans_color.green=(png_uint_16)
9239 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9240
9241 ping_trans_color.blue=(png_uint_16)
9242 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9243
9244 ping_trans_color.gray=(png_uint_16)
9245 (ScaleQuantumToShort(PixelIntensityToQuantum(
9246 image->colormap)) & mask);
9247
9248 ping_trans_color.index=(png_byte) 0;
9249
9250 ping_have_tRNS=MagickTrue;
9251 }
9252
9253 if (ping_have_tRNS != MagickFalse)
9254 {
9255 /*
glennrpfd05d622011-02-25 04:10:33 +00009256 * Determine if there is one and only one transparent color
9257 * and if so if it is fully transparent.
9258 */
9259 if (ping_have_cheap_transparency == MagickFalse)
9260 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009261 }
9262
9263 if (ping_have_tRNS != MagickFalse)
9264 {
glennrp7c4c9e62011-03-21 20:23:32 +00009265 if (mng_info->write_png_colortype == 0)
9266 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009267
9268 if (image_depth == 8)
9269 {
9270 ping_trans_color.red&=0xff;
9271 ping_trans_color.green&=0xff;
9272 ping_trans_color.blue&=0xff;
9273 ping_trans_color.gray&=0xff;
9274 }
9275 }
9276 }
cristy3ed852e2009-09-05 21:47:34 +00009277 else
9278 {
cristy3ed852e2009-09-05 21:47:34 +00009279 if (image_depth == 8)
9280 {
glennrp5af765f2010-03-30 11:12:18 +00009281 ping_trans_color.red&=0xff;
9282 ping_trans_color.green&=0xff;
9283 ping_trans_color.blue&=0xff;
9284 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009285 }
9286 }
9287 }
glennrp8640fb52010-11-23 15:48:26 +00009288
cristy3ed852e2009-09-05 21:47:34 +00009289 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009290
glennrp2e09f552010-11-14 00:38:48 +00009291 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009292 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009293
glennrp39992b42010-11-14 00:03:43 +00009294 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009295 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009296 ping_have_color == MagickFalse &&
9297 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009298 {
cristy35ef8242010-06-03 16:24:13 +00009299 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009300
cristy3ed852e2009-09-05 21:47:34 +00009301 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009302 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009303
glennrp7c4c9e62011-03-21 20:23:32 +00009304 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009305 {
glennrp5af765f2010-03-30 11:12:18 +00009306 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009307
cristy3ed852e2009-09-05 21:47:34 +00009308 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009309 {
9310 if (logging != MagickFalse)
9311 {
9312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9313 " Scaling ping_trans_color (0)");
9314 }
9315 ping_trans_color.gray*=0x0101;
9316 }
cristy3ed852e2009-09-05 21:47:34 +00009317 }
glennrp0fe50b42010-11-16 03:52:51 +00009318
cristy3ed852e2009-09-05 21:47:34 +00009319 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9320 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009321
glennrp136ee3a2011-04-27 15:47:45 +00009322 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009323 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009324 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009325
cristy3ed852e2009-09-05 21:47:34 +00009326 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009327 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009328
cristy3ed852e2009-09-05 21:47:34 +00009329 else
9330 {
glennrp5af765f2010-03-30 11:12:18 +00009331 ping_bit_depth=8;
9332 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009333 {
9334 if(!mng_info->write_png_depth)
9335 {
glennrp5af765f2010-03-30 11:12:18 +00009336 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009337
cristy35ef8242010-06-03 16:24:13 +00009338 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009339 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009340 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009341 }
9342 }
glennrp2b013e42010-11-24 16:55:50 +00009343
glennrp0fe50b42010-11-16 03:52:51 +00009344 else if (ping_color_type ==
9345 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009346 mng_info->IsPalette)
9347 {
cristy3ed852e2009-09-05 21:47:34 +00009348 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009349
cristy3ed852e2009-09-05 21:47:34 +00009350 int
9351 depth_4_ok=MagickTrue,
9352 depth_2_ok=MagickTrue,
9353 depth_1_ok=MagickTrue;
9354
cristybb503372010-05-27 20:51:26 +00009355 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009356 {
9357 unsigned char
9358 intensity;
9359
9360 intensity=ScaleQuantumToChar(image->colormap[i].red);
9361
9362 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9363 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9364 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9365 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009366 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009367 depth_1_ok=MagickFalse;
9368 }
glennrp2b013e42010-11-24 16:55:50 +00009369
cristy3ed852e2009-09-05 21:47:34 +00009370 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009371 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009372
cristy3ed852e2009-09-05 21:47:34 +00009373 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009374 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009375
cristy3ed852e2009-09-05 21:47:34 +00009376 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009377 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009378 }
9379 }
glennrp2b013e42010-11-24 16:55:50 +00009380
glennrp5af765f2010-03-30 11:12:18 +00009381 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009382 }
glennrp0fe50b42010-11-16 03:52:51 +00009383
cristy3ed852e2009-09-05 21:47:34 +00009384 else
glennrp0fe50b42010-11-16 03:52:51 +00009385
cristy3ed852e2009-09-05 21:47:34 +00009386 if (mng_info->IsPalette)
9387 {
glennrp17a14852010-05-10 03:01:59 +00009388 number_colors=image_colors;
9389
cristy3ed852e2009-09-05 21:47:34 +00009390 if (image_depth <= 8)
9391 {
cristy3ed852e2009-09-05 21:47:34 +00009392 /*
9393 Set image palette.
9394 */
glennrp5af765f2010-03-30 11:12:18 +00009395 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009396
glennrp58e01762011-01-07 15:28:54 +00009397 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009398 {
glennrp9c1eb072010-06-06 22:19:15 +00009399 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009400
glennrp3b51f0e2010-11-27 18:14:08 +00009401 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9403 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009404 }
glennrp0fe50b42010-11-16 03:52:51 +00009405
cristy3ed852e2009-09-05 21:47:34 +00009406 else
9407 {
cristybb503372010-05-27 20:51:26 +00009408 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009409 {
9410 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9411 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9412 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9413 }
glennrp0fe50b42010-11-16 03:52:51 +00009414
glennrp3b51f0e2010-11-27 18:14:08 +00009415 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009417 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009418 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009419
glennrp39992b42010-11-14 00:03:43 +00009420 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009421 }
glennrp0fe50b42010-11-16 03:52:51 +00009422
cristy3ed852e2009-09-05 21:47:34 +00009423 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009424 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009425 {
cristybefe4d22010-06-07 01:18:58 +00009426 size_t
9427 one;
9428
glennrp5af765f2010-03-30 11:12:18 +00009429 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009430 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009431
glennrpd17915c2011-04-29 14:24:22 +00009432 while ((one << ping_bit_depth) < (ssize_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009433 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009434 }
glennrp0fe50b42010-11-16 03:52:51 +00009435
glennrp5af765f2010-03-30 11:12:18 +00009436 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009437
glennrp58e01762011-01-07 15:28:54 +00009438 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009439 {
glennrp0fe50b42010-11-16 03:52:51 +00009440 /*
glennrpd6bf1612010-12-17 17:28:54 +00009441 * Set up trans_colors array.
9442 */
glennrp0fe50b42010-11-16 03:52:51 +00009443 assert(number_colors <= 256);
9444
glennrpd6bf1612010-12-17 17:28:54 +00009445 ping_num_trans=(unsigned short) (number_transparent +
9446 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009447
9448 if (ping_num_trans == 0)
9449 ping_have_tRNS=MagickFalse;
9450
glennrpd6bf1612010-12-17 17:28:54 +00009451 else
glennrp0fe50b42010-11-16 03:52:51 +00009452 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009453 if (logging != MagickFalse)
9454 {
9455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9456 " Scaling ping_trans_color (1)");
9457 }
glennrpd6bf1612010-12-17 17:28:54 +00009458 ping_have_tRNS=MagickTrue;
9459
9460 for (i=0; i < ping_num_trans; i++)
9461 {
9462 ping_trans_alpha[i]= (png_byte) (255-
9463 ScaleQuantumToChar(image->colormap[i].opacity));
9464 }
glennrp0fe50b42010-11-16 03:52:51 +00009465 }
9466 }
cristy3ed852e2009-09-05 21:47:34 +00009467 }
9468 }
glennrp0fe50b42010-11-16 03:52:51 +00009469
cristy3ed852e2009-09-05 21:47:34 +00009470 else
9471 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009472
cristy3ed852e2009-09-05 21:47:34 +00009473 if (image_depth < 8)
9474 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009475
cristy3ed852e2009-09-05 21:47:34 +00009476 if ((save_image_depth == 16) && (image_depth == 8))
9477 {
glennrp4f25bd02011-01-01 18:51:28 +00009478 if (logging != MagickFalse)
9479 {
9480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9481 " Scaling ping_trans_color from (%d,%d,%d)",
9482 (int) ping_trans_color.red,
9483 (int) ping_trans_color.green,
9484 (int) ping_trans_color.blue);
9485 }
9486
glennrp5af765f2010-03-30 11:12:18 +00009487 ping_trans_color.red*=0x0101;
9488 ping_trans_color.green*=0x0101;
9489 ping_trans_color.blue*=0x0101;
9490 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009491
9492 if (logging != MagickFalse)
9493 {
9494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9495 " to (%d,%d,%d)",
9496 (int) ping_trans_color.red,
9497 (int) ping_trans_color.green,
9498 (int) ping_trans_color.blue);
9499 }
cristy3ed852e2009-09-05 21:47:34 +00009500 }
9501 }
9502
cristy4383ec82011-01-05 15:42:32 +00009503 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9504 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009505
cristy3ed852e2009-09-05 21:47:34 +00009506 /*
9507 Adjust background and transparency samples in sub-8-bit grayscale files.
9508 */
glennrp5af765f2010-03-30 11:12:18 +00009509 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009510 PNG_COLOR_TYPE_GRAY)
9511 {
9512 png_uint_16
9513 maxval;
9514
cristy35ef8242010-06-03 16:24:13 +00009515 size_t
9516 one=1;
9517
cristy22ffd972010-06-03 16:51:47 +00009518 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009519
glennrp4f25bd02011-01-01 18:51:28 +00009520 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009521 {
cristy3ed852e2009-09-05 21:47:34 +00009522
glennrpa521b2f2010-10-29 04:11:03 +00009523 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00009524 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
9525
9526 if (logging != MagickFalse)
9527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009528 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9530 " background_color index is %d",
9531 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009532
glennrp991d11d2010-11-12 21:55:28 +00009533 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009534 }
cristy3ed852e2009-09-05 21:47:34 +00009535
glennrp5af765f2010-03-30 11:12:18 +00009536 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
9537 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00009538 }
glennrp17a14852010-05-10 03:01:59 +00009539
glennrp26f37912010-12-23 16:22:42 +00009540 if (ping_exclude_bKGD == MagickFalse)
9541 {
glennrp1273f7b2011-02-24 03:20:30 +00009542 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009543 {
9544 /*
9545 Identify which colormap entry is the background color.
9546 */
9547
glennrp17a14852010-05-10 03:01:59 +00009548 number_colors=image_colors;
9549
glennrpa521b2f2010-10-29 04:11:03 +00009550 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9551 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009552 break;
9553
9554 ping_background.index=(png_byte) i;
9555
glennrp3b51f0e2010-11-27 18:14:08 +00009556 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009557 {
9558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009559 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009560 }
glennrp0fe50b42010-11-16 03:52:51 +00009561
cristy13d07042010-11-21 20:56:18 +00009562 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009563 {
9564 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009565
9566 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009567 {
9568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9569 " background =(%d,%d,%d)",
9570 (int) ping_background.red,
9571 (int) ping_background.green,
9572 (int) ping_background.blue);
9573 }
9574 }
glennrpa521b2f2010-10-29 04:11:03 +00009575
glennrpd6bf1612010-12-17 17:28:54 +00009576 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009577 {
glennrp3b51f0e2010-11-27 18:14:08 +00009578 if (logging != MagickFalse)
9579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9580 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009581 ping_have_bKGD = MagickFalse;
9582 }
glennrp17a14852010-05-10 03:01:59 +00009583 }
glennrp26f37912010-12-23 16:22:42 +00009584 }
glennrp17a14852010-05-10 03:01:59 +00009585
cristy3ed852e2009-09-05 21:47:34 +00009586 if (logging != MagickFalse)
9587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009588 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009589 /*
9590 Initialize compression level and filtering.
9591 */
9592 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009593 {
9594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9595 " Setting up deflate compression");
9596
9597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9598 " Compression buffer size: 32768");
9599 }
9600
cristy3ed852e2009-09-05 21:47:34 +00009601 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009602
cristy3ed852e2009-09-05 21:47:34 +00009603 if (logging != MagickFalse)
9604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9605 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009606
cristy3ed852e2009-09-05 21:47:34 +00009607 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00009608
cristy3ed852e2009-09-05 21:47:34 +00009609 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9610 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009611
cristy3ed852e2009-09-05 21:47:34 +00009612 if (quality > 9)
9613 {
9614 int
9615 level;
9616
cristybb503372010-05-27 20:51:26 +00009617 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009618
cristy3ed852e2009-09-05 21:47:34 +00009619 if (logging != MagickFalse)
9620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9621 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00009622
cristy3ed852e2009-09-05 21:47:34 +00009623 png_set_compression_level(ping,level);
9624 }
glennrp0fe50b42010-11-16 03:52:51 +00009625
cristy3ed852e2009-09-05 21:47:34 +00009626 else
9627 {
9628 if (logging != MagickFalse)
9629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9630 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00009631
cristy3ed852e2009-09-05 21:47:34 +00009632 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9633 }
glennrp0fe50b42010-11-16 03:52:51 +00009634
cristy3ed852e2009-09-05 21:47:34 +00009635 if (logging != MagickFalse)
9636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9637 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009638
glennrp2b013e42010-11-24 16:55:50 +00009639#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00009640 /* This became available in libpng-1.0.9. Output must be a MNG. */
9641 if (mng_info->write_mng && ((quality % 10) == 7))
9642 {
9643 if (logging != MagickFalse)
9644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9645 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00009646
glennrp5af765f2010-03-30 11:12:18 +00009647 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00009648 }
glennrp0fe50b42010-11-16 03:52:51 +00009649
cristy3ed852e2009-09-05 21:47:34 +00009650 else
9651 if (logging != MagickFalse)
9652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9653 " Filter_type: 0");
9654#endif
glennrp2b013e42010-11-24 16:55:50 +00009655
cristy3ed852e2009-09-05 21:47:34 +00009656 {
9657 int
9658 base_filter;
9659
9660 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00009661 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009662
glennrp26c990a2010-11-23 02:23:20 +00009663 else
glennrp8640fb52010-11-23 15:48:26 +00009664 if ((quality % 10) != 5)
9665 base_filter=(int) quality % 10;
9666
9667 else
9668 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9669 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9670 (quality < 50))
9671 base_filter=PNG_NO_FILTERS;
9672
9673 else
9674 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009675
cristy3ed852e2009-09-05 21:47:34 +00009676 if (logging != MagickFalse)
9677 {
9678 if (base_filter == PNG_ALL_FILTERS)
9679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9680 " Base filter method: ADAPTIVE");
9681 else
9682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9683 " Base filter method: NONE");
9684 }
glennrp2b013e42010-11-24 16:55:50 +00009685
cristy3ed852e2009-09-05 21:47:34 +00009686 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9687 }
9688
glennrp823b55c2011-03-14 18:46:46 +00009689 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9690 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009691 {
9692 ResetImageProfileIterator(image);
9693 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009694 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009695 profile=GetImageProfile(image,name);
9696
9697 if (profile != (StringInfo *) NULL)
9698 {
glennrp5af765f2010-03-30 11:12:18 +00009699#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009700 if ((LocaleCompare(name,"ICC") == 0) ||
9701 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009702 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009703
9704 if (ping_exclude_iCCP == MagickFalse)
9705 {
9706 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009707#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009708 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009709#else
9710 (png_const_bytep) GetStringInfoDatum(profile),
9711#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009712 (png_uint_32) GetStringInfoLength(profile));
9713 }
glennrp26f37912010-12-23 16:22:42 +00009714 }
glennrp0fe50b42010-11-16 03:52:51 +00009715
glennrpc8cbc5d2011-01-01 00:12:34 +00009716 else
cristy3ed852e2009-09-05 21:47:34 +00009717#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009718 if (ping_exclude_zCCP == MagickFalse)
9719 {
glennrpcf002022011-01-30 02:38:15 +00009720 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009721 (unsigned char *) name,(unsigned char *) name,
9722 GetStringInfoDatum(profile),
9723 (png_uint_32) GetStringInfoLength(profile));
9724 }
9725 }
glennrp0b206f52011-01-07 04:55:32 +00009726
glennrpc8cbc5d2011-01-01 00:12:34 +00009727 if (logging != MagickFalse)
9728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9729 " Setting up text chunk with %s profile",name);
9730
9731 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009732 }
cristy3ed852e2009-09-05 21:47:34 +00009733 }
9734
9735#if defined(PNG_WRITE_sRGB_SUPPORTED)
9736 if ((mng_info->have_write_global_srgb == 0) &&
9737 ((image->rendering_intent != UndefinedIntent) ||
9738 (image->colorspace == sRGBColorspace)))
9739 {
glennrp26f37912010-12-23 16:22:42 +00009740 if (ping_exclude_sRGB == MagickFalse)
9741 {
9742 /*
9743 Note image rendering intent.
9744 */
9745 if (logging != MagickFalse)
9746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9747 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009748
glennrp26f37912010-12-23 16:22:42 +00009749 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009750 Magick_RenderingIntent_to_PNG_RenderingIntent(
9751 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009752
glennrp26f37912010-12-23 16:22:42 +00009753 if (ping_exclude_gAMA == MagickFalse)
9754 png_set_gAMA(ping,ping_info,0.45455);
9755 }
cristy3ed852e2009-09-05 21:47:34 +00009756 }
glennrp26f37912010-12-23 16:22:42 +00009757
glennrp5af765f2010-03-30 11:12:18 +00009758 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009759#endif
9760 {
glennrp2cc891a2010-12-24 13:44:32 +00009761 if (ping_exclude_gAMA == MagickFalse &&
9762 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009763 (image->gamma < .45 || image->gamma > .46)))
9764 {
cristy3ed852e2009-09-05 21:47:34 +00009765 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9766 {
9767 /*
9768 Note image gamma.
9769 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9770 */
9771 if (logging != MagickFalse)
9772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9773 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009774
cristy3ed852e2009-09-05 21:47:34 +00009775 png_set_gAMA(ping,ping_info,image->gamma);
9776 }
glennrp26f37912010-12-23 16:22:42 +00009777 }
glennrp2b013e42010-11-24 16:55:50 +00009778
glennrp26f37912010-12-23 16:22:42 +00009779 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009780 {
glennrp26f37912010-12-23 16:22:42 +00009781 if ((mng_info->have_write_global_chrm == 0) &&
9782 (image->chromaticity.red_primary.x != 0.0))
9783 {
9784 /*
9785 Note image chromaticity.
9786 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9787 */
9788 PrimaryInfo
9789 bp,
9790 gp,
9791 rp,
9792 wp;
cristy3ed852e2009-09-05 21:47:34 +00009793
glennrp26f37912010-12-23 16:22:42 +00009794 wp=image->chromaticity.white_point;
9795 rp=image->chromaticity.red_primary;
9796 gp=image->chromaticity.green_primary;
9797 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009798
glennrp26f37912010-12-23 16:22:42 +00009799 if (logging != MagickFalse)
9800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9801 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009802
glennrp26f37912010-12-23 16:22:42 +00009803 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9804 bp.x,bp.y);
9805 }
9806 }
cristy3ed852e2009-09-05 21:47:34 +00009807 }
glennrpdfd70802010-11-14 01:23:35 +00009808
glennrp5af765f2010-03-30 11:12:18 +00009809 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009810
9811 if (mng_info->write_mng)
9812 png_set_sig_bytes(ping,8);
9813
9814 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9815
glennrpd6bf1612010-12-17 17:28:54 +00009816 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009817 {
9818 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009819 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009820 {
glennrp5af765f2010-03-30 11:12:18 +00009821 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009822
glennrp5af765f2010-03-30 11:12:18 +00009823 if (ping_bit_depth < 8)
9824 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009825 }
glennrp0fe50b42010-11-16 03:52:51 +00009826
cristy3ed852e2009-09-05 21:47:34 +00009827 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009828 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009829 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009830 }
9831
glennrp0e8ea192010-12-24 18:00:33 +00009832 if (ping_need_colortype_warning != MagickFalse ||
9833 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009834 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009835 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009836 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009837 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009838 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009839 {
9840 if (logging != MagickFalse)
9841 {
glennrp0e8ea192010-12-24 18:00:33 +00009842 if (ping_need_colortype_warning != MagickFalse)
9843 {
9844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9845 " Image has transparency but tRNS chunk was excluded");
9846 }
9847
cristy3ed852e2009-09-05 21:47:34 +00009848 if (mng_info->write_png_depth)
9849 {
9850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9851 " Defined PNG:bit-depth=%u, Computed depth=%u",
9852 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009853 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009854 }
glennrp0e8ea192010-12-24 18:00:33 +00009855
cristy3ed852e2009-09-05 21:47:34 +00009856 if (mng_info->write_png_colortype)
9857 {
9858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9859 " Defined PNG:color-type=%u, Computed color type=%u",
9860 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009861 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009862 }
9863 }
glennrp0e8ea192010-12-24 18:00:33 +00009864
glennrp3bd2e412010-08-10 13:34:52 +00009865 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009866 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9867 }
9868
glennrp58e01762011-01-07 15:28:54 +00009869 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009870 {
9871 /* Add an opaque matte channel */
9872 image->matte = MagickTrue;
9873 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009874
glennrpb4a13412010-05-05 12:47:19 +00009875 if (logging != MagickFalse)
9876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9877 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009878 }
9879
glennrp0e319732011-01-25 21:53:13 +00009880 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009881 {
glennrp991d11d2010-11-12 21:55:28 +00009882 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009883 {
glennrp991d11d2010-11-12 21:55:28 +00009884 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009885 if (logging != MagickFalse)
9886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9887 " Setting ping_have_tRNS=MagickTrue.");
9888 }
glennrpe9c26dc2010-05-30 01:56:35 +00009889 }
9890
cristy3ed852e2009-09-05 21:47:34 +00009891 if (logging != MagickFalse)
9892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9893 " Writing PNG header chunks");
9894
glennrp5af765f2010-03-30 11:12:18 +00009895 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9896 ping_bit_depth,ping_color_type,
9897 ping_interlace_method,ping_compression_method,
9898 ping_filter_method);
9899
glennrp39992b42010-11-14 00:03:43 +00009900 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9901 {
glennrpf09bded2011-01-08 01:15:59 +00009902 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009903
glennrp3b51f0e2010-11-27 18:14:08 +00009904 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009905 {
glennrp8640fb52010-11-23 15:48:26 +00009906 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009907 {
glennrpd6bf1612010-12-17 17:28:54 +00009908 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009910 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9911 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009912 (int) palette[i].red,
9913 (int) palette[i].green,
9914 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009915 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009916 (int) ping_trans_alpha[i]);
9917 else
9918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009919 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009920 (int) i,
9921 (int) palette[i].red,
9922 (int) palette[i].green,
9923 (int) palette[i].blue);
9924 }
glennrp39992b42010-11-14 00:03:43 +00009925 }
glennrp39992b42010-11-14 00:03:43 +00009926 }
9927
glennrp26f37912010-12-23 16:22:42 +00009928 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009929 {
glennrp26f37912010-12-23 16:22:42 +00009930 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +00009931 {
glennrp26f37912010-12-23 16:22:42 +00009932 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +00009933 if (logging)
9934 {
9935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9936 " Setting up bKGD chunk");
9937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9938 " background color = (%d,%d,%d)",
9939 (int) ping_background.red,
9940 (int) ping_background.green,
9941 (int) ping_background.blue);
9942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9943 " index = %d, gray=%d",
9944 (int) ping_background.index,
9945 (int) ping_background.gray);
9946 }
9947 }
glennrp26f37912010-12-23 16:22:42 +00009948 }
9949
9950 if (ping_exclude_pHYs == MagickFalse)
9951 {
9952 if (ping_have_pHYs != MagickFalse)
9953 {
9954 png_set_pHYs(ping,ping_info,
9955 ping_pHYs_x_resolution,
9956 ping_pHYs_y_resolution,
9957 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009958
9959 if (logging)
9960 {
9961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9962 " Setting up pHYs chunk");
9963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9964 " x_resolution=%lu",
9965 (unsigned long) ping_pHYs_x_resolution);
9966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9967 " y_resolution=%lu",
9968 (unsigned long) ping_pHYs_y_resolution);
9969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9970 " unit_type=%lu",
9971 (unsigned long) ping_pHYs_unit_type);
9972 }
glennrp26f37912010-12-23 16:22:42 +00009973 }
glennrpdfd70802010-11-14 01:23:35 +00009974 }
9975
9976#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009977 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009978 {
glennrp26f37912010-12-23 16:22:42 +00009979 if (image->page.x || image->page.y)
9980 {
9981 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9982 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009983
glennrp26f37912010-12-23 16:22:42 +00009984 if (logging != MagickFalse)
9985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9986 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9987 (int) image->page.x, (int) image->page.y);
9988 }
glennrpdfd70802010-11-14 01:23:35 +00009989 }
9990#endif
9991
glennrpda8f3a72011-02-27 23:54:12 +00009992 if (mng_info->need_blob != MagickFalse)
9993 {
9994 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9995 MagickFalse)
9996 png_error(ping,"WriteBlob Failed");
9997
9998 ping_have_blob=MagickTrue;
9999 }
10000
cristy3ed852e2009-09-05 21:47:34 +000010001 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010002
glennrp39992b42010-11-14 00:03:43 +000010003 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010004 {
glennrp3b51f0e2010-11-27 18:14:08 +000010005 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010006 {
10007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10008 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10009 }
10010
10011 if (ping_color_type == 3)
10012 (void) png_set_tRNS(ping, ping_info,
10013 ping_trans_alpha,
10014 ping_num_trans,
10015 NULL);
10016
10017 else
10018 {
10019 (void) png_set_tRNS(ping, ping_info,
10020 NULL,
10021 0,
10022 &ping_trans_color);
10023
glennrp3b51f0e2010-11-27 18:14:08 +000010024 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010025 {
10026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010027 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010028 (int) ping_trans_color.red,
10029 (int) ping_trans_color.green,
10030 (int) ping_trans_color.blue);
10031 }
10032 }
glennrp991d11d2010-11-12 21:55:28 +000010033 }
10034
cristy3ed852e2009-09-05 21:47:34 +000010035 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010036 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010037
cristy3ed852e2009-09-05 21:47:34 +000010038 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010039
cristy3ed852e2009-09-05 21:47:34 +000010040 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010041 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010042
glennrp26f37912010-12-23 16:22:42 +000010043 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010044 {
glennrp4f25bd02011-01-01 18:51:28 +000010045 if ((image->page.width != 0 && image->page.width != image->columns) ||
10046 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010047 {
10048 unsigned char
10049 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010050
glennrp26f37912010-12-23 16:22:42 +000010051 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10052 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010053 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010054 PNGLong(chunk+4,(png_uint_32) image->page.width);
10055 PNGLong(chunk+8,(png_uint_32) image->page.height);
10056 chunk[12]=0; /* unit = pixels */
10057 (void) WriteBlob(image,13,chunk);
10058 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10059 }
cristy3ed852e2009-09-05 21:47:34 +000010060 }
10061
10062#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010063 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010064#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010065 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010066#undef PNG_HAVE_IDAT
10067#endif
10068
10069 png_set_packing(ping);
10070 /*
10071 Allocate memory.
10072 */
10073 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010074 if (image_depth > 8)
10075 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010076 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010077 {
glennrpb4a13412010-05-05 12:47:19 +000010078 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010079 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010080 break;
glennrp0fe50b42010-11-16 03:52:51 +000010081
glennrpb4a13412010-05-05 12:47:19 +000010082 case PNG_COLOR_TYPE_GRAY_ALPHA:
10083 rowbytes*=2;
10084 break;
glennrp0fe50b42010-11-16 03:52:51 +000010085
glennrpb4a13412010-05-05 12:47:19 +000010086 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010087 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010088 break;
glennrp0fe50b42010-11-16 03:52:51 +000010089
glennrpb4a13412010-05-05 12:47:19 +000010090 default:
10091 break;
cristy3ed852e2009-09-05 21:47:34 +000010092 }
glennrp3b51f0e2010-11-27 18:14:08 +000010093
10094 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010095 {
10096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10097 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010098
glennrpb4a13412010-05-05 12:47:19 +000010099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010100 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010101 }
glennrpcf002022011-01-30 02:38:15 +000010102 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10103 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010104
glennrpcf002022011-01-30 02:38:15 +000010105 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010106 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010107
cristy3ed852e2009-09-05 21:47:34 +000010108 /*
10109 Initialize image scanlines.
10110 */
glennrp5af765f2010-03-30 11:12:18 +000010111 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010112 {
10113 /*
10114 PNG write failed.
10115 */
10116#ifdef PNG_DEBUG
10117 if (image_info->verbose)
10118 (void) printf("PNG write has failed.\n");
10119#endif
10120 png_destroy_write_struct(&ping,&ping_info);
10121 if (quantum_info != (QuantumInfo *) NULL)
10122 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010123 if (ping_pixels != (unsigned char *) NULL)
10124 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010125#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010126 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010127#endif
glennrpda8f3a72011-02-27 23:54:12 +000010128 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010129 (void) CloseBlob(image);
10130 image_info=DestroyImageInfo(image_info);
10131 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010132 return(MagickFalse);
10133 }
cristyed552522009-10-16 14:04:35 +000010134 quantum_info=AcquireQuantumInfo(image_info,image);
10135 if (quantum_info == (QuantumInfo *) NULL)
10136 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010137 quantum_info->format=UndefinedQuantumFormat;
10138 quantum_info->depth=image_depth;
10139 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010140
cristy3ed852e2009-09-05 21:47:34 +000010141 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010142 !mng_info->write_png32) &&
10143 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010144 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010145 image_matte == MagickFalse &&
10146 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010147 {
glennrp8bb3a022010-12-13 20:40:04 +000010148 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010149 register const PixelPacket
10150 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010151
cristy3ed852e2009-09-05 21:47:34 +000010152 quantum_info->depth=8;
10153 for (pass=0; pass < num_passes; pass++)
10154 {
10155 /*
10156 Convert PseudoClass image to a PNG monochrome image.
10157 */
cristybb503372010-05-27 20:51:26 +000010158 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010159 {
glennrpd71e86a2011-02-24 01:28:37 +000010160 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10162 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010163
cristy3ed852e2009-09-05 21:47:34 +000010164 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010165
cristy3ed852e2009-09-05 21:47:34 +000010166 if (p == (const PixelPacket *) NULL)
10167 break;
glennrp0fe50b42010-11-16 03:52:51 +000010168
cristy3ed852e2009-09-05 21:47:34 +000010169 if (mng_info->IsPalette)
10170 {
10171 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010172 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010173 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10174 mng_info->write_png_depth &&
10175 mng_info->write_png_depth != old_bit_depth)
10176 {
10177 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010178 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010179 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010180 >> (8-old_bit_depth));
10181 }
10182 }
glennrp0fe50b42010-11-16 03:52:51 +000010183
cristy3ed852e2009-09-05 21:47:34 +000010184 else
10185 {
10186 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010187 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010188 }
glennrp0fe50b42010-11-16 03:52:51 +000010189
cristy3ed852e2009-09-05 21:47:34 +000010190 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010191 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010192 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010193 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010194
glennrp3b51f0e2010-11-27 18:14:08 +000010195 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10197 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010198
glennrpcf002022011-01-30 02:38:15 +000010199 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010200 }
10201 if (image->previous == (Image *) NULL)
10202 {
10203 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10204 if (status == MagickFalse)
10205 break;
10206 }
10207 }
10208 }
glennrp0fe50b42010-11-16 03:52:51 +000010209
glennrp8bb3a022010-12-13 20:40:04 +000010210 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010211 {
glennrp0fe50b42010-11-16 03:52:51 +000010212 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010213 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010214 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010215 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010216 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010217 {
glennrp8bb3a022010-12-13 20:40:04 +000010218 register const PixelPacket
10219 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010220
glennrp8bb3a022010-12-13 20:40:04 +000010221 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010222 {
glennrp8bb3a022010-12-13 20:40:04 +000010223
cristybb503372010-05-27 20:51:26 +000010224 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010225 {
10226 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010227
cristy3ed852e2009-09-05 21:47:34 +000010228 if (p == (const PixelPacket *) NULL)
10229 break;
glennrp2cc891a2010-12-24 13:44:32 +000010230
glennrp5af765f2010-03-30 11:12:18 +000010231 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010232 {
glennrp8bb3a022010-12-13 20:40:04 +000010233 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +000010234 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010235 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010236
glennrp8bb3a022010-12-13 20:40:04 +000010237 else
10238 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010239 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010240
glennrp3b51f0e2010-11-27 18:14:08 +000010241 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010243 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010244 }
glennrp2cc891a2010-12-24 13:44:32 +000010245
glennrp8bb3a022010-12-13 20:40:04 +000010246 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10247 {
10248 if (logging != MagickFalse && y == 0)
10249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10250 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010251
glennrp8bb3a022010-12-13 20:40:04 +000010252 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010253 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010254 }
glennrp2cc891a2010-12-24 13:44:32 +000010255
glennrp3b51f0e2010-11-27 18:14:08 +000010256 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010258 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010259
glennrpcf002022011-01-30 02:38:15 +000010260 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010261 }
glennrp2cc891a2010-12-24 13:44:32 +000010262
glennrp8bb3a022010-12-13 20:40:04 +000010263 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010264 {
glennrp8bb3a022010-12-13 20:40:04 +000010265 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10266 if (status == MagickFalse)
10267 break;
cristy3ed852e2009-09-05 21:47:34 +000010268 }
cristy3ed852e2009-09-05 21:47:34 +000010269 }
10270 }
glennrp8bb3a022010-12-13 20:40:04 +000010271
10272 else
10273 {
10274 register const PixelPacket
10275 *p;
10276
10277 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010278 {
glennrp8bb3a022010-12-13 20:40:04 +000010279 if ((image_depth > 8) || (mng_info->write_png24 ||
10280 mng_info->write_png32 ||
10281 (!mng_info->write_png8 && !mng_info->IsPalette)))
10282 {
10283 for (y=0; y < (ssize_t) image->rows; y++)
10284 {
10285 p=GetVirtualPixels(image,0,y,image->columns,1,
10286 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010287
glennrp8bb3a022010-12-13 20:40:04 +000010288 if (p == (const PixelPacket *) NULL)
10289 break;
glennrp2cc891a2010-12-24 13:44:32 +000010290
glennrp8bb3a022010-12-13 20:40:04 +000010291 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10292 {
10293 if (image->storage_class == DirectClass)
10294 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010295 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010296
glennrp8bb3a022010-12-13 20:40:04 +000010297 else
10298 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010299 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010300 }
glennrp2cc891a2010-12-24 13:44:32 +000010301
glennrp8bb3a022010-12-13 20:40:04 +000010302 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10303 {
10304 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010305 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +000010306 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010307
glennrp8bb3a022010-12-13 20:40:04 +000010308 if (logging != MagickFalse && y == 0)
10309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10310 " Writing GRAY_ALPHA PNG pixels (3)");
10311 }
glennrp2cc891a2010-12-24 13:44:32 +000010312
glennrp8bb3a022010-12-13 20:40:04 +000010313 else if (image_matte != MagickFalse)
10314 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010315 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010316
glennrp8bb3a022010-12-13 20:40:04 +000010317 else
10318 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010319 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010320
glennrp8bb3a022010-12-13 20:40:04 +000010321 if (logging != MagickFalse && y == 0)
10322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10323 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010324
glennrpcf002022011-01-30 02:38:15 +000010325 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010326 }
10327 }
glennrp2cc891a2010-12-24 13:44:32 +000010328
glennrp8bb3a022010-12-13 20:40:04 +000010329 else
10330 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10331 mng_info->write_png32 ||
10332 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10333 {
10334 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10335 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10336 {
10337 if (logging != MagickFalse)
10338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10339 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010340
glennrp8bb3a022010-12-13 20:40:04 +000010341 quantum_info->depth=8;
10342 image_depth=8;
10343 }
glennrp2cc891a2010-12-24 13:44:32 +000010344
glennrp8bb3a022010-12-13 20:40:04 +000010345 for (y=0; y < (ssize_t) image->rows; y++)
10346 {
10347 if (logging != MagickFalse && y == 0)
10348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10349 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010350
glennrp770d1932011-03-06 22:11:17 +000010351 p=GetVirtualPixels(image,0,y,image->columns,1,
10352 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010353
glennrp8bb3a022010-12-13 20:40:04 +000010354 if (p == (const PixelPacket *) NULL)
10355 break;
glennrp2cc891a2010-12-24 13:44:32 +000010356
glennrp8bb3a022010-12-13 20:40:04 +000010357 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010358 {
glennrp4bf89732011-03-21 13:48:28 +000010359 quantum_info->depth=image->depth;
10360
glennrp44757ab2011-03-17 12:57:03 +000010361 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010362 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +000010363 }
glennrp2cc891a2010-12-24 13:44:32 +000010364
glennrp8bb3a022010-12-13 20:40:04 +000010365 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10366 {
10367 if (logging != MagickFalse && y == 0)
10368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10369 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010370
glennrp8bb3a022010-12-13 20:40:04 +000010371 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010372 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +000010373 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010374 }
glennrp2cc891a2010-12-24 13:44:32 +000010375
glennrp8bb3a022010-12-13 20:40:04 +000010376 else
glennrp8bb3a022010-12-13 20:40:04 +000010377 {
glennrp179d0752011-03-17 13:02:10 +000010378 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +000010379 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10380
10381 if (logging != MagickFalse && y <= 2)
10382 {
10383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010384 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010385
10386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10387 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10388 (int)ping_pixels[0],(int)ping_pixels[1]);
10389 }
glennrp8bb3a022010-12-13 20:40:04 +000010390 }
glennrpcf002022011-01-30 02:38:15 +000010391 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010392 }
10393 }
glennrp2cc891a2010-12-24 13:44:32 +000010394
glennrp8bb3a022010-12-13 20:40:04 +000010395 if (image->previous == (Image *) NULL)
10396 {
10397 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10398 if (status == MagickFalse)
10399 break;
10400 }
cristy3ed852e2009-09-05 21:47:34 +000010401 }
glennrp8bb3a022010-12-13 20:40:04 +000010402 }
10403 }
10404
cristyb32b90a2009-09-07 21:45:48 +000010405 if (quantum_info != (QuantumInfo *) NULL)
10406 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010407
10408 if (logging != MagickFalse)
10409 {
10410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010411 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010412
cristy3ed852e2009-09-05 21:47:34 +000010413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010414 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010415
cristy3ed852e2009-09-05 21:47:34 +000010416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010417 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010418
cristy3ed852e2009-09-05 21:47:34 +000010419 if (mng_info->write_png_depth)
10420 {
10421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10422 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10423 }
glennrp0fe50b42010-11-16 03:52:51 +000010424
cristy3ed852e2009-09-05 21:47:34 +000010425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010426 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010427
cristy3ed852e2009-09-05 21:47:34 +000010428 if (mng_info->write_png_colortype)
10429 {
10430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10431 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10432 }
glennrp0fe50b42010-11-16 03:52:51 +000010433
cristy3ed852e2009-09-05 21:47:34 +000010434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010435 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010436
cristy3ed852e2009-09-05 21:47:34 +000010437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010438 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010439 }
10440 /*
glennrpa0ed0092011-04-18 16:36:29 +000010441 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010442 */
glennrp823b55c2011-03-14 18:46:46 +000010443 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010444 {
glennrp26f37912010-12-23 16:22:42 +000010445 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010446 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010447 while (property != (const char *) NULL)
10448 {
10449 png_textp
10450 text;
glennrp2cc891a2010-12-24 13:44:32 +000010451
glennrp26f37912010-12-23 16:22:42 +000010452 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +000010453
10454 /* Don't write any "png:" properties; those are just for "identify" */
10455 if (LocaleNCompare(property,"png:",4) != 0 &&
10456
10457 /* Suppress density and units if we wrote a pHYs chunk */
10458 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010459 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010460 LocaleCompare(property,"units") != 0) &&
10461
10462 /* Suppress the IM-generated Date:create and Date:modify */
10463 (ping_exclude_date == MagickFalse ||
10464 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010465 {
glennrpc70af4a2011-03-07 00:08:23 +000010466 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010467 {
glennrpc70af4a2011-03-07 00:08:23 +000010468 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10469 text[0].key=(char *) property;
10470 text[0].text=(char *) value;
10471 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010472
glennrpc70af4a2011-03-07 00:08:23 +000010473 if (ping_exclude_tEXt != MagickFalse)
10474 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10475
10476 else if (ping_exclude_zTXt != MagickFalse)
10477 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10478
10479 else
glennrp26f37912010-12-23 16:22:42 +000010480 {
glennrpc70af4a2011-03-07 00:08:23 +000010481 text[0].compression=image_info->compression == NoCompression ||
10482 (image_info->compression == UndefinedCompression &&
10483 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10484 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010485 }
glennrp2cc891a2010-12-24 13:44:32 +000010486
glennrpc70af4a2011-03-07 00:08:23 +000010487 if (logging != MagickFalse)
10488 {
10489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10490 " Setting up text chunk");
10491
10492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10493 " keyword: %s",text[0].key);
10494 }
10495
10496 png_set_text(ping,ping_info,text,1);
10497 png_free(ping,text);
10498 }
glennrp26f37912010-12-23 16:22:42 +000010499 }
10500 property=GetNextImageProperty(image);
10501 }
cristy3ed852e2009-09-05 21:47:34 +000010502 }
10503
10504 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010505 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010506
10507 if (logging != MagickFalse)
10508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10509 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010510
cristy3ed852e2009-09-05 21:47:34 +000010511 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010512
cristy3ed852e2009-09-05 21:47:34 +000010513 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10514 {
10515 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010516 (ping_width != mng_info->page.width) ||
10517 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010518 {
10519 unsigned char
10520 chunk[32];
10521
10522 /*
10523 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10524 */
10525 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10526 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010527 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010528 chunk[4]=4;
10529 chunk[5]=0; /* frame name separator (no name) */
10530 chunk[6]=1; /* flag for changing delay, for next frame only */
10531 chunk[7]=0; /* flag for changing frame timeout */
10532 chunk[8]=1; /* flag for changing frame clipping for next frame */
10533 chunk[9]=0; /* flag for changing frame sync_id */
10534 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10535 chunk[14]=0; /* clipping boundaries delta type */
10536 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10537 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010538 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010539 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10540 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010541 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010542 (void) WriteBlob(image,31,chunk);
10543 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10544 mng_info->old_framing_mode=4;
10545 mng_info->framing_mode=1;
10546 }
glennrp0fe50b42010-11-16 03:52:51 +000010547
cristy3ed852e2009-09-05 21:47:34 +000010548 else
10549 mng_info->framing_mode=3;
10550 }
10551 if (mng_info->write_mng && !mng_info->need_fram &&
10552 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +000010553 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010554 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010555 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010556
cristy3ed852e2009-09-05 21:47:34 +000010557 /*
10558 Free PNG resources.
10559 */
glennrp5af765f2010-03-30 11:12:18 +000010560
cristy3ed852e2009-09-05 21:47:34 +000010561 png_destroy_write_struct(&ping,&ping_info);
10562
glennrpcf002022011-01-30 02:38:15 +000010563 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010564
10565#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010566 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010567#endif
10568
glennrpda8f3a72011-02-27 23:54:12 +000010569 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010570 (void) CloseBlob(image);
10571
10572 image_info=DestroyImageInfo(image_info);
10573 image=DestroyImage(image);
10574
10575 /* Store bit depth actually written */
10576 s[0]=(char) ping_bit_depth;
10577 s[1]='\0';
10578
10579 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10580
cristy3ed852e2009-09-05 21:47:34 +000010581 if (logging != MagickFalse)
10582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10583 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010584
cristy3ed852e2009-09-05 21:47:34 +000010585 return(MagickTrue);
10586/* End write one PNG image */
10587}
10588
10589/*
10590%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10591% %
10592% %
10593% %
10594% W r i t e P N G I m a g e %
10595% %
10596% %
10597% %
10598%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10599%
10600% WritePNGImage() writes a Portable Network Graphics (PNG) or
10601% Multiple-image Network Graphics (MNG) image file.
10602%
10603% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10604%
10605% The format of the WritePNGImage method is:
10606%
10607% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10608%
10609% A description of each parameter follows:
10610%
10611% o image_info: the image info.
10612%
10613% o image: The image.
10614%
10615% Returns MagickTrue on success, MagickFalse on failure.
10616%
10617% Communicating with the PNG encoder:
10618%
10619% While the datastream written is always in PNG format and normally would
10620% be given the "png" file extension, this method also writes the following
10621% pseudo-formats which are subsets of PNG:
10622%
glennrp5a39f372011-02-25 04:52:16 +000010623% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10624% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010625% is present, the tRNS chunk must only have values 0 and 255
10626% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010627% transparent). If other values are present they will be
10628% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010629% colors are present, they will be quantized to the 4-4-4-1,
10630% 3-3-3-1, or 3-3-2-1 palette.
10631%
10632% If you want better quantization or dithering of the colors
10633% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010634% PNG encoder. The pixels contain 8-bit indices even if
10635% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010636% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010637% PNG grayscale type might be slightly more efficient. Please
10638% note that writing to the PNG8 format may result in loss
10639% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010640%
10641% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10642% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010643% one of the colors as transparent. The only loss incurred
10644% is reduction of sample depth to 8. If the image has more
10645% than one transparent color, has semitransparent pixels, or
10646% has an opaque pixel with the same RGB components as the
10647% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010648%
10649% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10650% transparency is permitted, i.e., the alpha sample for
10651% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010652% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010653% The only loss in data is the reduction of the sample depth
10654% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010655%
10656% o -define: For more precise control of the PNG output, you can use the
10657% Image options "png:bit-depth" and "png:color-type". These
10658% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010659% from the application programming interfaces. The options
10660% are case-independent and are converted to lowercase before
10661% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010662%
10663% png:color-type can be 0, 2, 3, 4, or 6.
10664%
10665% When png:color-type is 0 (Grayscale), png:bit-depth can
10666% be 1, 2, 4, 8, or 16.
10667%
10668% When png:color-type is 2 (RGB), png:bit-depth can
10669% be 8 or 16.
10670%
10671% When png:color-type is 3 (Indexed), png:bit-depth can
10672% be 1, 2, 4, or 8. This refers to the number of bits
10673% used to store the index. The color samples always have
10674% bit-depth 8 in indexed PNG files.
10675%
10676% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10677% png:bit-depth can be 8 or 16.
10678%
glennrp5a39f372011-02-25 04:52:16 +000010679% If the image cannot be written without loss with the requested bit-depth
10680% and color-type, a PNG file will not be written, and the encoder will
10681% return MagickFalse.
10682%
cristy3ed852e2009-09-05 21:47:34 +000010683% Since image encoders should not be responsible for the "heavy lifting",
10684% the user should make sure that ImageMagick has already reduced the
10685% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010686% transparency prior to attempting to write the image with depth, color,
10687% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010688%
glennrp97cefe22011-04-22 16:17:00 +000010689% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +000010690%
cristy3ed852e2009-09-05 21:47:34 +000010691% Note that another definition, "png:bit-depth-written" exists, but it
10692% is not intended for external use. It is only used internally by the
10693% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10694%
10695% It is possible to request that the PNG encoder write previously-formatted
10696% ancillary chunks in the output PNG file, using the "-profile" commandline
10697% option as shown below or by setting the profile via a programming
10698% interface:
10699%
10700% -profile PNG-chunk-x:<file>
10701%
10702% where x is a location flag and <file> is a file containing the chunk
10703% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010704% This encoder will compute the chunk length and CRC, so those must not
10705% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010706%
10707% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10708% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10709% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010710% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010711%
glennrpbb8a7332010-11-13 15:17:35 +000010712% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010713%
glennrp3241bd02010-12-12 04:36:28 +000010714% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010715%
glennrpd6afd542010-11-19 01:53:05 +000010716% o 32-bit depth is reduced to 16.
10717% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10718% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010719% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010720% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010721% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010722% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10723% this can be done without loss and a larger bit depth N was not
10724% requested via the "-define PNG:bit-depth=N" option.
10725% o If matte channel is present but only one transparent color is
10726% present, RGB+tRNS is written instead of RGBA
10727% o Opaque matte channel is removed (or added, if color-type 4 or 6
10728% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010729%
cristy3ed852e2009-09-05 21:47:34 +000010730%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10731*/
10732static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10733 Image *image)
10734{
10735 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010736 excluding,
10737 logging,
10738 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010739 status;
10740
10741 MngInfo
10742 *mng_info;
10743
10744 const char
10745 *value;
10746
10747 int
glennrp21f0e622011-01-07 16:20:57 +000010748 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010749 source;
10750
cristy3ed852e2009-09-05 21:47:34 +000010751 /*
10752 Open image file.
10753 */
10754 assert(image_info != (const ImageInfo *) NULL);
10755 assert(image_info->signature == MagickSignature);
10756 assert(image != (Image *) NULL);
10757 assert(image->signature == MagickSignature);
10758 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010759 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010760 /*
10761 Allocate a MngInfo structure.
10762 */
10763 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010764 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010765
cristy3ed852e2009-09-05 21:47:34 +000010766 if (mng_info == (MngInfo *) NULL)
10767 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010768
cristy3ed852e2009-09-05 21:47:34 +000010769 /*
10770 Initialize members of the MngInfo structure.
10771 */
10772 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10773 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010774 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010775 have_mng_structure=MagickTrue;
10776
10777 /* See if user has requested a specific PNG subformat */
10778
10779 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10780 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10781 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10782
10783 if (mng_info->write_png8)
10784 {
glennrp9c1eb072010-06-06 22:19:15 +000010785 mng_info->write_png_colortype = /* 3 */ 4;
10786 mng_info->write_png_depth = 8;
10787 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010788 }
10789
10790 if (mng_info->write_png24)
10791 {
glennrp9c1eb072010-06-06 22:19:15 +000010792 mng_info->write_png_colortype = /* 2 */ 3;
10793 mng_info->write_png_depth = 8;
10794 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010795
glennrp9c1eb072010-06-06 22:19:15 +000010796 if (image->matte == MagickTrue)
10797 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010798
glennrp9c1eb072010-06-06 22:19:15 +000010799 else
10800 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010801
glennrp9c1eb072010-06-06 22:19:15 +000010802 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010803 }
10804
10805 if (mng_info->write_png32)
10806 {
glennrp9c1eb072010-06-06 22:19:15 +000010807 mng_info->write_png_colortype = /* 6 */ 7;
10808 mng_info->write_png_depth = 8;
10809 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010810
glennrp9c1eb072010-06-06 22:19:15 +000010811 if (image->matte == MagickTrue)
10812 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010813
glennrp9c1eb072010-06-06 22:19:15 +000010814 else
10815 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010816
glennrp9c1eb072010-06-06 22:19:15 +000010817 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010818 }
10819
10820 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010821
cristy3ed852e2009-09-05 21:47:34 +000010822 if (value != (char *) NULL)
10823 {
10824 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010825 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010826
cristy3ed852e2009-09-05 21:47:34 +000010827 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010828 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010829
cristy3ed852e2009-09-05 21:47:34 +000010830 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010831 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010832
cristy3ed852e2009-09-05 21:47:34 +000010833 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010834 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010835
cristy3ed852e2009-09-05 21:47:34 +000010836 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010837 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010838
glennrpbb8a7332010-11-13 15:17:35 +000010839 else
10840 (void) ThrowMagickException(&image->exception,
10841 GetMagickModule(),CoderWarning,
10842 "ignoring invalid defined png:bit-depth",
10843 "=%s",value);
10844
cristy3ed852e2009-09-05 21:47:34 +000010845 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010847 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010848 }
glennrp0fe50b42010-11-16 03:52:51 +000010849
cristy3ed852e2009-09-05 21:47:34 +000010850 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010851
cristy3ed852e2009-09-05 21:47:34 +000010852 if (value != (char *) NULL)
10853 {
10854 /* We must store colortype+1 because 0 is a valid colortype */
10855 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010856 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010857
cristy3ed852e2009-09-05 21:47:34 +000010858 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010859 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010860
cristy3ed852e2009-09-05 21:47:34 +000010861 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010862 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010863
cristy3ed852e2009-09-05 21:47:34 +000010864 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010865 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010866
cristy3ed852e2009-09-05 21:47:34 +000010867 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010868 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010869
glennrpbb8a7332010-11-13 15:17:35 +000010870 else
10871 (void) ThrowMagickException(&image->exception,
10872 GetMagickModule(),CoderWarning,
10873 "ignoring invalid defined png:color-type",
10874 "=%s",value);
10875
cristy3ed852e2009-09-05 21:47:34 +000010876 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010878 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010879 }
10880
glennrp0e8ea192010-12-24 18:00:33 +000010881 /* Check for chunks to be excluded:
10882 *
glennrp0dff56c2011-01-29 19:10:02 +000010883 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010884 * listed in the "unused_chunks" array, above.
10885 *
10886 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10887 * define (in the image properties or in the image artifacts)
10888 * or via a mng_info member. For convenience, in addition
10889 * to or instead of a comma-separated list of chunks, the
10890 * "exclude-chunk" string can be simply "all" or "none".
10891 *
10892 * The exclude-chunk define takes priority over the mng_info.
10893 *
10894 * A "PNG:include-chunk" define takes priority over both the
10895 * mng_info and the "PNG:exclude-chunk" define. Like the
10896 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010897 * well as a comma-separated list. Chunks that are unknown to
10898 * ImageMagick are always excluded, regardless of their "copy-safe"
10899 * status according to the PNG specification, and even if they
10900 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010901 *
10902 * Finally, all chunks listed in the "unused_chunks" array are
10903 * automatically excluded, regardless of the other instructions
10904 * or lack thereof.
10905 *
10906 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10907 * will not be written and the gAMA chunk will only be written if it
10908 * is not between .45 and .46, or approximately (1.0/2.2).
10909 *
10910 * If you exclude tRNS and the image has transparency, the colortype
10911 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10912 *
10913 * The -strip option causes StripImage() to set the png:include-chunk
10914 * artifact to "none,gama".
10915 */
10916
glennrp26f37912010-12-23 16:22:42 +000010917 mng_info->ping_exclude_bKGD=MagickFalse;
10918 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010919 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010920 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10921 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010922 mng_info->ping_exclude_iCCP=MagickFalse;
10923 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10924 mng_info->ping_exclude_oFFs=MagickFalse;
10925 mng_info->ping_exclude_pHYs=MagickFalse;
10926 mng_info->ping_exclude_sRGB=MagickFalse;
10927 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010928 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010929 mng_info->ping_exclude_vpAg=MagickFalse;
10930 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10931 mng_info->ping_exclude_zTXt=MagickFalse;
10932
glennrp8d3d6e52011-04-19 04:39:51 +000010933 mng_info->ping_preserve_colormap=MagickFalse;
10934
10935 value=GetImageArtifact(image,"png:preserve-colormap");
10936 if (value == NULL)
10937 value=GetImageOption(image_info,"png:preserve-colormap");
10938 if (value != NULL)
10939 mng_info->ping_preserve_colormap=MagickTrue;
10940
glennrp03812ae2010-12-24 01:31:34 +000010941 excluding=MagickFalse;
10942
glennrp5c7cf4e2010-12-24 00:30:00 +000010943 for (source=0; source<1; source++)
10944 {
10945 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010946 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010947 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010948
10949 if (value == NULL)
10950 value=GetImageArtifact(image,"png:exclude-chunks");
10951 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010952 else
glennrpacba0042010-12-24 14:27:26 +000010953 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010954 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010955
glennrpacba0042010-12-24 14:27:26 +000010956 if (value == NULL)
10957 value=GetImageOption(image_info,"png:exclude-chunks");
10958 }
10959
glennrp03812ae2010-12-24 01:31:34 +000010960 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010961 {
glennrp03812ae2010-12-24 01:31:34 +000010962
10963 size_t
10964 last;
10965
10966 excluding=MagickTrue;
10967
10968 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010969 {
10970 if (source == 0)
10971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10972 " png:exclude-chunk=%s found in image artifacts.\n", value);
10973 else
10974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10975 " png:exclude-chunk=%s found in image properties.\n", value);
10976 }
glennrp03812ae2010-12-24 01:31:34 +000010977
10978 last=strlen(value);
10979
10980 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010981 {
glennrp03812ae2010-12-24 01:31:34 +000010982
10983 if (LocaleNCompare(value+i,"all",3) == 0)
10984 {
10985 mng_info->ping_exclude_bKGD=MagickTrue;
10986 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010987 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010988 mng_info->ping_exclude_EXIF=MagickTrue;
10989 mng_info->ping_exclude_gAMA=MagickTrue;
10990 mng_info->ping_exclude_iCCP=MagickTrue;
10991 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10992 mng_info->ping_exclude_oFFs=MagickTrue;
10993 mng_info->ping_exclude_pHYs=MagickTrue;
10994 mng_info->ping_exclude_sRGB=MagickTrue;
10995 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010996 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010997 mng_info->ping_exclude_vpAg=MagickTrue;
10998 mng_info->ping_exclude_zCCP=MagickTrue;
10999 mng_info->ping_exclude_zTXt=MagickTrue;
11000 i--;
11001 }
glennrp2cc891a2010-12-24 13:44:32 +000011002
glennrp03812ae2010-12-24 01:31:34 +000011003 if (LocaleNCompare(value+i,"none",4) == 0)
11004 {
11005 mng_info->ping_exclude_bKGD=MagickFalse;
11006 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011007 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011008 mng_info->ping_exclude_EXIF=MagickFalse;
11009 mng_info->ping_exclude_gAMA=MagickFalse;
11010 mng_info->ping_exclude_iCCP=MagickFalse;
11011 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11012 mng_info->ping_exclude_oFFs=MagickFalse;
11013 mng_info->ping_exclude_pHYs=MagickFalse;
11014 mng_info->ping_exclude_sRGB=MagickFalse;
11015 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011016 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011017 mng_info->ping_exclude_vpAg=MagickFalse;
11018 mng_info->ping_exclude_zCCP=MagickFalse;
11019 mng_info->ping_exclude_zTXt=MagickFalse;
11020 }
glennrp2cc891a2010-12-24 13:44:32 +000011021
glennrp03812ae2010-12-24 01:31:34 +000011022 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11023 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011024
glennrp03812ae2010-12-24 01:31:34 +000011025 if (LocaleNCompare(value+i,"chrm",4) == 0)
11026 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011027
glennrpa0ed0092011-04-18 16:36:29 +000011028 if (LocaleNCompare(value+i,"date",4) == 0)
11029 mng_info->ping_exclude_date=MagickTrue;
11030
glennrp03812ae2010-12-24 01:31:34 +000011031 if (LocaleNCompare(value+i,"exif",4) == 0)
11032 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011033
glennrp03812ae2010-12-24 01:31:34 +000011034 if (LocaleNCompare(value+i,"gama",4) == 0)
11035 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011036
glennrp03812ae2010-12-24 01:31:34 +000011037 if (LocaleNCompare(value+i,"iccp",4) == 0)
11038 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011039
glennrp03812ae2010-12-24 01:31:34 +000011040 /*
11041 if (LocaleNCompare(value+i,"itxt",4) == 0)
11042 mng_info->ping_exclude_iTXt=MagickTrue;
11043 */
glennrp2cc891a2010-12-24 13:44:32 +000011044
glennrp03812ae2010-12-24 01:31:34 +000011045 if (LocaleNCompare(value+i,"gama",4) == 0)
11046 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011047
glennrp03812ae2010-12-24 01:31:34 +000011048 if (LocaleNCompare(value+i,"offs",4) == 0)
11049 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011050
glennrp03812ae2010-12-24 01:31:34 +000011051 if (LocaleNCompare(value+i,"phys",4) == 0)
11052 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011053
glennrpa1e3b7b2010-12-24 16:37:33 +000011054 if (LocaleNCompare(value+i,"srgb",4) == 0)
11055 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011056
glennrp03812ae2010-12-24 01:31:34 +000011057 if (LocaleNCompare(value+i,"text",4) == 0)
11058 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011059
glennrpa1e3b7b2010-12-24 16:37:33 +000011060 if (LocaleNCompare(value+i,"trns",4) == 0)
11061 mng_info->ping_exclude_tRNS=MagickTrue;
11062
glennrp03812ae2010-12-24 01:31:34 +000011063 if (LocaleNCompare(value+i,"vpag",4) == 0)
11064 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011065
glennrp03812ae2010-12-24 01:31:34 +000011066 if (LocaleNCompare(value+i,"zccp",4) == 0)
11067 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011068
glennrp03812ae2010-12-24 01:31:34 +000011069 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11070 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011071
glennrp03812ae2010-12-24 01:31:34 +000011072 }
glennrpce91ed52010-12-23 22:37:49 +000011073 }
glennrp26f37912010-12-23 16:22:42 +000011074 }
11075
glennrp5c7cf4e2010-12-24 00:30:00 +000011076 for (source=0; source<1; source++)
11077 {
11078 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011079 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011080 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011081
11082 if (value == NULL)
11083 value=GetImageArtifact(image,"png:include-chunks");
11084 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011085 else
glennrpacba0042010-12-24 14:27:26 +000011086 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011087 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011088
glennrpacba0042010-12-24 14:27:26 +000011089 if (value == NULL)
11090 value=GetImageOption(image_info,"png:include-chunks");
11091 }
11092
glennrp03812ae2010-12-24 01:31:34 +000011093 if (value != NULL)
11094 {
11095 size_t
11096 last;
glennrp26f37912010-12-23 16:22:42 +000011097
glennrp03812ae2010-12-24 01:31:34 +000011098 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011099
glennrp03812ae2010-12-24 01:31:34 +000011100 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011101 {
11102 if (source == 0)
11103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11104 " png:include-chunk=%s found in image artifacts.\n", value);
11105 else
11106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11107 " png:include-chunk=%s found in image properties.\n", value);
11108 }
glennrp03812ae2010-12-24 01:31:34 +000011109
11110 last=strlen(value);
11111
11112 for (i=0; i<(int) last; i+=5)
11113 {
11114 if (LocaleNCompare(value+i,"all",3) == 0)
11115 {
11116 mng_info->ping_exclude_bKGD=MagickFalse;
11117 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011118 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011119 mng_info->ping_exclude_EXIF=MagickFalse;
11120 mng_info->ping_exclude_gAMA=MagickFalse;
11121 mng_info->ping_exclude_iCCP=MagickFalse;
11122 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11123 mng_info->ping_exclude_oFFs=MagickFalse;
11124 mng_info->ping_exclude_pHYs=MagickFalse;
11125 mng_info->ping_exclude_sRGB=MagickFalse;
11126 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011127 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011128 mng_info->ping_exclude_vpAg=MagickFalse;
11129 mng_info->ping_exclude_zCCP=MagickFalse;
11130 mng_info->ping_exclude_zTXt=MagickFalse;
11131 i--;
11132 }
glennrp2cc891a2010-12-24 13:44:32 +000011133
glennrp03812ae2010-12-24 01:31:34 +000011134 if (LocaleNCompare(value+i,"none",4) == 0)
11135 {
11136 mng_info->ping_exclude_bKGD=MagickTrue;
11137 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011138 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011139 mng_info->ping_exclude_EXIF=MagickTrue;
11140 mng_info->ping_exclude_gAMA=MagickTrue;
11141 mng_info->ping_exclude_iCCP=MagickTrue;
11142 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11143 mng_info->ping_exclude_oFFs=MagickTrue;
11144 mng_info->ping_exclude_pHYs=MagickTrue;
11145 mng_info->ping_exclude_sRGB=MagickTrue;
11146 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011147 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011148 mng_info->ping_exclude_vpAg=MagickTrue;
11149 mng_info->ping_exclude_zCCP=MagickTrue;
11150 mng_info->ping_exclude_zTXt=MagickTrue;
11151 }
glennrp2cc891a2010-12-24 13:44:32 +000011152
glennrp03812ae2010-12-24 01:31:34 +000011153 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11154 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011155
glennrp03812ae2010-12-24 01:31:34 +000011156 if (LocaleNCompare(value+i,"chrm",4) == 0)
11157 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011158
glennrpa0ed0092011-04-18 16:36:29 +000011159 if (LocaleNCompare(value+i,"date",4) == 0)
11160 mng_info->ping_exclude_date=MagickFalse;
11161
glennrp03812ae2010-12-24 01:31:34 +000011162 if (LocaleNCompare(value+i,"exif",4) == 0)
11163 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011164
glennrp03812ae2010-12-24 01:31:34 +000011165 if (LocaleNCompare(value+i,"gama",4) == 0)
11166 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011167
glennrp03812ae2010-12-24 01:31:34 +000011168 if (LocaleNCompare(value+i,"iccp",4) == 0)
11169 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011170
glennrp03812ae2010-12-24 01:31:34 +000011171 /*
11172 if (LocaleNCompare(value+i,"itxt",4) == 0)
11173 mng_info->ping_exclude_iTXt=MagickFalse;
11174 */
glennrp2cc891a2010-12-24 13:44:32 +000011175
glennrp03812ae2010-12-24 01:31:34 +000011176 if (LocaleNCompare(value+i,"gama",4) == 0)
11177 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011178
glennrp03812ae2010-12-24 01:31:34 +000011179 if (LocaleNCompare(value+i,"offs",4) == 0)
11180 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011181
glennrp03812ae2010-12-24 01:31:34 +000011182 if (LocaleNCompare(value+i,"phys",4) == 0)
11183 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011184
glennrpa1e3b7b2010-12-24 16:37:33 +000011185 if (LocaleNCompare(value+i,"srgb",4) == 0)
11186 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011187
glennrp03812ae2010-12-24 01:31:34 +000011188 if (LocaleNCompare(value+i,"text",4) == 0)
11189 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011190
glennrpa1e3b7b2010-12-24 16:37:33 +000011191 if (LocaleNCompare(value+i,"trns",4) == 0)
11192 mng_info->ping_exclude_tRNS=MagickFalse;
11193
glennrp03812ae2010-12-24 01:31:34 +000011194 if (LocaleNCompare(value+i,"vpag",4) == 0)
11195 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011196
glennrp03812ae2010-12-24 01:31:34 +000011197 if (LocaleNCompare(value+i,"zccp",4) == 0)
11198 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011199
glennrp03812ae2010-12-24 01:31:34 +000011200 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11201 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011202
glennrp03812ae2010-12-24 01:31:34 +000011203 }
glennrpce91ed52010-12-23 22:37:49 +000011204 }
glennrp26f37912010-12-23 16:22:42 +000011205 }
11206
glennrp03812ae2010-12-24 01:31:34 +000011207 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011208 {
11209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11210 " Chunks to be excluded from the output PNG:");
11211 if (mng_info->ping_exclude_bKGD != MagickFalse)
11212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11213 " bKGD");
11214 if (mng_info->ping_exclude_cHRM != MagickFalse)
11215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11216 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011217 if (mng_info->ping_exclude_date != MagickFalse)
11218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11219 " date");
glennrp26f37912010-12-23 16:22:42 +000011220 if (mng_info->ping_exclude_EXIF != MagickFalse)
11221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11222 " EXIF");
11223 if (mng_info->ping_exclude_gAMA != MagickFalse)
11224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11225 " gAMA");
11226 if (mng_info->ping_exclude_iCCP != MagickFalse)
11227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11228 " iCCP");
11229/*
11230 if (mng_info->ping_exclude_iTXt != MagickFalse)
11231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11232 " iTXt");
11233*/
11234 if (mng_info->ping_exclude_oFFs != MagickFalse)
11235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11236 " oFFs");
11237 if (mng_info->ping_exclude_pHYs != MagickFalse)
11238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11239 " pHYs");
11240 if (mng_info->ping_exclude_sRGB != MagickFalse)
11241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11242 " sRGB");
11243 if (mng_info->ping_exclude_tEXt != MagickFalse)
11244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11245 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011246 if (mng_info->ping_exclude_tRNS != MagickFalse)
11247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11248 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011249 if (mng_info->ping_exclude_vpAg != MagickFalse)
11250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11251 " vpAg");
11252 if (mng_info->ping_exclude_zCCP != MagickFalse)
11253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11254 " zCCP");
11255 if (mng_info->ping_exclude_zTXt != MagickFalse)
11256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11257 " zTXt");
11258 }
11259
glennrpb9cfe272010-12-21 15:08:06 +000011260 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011261
glennrpb9cfe272010-12-21 15:08:06 +000011262 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000011263
11264 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011265
cristy3ed852e2009-09-05 21:47:34 +000011266 if (logging != MagickFalse)
11267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011268
cristy3ed852e2009-09-05 21:47:34 +000011269 return(status);
11270}
11271
11272#if defined(JNG_SUPPORTED)
11273
11274/* Write one JNG image */
11275static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11276 const ImageInfo *image_info,Image *image)
11277{
11278 Image
11279 *jpeg_image;
11280
11281 ImageInfo
11282 *jpeg_image_info;
11283
11284 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011285 logging,
cristy3ed852e2009-09-05 21:47:34 +000011286 status;
11287
11288 size_t
11289 length;
11290
11291 unsigned char
11292 *blob,
11293 chunk[80],
11294 *p;
11295
11296 unsigned int
11297 jng_alpha_compression_method,
11298 jng_alpha_sample_depth,
11299 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011300 transparent;
11301
cristybb503372010-05-27 20:51:26 +000011302 size_t
cristy3ed852e2009-09-05 21:47:34 +000011303 jng_quality;
11304
11305 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011306 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011307
11308 blob=(unsigned char *) NULL;
11309 jpeg_image=(Image *) NULL;
11310 jpeg_image_info=(ImageInfo *) NULL;
11311
11312 status=MagickTrue;
11313 transparent=image_info->type==GrayscaleMatteType ||
11314 image_info->type==TrueColorMatteType;
11315 jng_color_type=10;
11316 jng_alpha_sample_depth=0;
11317 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11318 jng_alpha_compression_method=0;
11319
11320 if (image->matte != MagickFalse)
11321 {
11322 /* if any pixels are transparent */
11323 transparent=MagickTrue;
11324 if (image_info->compression==JPEGCompression)
11325 jng_alpha_compression_method=8;
11326 }
11327
11328 if (transparent)
11329 {
11330 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011331
cristy3ed852e2009-09-05 21:47:34 +000011332 /* Create JPEG blob, image, and image_info */
11333 if (logging != MagickFalse)
11334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11335 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000011336
cristy3ed852e2009-09-05 21:47:34 +000011337 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011338
cristy3ed852e2009-09-05 21:47:34 +000011339 if (jpeg_image_info == (ImageInfo *) NULL)
11340 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011341
cristy3ed852e2009-09-05 21:47:34 +000011342 if (logging != MagickFalse)
11343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11344 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011345
cristy3ed852e2009-09-05 21:47:34 +000011346 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011347
cristy3ed852e2009-09-05 21:47:34 +000011348 if (jpeg_image == (Image *) NULL)
11349 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011350
cristy3ed852e2009-09-05 21:47:34 +000011351 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11352 status=SeparateImageChannel(jpeg_image,OpacityChannel);
11353 status=NegateImage(jpeg_image,MagickFalse);
11354 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011355
cristy3ed852e2009-09-05 21:47:34 +000011356 if (jng_quality >= 1000)
11357 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011358
cristy3ed852e2009-09-05 21:47:34 +000011359 else
11360 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011361
cristy3ed852e2009-09-05 21:47:34 +000011362 jpeg_image_info->type=GrayscaleType;
11363 (void) SetImageType(jpeg_image,GrayscaleType);
11364 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011365 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011366 "%s",jpeg_image->filename);
11367 }
11368
11369 /* To do: check bit depth of PNG alpha channel */
11370
11371 /* Check if image is grayscale. */
11372 if (image_info->type != TrueColorMatteType && image_info->type !=
11373 TrueColorType && ImageIsGray(image))
11374 jng_color_type-=2;
11375
11376 if (transparent)
11377 {
11378 if (jng_alpha_compression_method==0)
11379 {
11380 const char
11381 *value;
11382
11383 /* Encode opacity as a grayscale PNG blob */
11384 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11385 &image->exception);
11386 if (logging != MagickFalse)
11387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11388 " Creating PNG blob.");
11389 length=0;
11390
11391 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11392 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11393 jpeg_image_info->interlace=NoInterlace;
11394
11395 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11396 &image->exception);
11397
11398 /* Retrieve sample depth used */
11399 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11400 if (value != (char *) NULL)
11401 jng_alpha_sample_depth= (unsigned int) value[0];
11402 }
11403 else
11404 {
11405 /* Encode opacity as a grayscale JPEG blob */
11406
11407 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11408 &image->exception);
11409
11410 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11411 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11412 jpeg_image_info->interlace=NoInterlace;
11413 if (logging != MagickFalse)
11414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11415 " Creating blob.");
11416 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000011417 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000011418 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011419
cristy3ed852e2009-09-05 21:47:34 +000011420 if (logging != MagickFalse)
11421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011422 " Successfully read jpeg_image into a blob, length=%.20g.",
11423 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011424
11425 }
11426 /* Destroy JPEG image and image_info */
11427 jpeg_image=DestroyImage(jpeg_image);
11428 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11429 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11430 }
11431
11432 /* Write JHDR chunk */
11433 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11434 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011435 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011436 PNGLong(chunk+4,(png_uint_32) image->columns);
11437 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011438 chunk[12]=jng_color_type;
11439 chunk[13]=8; /* sample depth */
11440 chunk[14]=8; /*jng_image_compression_method */
11441 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11442 chunk[16]=jng_alpha_sample_depth;
11443 chunk[17]=jng_alpha_compression_method;
11444 chunk[18]=0; /*jng_alpha_filter_method */
11445 chunk[19]=0; /*jng_alpha_interlace_method */
11446 (void) WriteBlob(image,20,chunk);
11447 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11448 if (logging != MagickFalse)
11449 {
11450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011451 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011452
cristy3ed852e2009-09-05 21:47:34 +000011453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011454 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011455
cristy3ed852e2009-09-05 21:47:34 +000011456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11457 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011458
cristy3ed852e2009-09-05 21:47:34 +000011459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11460 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011461
cristy3ed852e2009-09-05 21:47:34 +000011462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11463 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011464
cristy3ed852e2009-09-05 21:47:34 +000011465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11466 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011467
cristy3ed852e2009-09-05 21:47:34 +000011468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11469 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011470
cristy3ed852e2009-09-05 21:47:34 +000011471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11472 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011473
cristy3ed852e2009-09-05 21:47:34 +000011474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11475 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011476
cristy3ed852e2009-09-05 21:47:34 +000011477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11478 " JNG alpha interlace:%5d",0);
11479 }
11480
glennrp0fe50b42010-11-16 03:52:51 +000011481 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011482 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011483
11484 /*
11485 Write leading ancillary chunks
11486 */
11487
11488 if (transparent)
11489 {
11490 /*
11491 Write JNG bKGD chunk
11492 */
11493
11494 unsigned char
11495 blue,
11496 green,
11497 red;
11498
cristybb503372010-05-27 20:51:26 +000011499 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011500 num_bytes;
11501
11502 if (jng_color_type == 8 || jng_color_type == 12)
11503 num_bytes=6L;
11504 else
11505 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011506 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011507 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011508 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011509 red=ScaleQuantumToChar(image->background_color.red);
11510 green=ScaleQuantumToChar(image->background_color.green);
11511 blue=ScaleQuantumToChar(image->background_color.blue);
11512 *(chunk+4)=0;
11513 *(chunk+5)=red;
11514 *(chunk+6)=0;
11515 *(chunk+7)=green;
11516 *(chunk+8)=0;
11517 *(chunk+9)=blue;
11518 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11519 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11520 }
11521
11522 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11523 {
11524 /*
11525 Write JNG sRGB chunk
11526 */
11527 (void) WriteBlobMSBULong(image,1L);
11528 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011529 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011530
cristy3ed852e2009-09-05 21:47:34 +000011531 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011532 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011533 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011534 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011535
cristy3ed852e2009-09-05 21:47:34 +000011536 else
glennrpe610a072010-08-05 17:08:46 +000011537 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011538 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011539 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011540
cristy3ed852e2009-09-05 21:47:34 +000011541 (void) WriteBlob(image,5,chunk);
11542 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11543 }
11544 else
11545 {
11546 if (image->gamma != 0.0)
11547 {
11548 /*
11549 Write JNG gAMA chunk
11550 */
11551 (void) WriteBlobMSBULong(image,4L);
11552 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011553 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011554 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011555 (void) WriteBlob(image,8,chunk);
11556 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11557 }
glennrp0fe50b42010-11-16 03:52:51 +000011558
cristy3ed852e2009-09-05 21:47:34 +000011559 if ((mng_info->equal_chrms == MagickFalse) &&
11560 (image->chromaticity.red_primary.x != 0.0))
11561 {
11562 PrimaryInfo
11563 primary;
11564
11565 /*
11566 Write JNG cHRM chunk
11567 */
11568 (void) WriteBlobMSBULong(image,32L);
11569 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011570 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011571 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011572 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11573 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011574 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011575 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11576 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011577 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011578 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11579 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011580 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011581 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11582 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011583 (void) WriteBlob(image,36,chunk);
11584 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11585 }
11586 }
glennrp0fe50b42010-11-16 03:52:51 +000011587
cristy3ed852e2009-09-05 21:47:34 +000011588 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11589 {
11590 /*
11591 Write JNG pHYs chunk
11592 */
11593 (void) WriteBlobMSBULong(image,9L);
11594 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011595 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011596 if (image->units == PixelsPerInchResolution)
11597 {
cristy35ef8242010-06-03 16:24:13 +000011598 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011599 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011600
cristy35ef8242010-06-03 16:24:13 +000011601 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011602 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011603
cristy3ed852e2009-09-05 21:47:34 +000011604 chunk[12]=1;
11605 }
glennrp0fe50b42010-11-16 03:52:51 +000011606
cristy3ed852e2009-09-05 21:47:34 +000011607 else
11608 {
11609 if (image->units == PixelsPerCentimeterResolution)
11610 {
cristy35ef8242010-06-03 16:24:13 +000011611 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011612 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011613
cristy35ef8242010-06-03 16:24:13 +000011614 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011615 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011616
cristy3ed852e2009-09-05 21:47:34 +000011617 chunk[12]=1;
11618 }
glennrp0fe50b42010-11-16 03:52:51 +000011619
cristy3ed852e2009-09-05 21:47:34 +000011620 else
11621 {
cristy35ef8242010-06-03 16:24:13 +000011622 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11623 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011624 chunk[12]=0;
11625 }
11626 }
11627 (void) WriteBlob(image,13,chunk);
11628 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11629 }
11630
11631 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11632 {
11633 /*
11634 Write JNG oFFs chunk
11635 */
11636 (void) WriteBlobMSBULong(image,9L);
11637 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011638 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011639 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11640 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011641 chunk[12]=0;
11642 (void) WriteBlob(image,13,chunk);
11643 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11644 }
11645 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11646 {
11647 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11648 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011649 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011650 PNGLong(chunk+4,(png_uint_32) image->page.width);
11651 PNGLong(chunk+8,(png_uint_32) image->page.height);
11652 chunk[12]=0; /* unit = pixels */
11653 (void) WriteBlob(image,13,chunk);
11654 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11655 }
11656
11657
11658 if (transparent)
11659 {
11660 if (jng_alpha_compression_method==0)
11661 {
cristybb503372010-05-27 20:51:26 +000011662 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011663 i;
11664
cristybb503372010-05-27 20:51:26 +000011665 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011666 len;
11667
11668 /* Write IDAT chunk header */
11669 if (logging != MagickFalse)
11670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011671 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011672 length);
cristy3ed852e2009-09-05 21:47:34 +000011673
11674 /* Copy IDAT chunks */
11675 len=0;
11676 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011677 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011678 {
11679 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11680 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011681
cristy3ed852e2009-09-05 21:47:34 +000011682 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11683 {
11684 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011685 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011686 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011687 (void) WriteBlob(image,(size_t) len+4,p);
11688 (void) WriteBlobMSBULong(image,
11689 crc32(0,p,(uInt) len+4));
11690 }
glennrp0fe50b42010-11-16 03:52:51 +000011691
cristy3ed852e2009-09-05 21:47:34 +000011692 else
11693 {
11694 if (logging != MagickFalse)
11695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011696 " Skipping %c%c%c%c chunk, length=%.20g.",
11697 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011698 }
11699 p+=(8+len);
11700 }
11701 }
11702 else
11703 {
11704 /* Write JDAA chunk header */
11705 if (logging != MagickFalse)
11706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011707 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011708 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011709 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011710 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011711 /* Write JDAT chunk(s) data */
11712 (void) WriteBlob(image,4,chunk);
11713 (void) WriteBlob(image,length,blob);
11714 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11715 (uInt) length));
11716 }
11717 blob=(unsigned char *) RelinquishMagickMemory(blob);
11718 }
11719
11720 /* Encode image as a JPEG blob */
11721 if (logging != MagickFalse)
11722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11723 " Creating jpeg_image_info.");
11724 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11725 if (jpeg_image_info == (ImageInfo *) NULL)
11726 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11727
11728 if (logging != MagickFalse)
11729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11730 " Creating jpeg_image.");
11731
11732 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11733 if (jpeg_image == (Image *) NULL)
11734 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11735 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11736
11737 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011738 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000011739 jpeg_image->filename);
11740
11741 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11742 &image->exception);
11743
11744 if (logging != MagickFalse)
11745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011746 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11747 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011748
11749 if (jng_color_type == 8 || jng_color_type == 12)
11750 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011751
cristy3ed852e2009-09-05 21:47:34 +000011752 jpeg_image_info->quality=jng_quality % 1000;
11753 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11754 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011755
cristy3ed852e2009-09-05 21:47:34 +000011756 if (logging != MagickFalse)
11757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11758 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011759
cristy3ed852e2009-09-05 21:47:34 +000011760 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011761
cristy3ed852e2009-09-05 21:47:34 +000011762 if (logging != MagickFalse)
11763 {
11764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011765 " Successfully read jpeg_image into a blob, length=%.20g.",
11766 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011767
11768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011769 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011770 }
glennrp0fe50b42010-11-16 03:52:51 +000011771
cristy3ed852e2009-09-05 21:47:34 +000011772 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011773 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011774 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011775 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011776 (void) WriteBlob(image,4,chunk);
11777 (void) WriteBlob(image,length,blob);
11778 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11779
11780 jpeg_image=DestroyImage(jpeg_image);
11781 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11782 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11783 blob=(unsigned char *) RelinquishMagickMemory(blob);
11784
11785 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011786 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011787
11788 /* Write IEND chunk */
11789 (void) WriteBlobMSBULong(image,0L);
11790 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011791 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011792 (void) WriteBlob(image,4,chunk);
11793 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11794
11795 if (logging != MagickFalse)
11796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11797 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011798
cristy3ed852e2009-09-05 21:47:34 +000011799 return(status);
11800}
11801
11802
11803/*
11804%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11805% %
11806% %
11807% %
11808% W r i t e J N G I m a g e %
11809% %
11810% %
11811% %
11812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11813%
11814% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11815%
11816% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11817%
11818% The format of the WriteJNGImage method is:
11819%
11820% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11821%
11822% A description of each parameter follows:
11823%
11824% o image_info: the image info.
11825%
11826% o image: The image.
11827%
11828%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11829*/
11830static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11831{
11832 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011833 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011834 logging,
cristy3ed852e2009-09-05 21:47:34 +000011835 status;
11836
11837 MngInfo
11838 *mng_info;
11839
cristy3ed852e2009-09-05 21:47:34 +000011840 /*
11841 Open image file.
11842 */
11843 assert(image_info != (const ImageInfo *) NULL);
11844 assert(image_info->signature == MagickSignature);
11845 assert(image != (Image *) NULL);
11846 assert(image->signature == MagickSignature);
11847 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011848 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011849 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11850 if (status == MagickFalse)
11851 return(status);
11852
11853 /*
11854 Allocate a MngInfo structure.
11855 */
11856 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011857 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011858 if (mng_info == (MngInfo *) NULL)
11859 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11860 /*
11861 Initialize members of the MngInfo structure.
11862 */
11863 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11864 mng_info->image=image;
11865 have_mng_structure=MagickTrue;
11866
11867 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11868
11869 status=WriteOneJNGImage(mng_info,image_info,image);
11870 (void) CloseBlob(image);
11871
11872 (void) CatchImageException(image);
11873 MngInfoFreeStruct(mng_info,&have_mng_structure);
11874 if (logging != MagickFalse)
11875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11876 return(status);
11877}
11878#endif
11879
11880
11881
11882static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11883{
11884 const char
11885 *option;
11886
11887 Image
11888 *next_image;
11889
11890 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011891 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011892 status;
11893
glennrp03812ae2010-12-24 01:31:34 +000011894 volatile MagickBooleanType
11895 logging;
11896
cristy3ed852e2009-09-05 21:47:34 +000011897 MngInfo
11898 *mng_info;
11899
11900 int
cristy3ed852e2009-09-05 21:47:34 +000011901 image_count,
11902 need_iterations,
11903 need_matte;
11904
11905 volatile int
11906#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11907 defined(PNG_MNG_FEATURES_SUPPORTED)
11908 need_local_plte,
11909#endif
11910 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011911 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011912 use_global_plte;
11913
cristybb503372010-05-27 20:51:26 +000011914 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011915 i;
11916
11917 unsigned char
11918 chunk[800];
11919
11920 volatile unsigned int
11921 write_jng,
11922 write_mng;
11923
cristybb503372010-05-27 20:51:26 +000011924 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011925 scene;
11926
cristybb503372010-05-27 20:51:26 +000011927 size_t
cristy3ed852e2009-09-05 21:47:34 +000011928 final_delay=0,
11929 initial_delay;
11930
glennrpd5045b42010-03-24 12:40:35 +000011931#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011932 if (image_info->verbose)
11933 printf("Your PNG library (libpng-%s) is rather old.\n",
11934 PNG_LIBPNG_VER_STRING);
11935#endif
11936
11937 /*
11938 Open image file.
11939 */
11940 assert(image_info != (const ImageInfo *) NULL);
11941 assert(image_info->signature == MagickSignature);
11942 assert(image != (Image *) NULL);
11943 assert(image->signature == MagickSignature);
11944 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011945 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011946 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11947 if (status == MagickFalse)
11948 return(status);
11949
11950 /*
11951 Allocate a MngInfo structure.
11952 */
11953 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011954 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011955 if (mng_info == (MngInfo *) NULL)
11956 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11957 /*
11958 Initialize members of the MngInfo structure.
11959 */
11960 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11961 mng_info->image=image;
11962 have_mng_structure=MagickTrue;
11963 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11964
11965 /*
11966 * See if user has requested a specific PNG subformat to be used
11967 * for all of the PNGs in the MNG being written, e.g.,
11968 *
11969 * convert *.png png8:animation.mng
11970 *
11971 * To do: check -define png:bit_depth and png:color_type as well,
11972 * or perhaps use mng:bit_depth and mng:color_type instead for
11973 * global settings.
11974 */
11975
11976 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11977 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11978 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11979
11980 write_jng=MagickFalse;
11981 if (image_info->compression == JPEGCompression)
11982 write_jng=MagickTrue;
11983
11984 mng_info->adjoin=image_info->adjoin &&
11985 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11986
cristy3ed852e2009-09-05 21:47:34 +000011987 if (logging != MagickFalse)
11988 {
11989 /* Log some info about the input */
11990 Image
11991 *p;
11992
11993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11994 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011995
cristy3ed852e2009-09-05 21:47:34 +000011996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011997 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011998
cristy3ed852e2009-09-05 21:47:34 +000011999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12000 " Type: %d",image_info->type);
12001
12002 scene=0;
12003 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12004 {
12005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012006 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012007
cristy3ed852e2009-09-05 21:47:34 +000012008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012009 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012010
cristy3ed852e2009-09-05 21:47:34 +000012011 if (p->matte)
12012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12013 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012014
cristy3ed852e2009-09-05 21:47:34 +000012015 else
12016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12017 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012018
cristy3ed852e2009-09-05 21:47:34 +000012019 if (p->storage_class == PseudoClass)
12020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12021 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012022
cristy3ed852e2009-09-05 21:47:34 +000012023 else
12024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12025 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012026
cristy3ed852e2009-09-05 21:47:34 +000012027 if (p->colors)
12028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012029 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012030
cristy3ed852e2009-09-05 21:47:34 +000012031 else
12032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12033 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012034
cristy3ed852e2009-09-05 21:47:34 +000012035 if (mng_info->adjoin == MagickFalse)
12036 break;
12037 }
12038 }
12039
cristy3ed852e2009-09-05 21:47:34 +000012040 use_global_plte=MagickFalse;
12041 all_images_are_gray=MagickFalse;
12042#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12043 need_local_plte=MagickTrue;
12044#endif
12045 need_defi=MagickFalse;
12046 need_matte=MagickFalse;
12047 mng_info->framing_mode=1;
12048 mng_info->old_framing_mode=1;
12049
12050 if (write_mng)
12051 if (image_info->page != (char *) NULL)
12052 {
12053 /*
12054 Determine image bounding box.
12055 */
12056 SetGeometry(image,&mng_info->page);
12057 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12058 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12059 }
12060 if (write_mng)
12061 {
12062 unsigned int
12063 need_geom;
12064
12065 unsigned short
12066 red,
12067 green,
12068 blue;
12069
12070 mng_info->page=image->page;
12071 need_geom=MagickTrue;
12072 if (mng_info->page.width || mng_info->page.height)
12073 need_geom=MagickFalse;
12074 /*
12075 Check all the scenes.
12076 */
12077 initial_delay=image->delay;
12078 need_iterations=MagickFalse;
12079 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12080 mng_info->equal_physs=MagickTrue,
12081 mng_info->equal_gammas=MagickTrue;
12082 mng_info->equal_srgbs=MagickTrue;
12083 mng_info->equal_backgrounds=MagickTrue;
12084 image_count=0;
12085#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12086 defined(PNG_MNG_FEATURES_SUPPORTED)
12087 all_images_are_gray=MagickTrue;
12088 mng_info->equal_palettes=MagickFalse;
12089 need_local_plte=MagickFalse;
12090#endif
12091 for (next_image=image; next_image != (Image *) NULL; )
12092 {
12093 if (need_geom)
12094 {
12095 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12096 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012097
cristy3ed852e2009-09-05 21:47:34 +000012098 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12099 mng_info->page.height=next_image->rows+next_image->page.y;
12100 }
glennrp0fe50b42010-11-16 03:52:51 +000012101
cristy3ed852e2009-09-05 21:47:34 +000012102 if (next_image->page.x || next_image->page.y)
12103 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012104
cristy3ed852e2009-09-05 21:47:34 +000012105 if (next_image->matte)
12106 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012107
cristy3ed852e2009-09-05 21:47:34 +000012108 if ((int) next_image->dispose >= BackgroundDispose)
12109 if (next_image->matte || next_image->page.x || next_image->page.y ||
12110 ((next_image->columns < mng_info->page.width) &&
12111 (next_image->rows < mng_info->page.height)))
12112 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012113
cristy3ed852e2009-09-05 21:47:34 +000012114 if (next_image->iterations)
12115 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012116
cristy3ed852e2009-09-05 21:47:34 +000012117 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012118
cristy3ed852e2009-09-05 21:47:34 +000012119 if (final_delay != initial_delay || final_delay > 1UL*
12120 next_image->ticks_per_second)
12121 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012122
cristy3ed852e2009-09-05 21:47:34 +000012123#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12124 defined(PNG_MNG_FEATURES_SUPPORTED)
12125 /*
12126 check for global palette possibility.
12127 */
12128 if (image->matte != MagickFalse)
12129 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012130
cristy3ed852e2009-09-05 21:47:34 +000012131 if (need_local_plte == 0)
12132 {
12133 if (ImageIsGray(image) == MagickFalse)
12134 all_images_are_gray=MagickFalse;
12135 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12136 if (use_global_plte == 0)
12137 use_global_plte=mng_info->equal_palettes;
12138 need_local_plte=!mng_info->equal_palettes;
12139 }
12140#endif
12141 if (GetNextImageInList(next_image) != (Image *) NULL)
12142 {
12143 if (next_image->background_color.red !=
12144 next_image->next->background_color.red ||
12145 next_image->background_color.green !=
12146 next_image->next->background_color.green ||
12147 next_image->background_color.blue !=
12148 next_image->next->background_color.blue)
12149 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012150
cristy3ed852e2009-09-05 21:47:34 +000012151 if (next_image->gamma != next_image->next->gamma)
12152 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012153
cristy3ed852e2009-09-05 21:47:34 +000012154 if (next_image->rendering_intent !=
12155 next_image->next->rendering_intent)
12156 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012157
cristy3ed852e2009-09-05 21:47:34 +000012158 if ((next_image->units != next_image->next->units) ||
12159 (next_image->x_resolution != next_image->next->x_resolution) ||
12160 (next_image->y_resolution != next_image->next->y_resolution))
12161 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012162
cristy3ed852e2009-09-05 21:47:34 +000012163 if (mng_info->equal_chrms)
12164 {
12165 if (next_image->chromaticity.red_primary.x !=
12166 next_image->next->chromaticity.red_primary.x ||
12167 next_image->chromaticity.red_primary.y !=
12168 next_image->next->chromaticity.red_primary.y ||
12169 next_image->chromaticity.green_primary.x !=
12170 next_image->next->chromaticity.green_primary.x ||
12171 next_image->chromaticity.green_primary.y !=
12172 next_image->next->chromaticity.green_primary.y ||
12173 next_image->chromaticity.blue_primary.x !=
12174 next_image->next->chromaticity.blue_primary.x ||
12175 next_image->chromaticity.blue_primary.y !=
12176 next_image->next->chromaticity.blue_primary.y ||
12177 next_image->chromaticity.white_point.x !=
12178 next_image->next->chromaticity.white_point.x ||
12179 next_image->chromaticity.white_point.y !=
12180 next_image->next->chromaticity.white_point.y)
12181 mng_info->equal_chrms=MagickFalse;
12182 }
12183 }
12184 image_count++;
12185 next_image=GetNextImageInList(next_image);
12186 }
12187 if (image_count < 2)
12188 {
12189 mng_info->equal_backgrounds=MagickFalse;
12190 mng_info->equal_chrms=MagickFalse;
12191 mng_info->equal_gammas=MagickFalse;
12192 mng_info->equal_srgbs=MagickFalse;
12193 mng_info->equal_physs=MagickFalse;
12194 use_global_plte=MagickFalse;
12195#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12196 need_local_plte=MagickTrue;
12197#endif
12198 need_iterations=MagickFalse;
12199 }
glennrp0fe50b42010-11-16 03:52:51 +000012200
cristy3ed852e2009-09-05 21:47:34 +000012201 if (mng_info->need_fram == MagickFalse)
12202 {
12203 /*
12204 Only certain framing rates 100/n are exactly representable without
12205 the FRAM chunk but we'll allow some slop in VLC files
12206 */
12207 if (final_delay == 0)
12208 {
12209 if (need_iterations != MagickFalse)
12210 {
12211 /*
12212 It's probably a GIF with loop; don't run it *too* fast.
12213 */
glennrp02617122010-07-28 13:07:35 +000012214 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012215 {
12216 final_delay=10;
12217 (void) ThrowMagickException(&image->exception,
12218 GetMagickModule(),CoderWarning,
12219 "input has zero delay between all frames; assuming",
12220 " 10 cs `%s'","");
12221 }
cristy3ed852e2009-09-05 21:47:34 +000012222 }
12223 else
12224 mng_info->ticks_per_second=0;
12225 }
12226 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012227 mng_info->ticks_per_second=(png_uint_32)
12228 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012229 if (final_delay > 50)
12230 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012231
cristy3ed852e2009-09-05 21:47:34 +000012232 if (final_delay > 75)
12233 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012234
cristy3ed852e2009-09-05 21:47:34 +000012235 if (final_delay > 125)
12236 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012237
cristy3ed852e2009-09-05 21:47:34 +000012238 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12239 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12240 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12241 1UL*image->ticks_per_second))
12242 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12243 }
glennrp0fe50b42010-11-16 03:52:51 +000012244
cristy3ed852e2009-09-05 21:47:34 +000012245 if (mng_info->need_fram != MagickFalse)
12246 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12247 /*
12248 If pseudocolor, we should also check to see if all the
12249 palettes are identical and write a global PLTE if they are.
12250 ../glennrp Feb 99.
12251 */
12252 /*
12253 Write the MNG version 1.0 signature and MHDR chunk.
12254 */
12255 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12256 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12257 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012258 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012259 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12260 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012261 PNGLong(chunk+12,mng_info->ticks_per_second);
12262 PNGLong(chunk+16,0L); /* layer count=unknown */
12263 PNGLong(chunk+20,0L); /* frame count=unknown */
12264 PNGLong(chunk+24,0L); /* play time=unknown */
12265 if (write_jng)
12266 {
12267 if (need_matte)
12268 {
12269 if (need_defi || mng_info->need_fram || use_global_plte)
12270 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012271
cristy3ed852e2009-09-05 21:47:34 +000012272 else
12273 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12274 }
glennrp0fe50b42010-11-16 03:52:51 +000012275
cristy3ed852e2009-09-05 21:47:34 +000012276 else
12277 {
12278 if (need_defi || mng_info->need_fram || use_global_plte)
12279 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012280
cristy3ed852e2009-09-05 21:47:34 +000012281 else
12282 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12283 }
12284 }
glennrp0fe50b42010-11-16 03:52:51 +000012285
cristy3ed852e2009-09-05 21:47:34 +000012286 else
12287 {
12288 if (need_matte)
12289 {
12290 if (need_defi || mng_info->need_fram || use_global_plte)
12291 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012292
cristy3ed852e2009-09-05 21:47:34 +000012293 else
12294 PNGLong(chunk+28,9L); /* simplicity=VLC */
12295 }
glennrp0fe50b42010-11-16 03:52:51 +000012296
cristy3ed852e2009-09-05 21:47:34 +000012297 else
12298 {
12299 if (need_defi || mng_info->need_fram || use_global_plte)
12300 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012301
cristy3ed852e2009-09-05 21:47:34 +000012302 else
12303 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12304 }
12305 }
12306 (void) WriteBlob(image,32,chunk);
12307 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12308 option=GetImageOption(image_info,"mng:need-cacheoff");
12309 if (option != (const char *) NULL)
12310 {
12311 size_t
12312 length;
12313
12314 /*
12315 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12316 */
12317 PNGType(chunk,mng_nEED);
12318 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012319 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012320 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012321 length+=4;
12322 (void) WriteBlob(image,length,chunk);
12323 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12324 }
12325 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12326 (GetNextImageInList(image) != (Image *) NULL) &&
12327 (image->iterations != 1))
12328 {
12329 /*
12330 Write MNG TERM chunk
12331 */
12332 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12333 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012334 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012335 chunk[4]=3; /* repeat animation */
12336 chunk[5]=0; /* show last frame when done */
12337 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12338 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012339
cristy3ed852e2009-09-05 21:47:34 +000012340 if (image->iterations == 0)
12341 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012342
cristy3ed852e2009-09-05 21:47:34 +000012343 else
12344 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012345
cristy3ed852e2009-09-05 21:47:34 +000012346 if (logging != MagickFalse)
12347 {
12348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012349 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12350 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012351
cristy3ed852e2009-09-05 21:47:34 +000012352 if (image->iterations == 0)
12353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012354 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012355
cristy3ed852e2009-09-05 21:47:34 +000012356 else
12357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012358 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012359 }
12360 (void) WriteBlob(image,14,chunk);
12361 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12362 }
12363 /*
12364 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12365 */
12366 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12367 mng_info->equal_srgbs)
12368 {
12369 /*
12370 Write MNG sRGB chunk
12371 */
12372 (void) WriteBlobMSBULong(image,1L);
12373 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012374 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012375
cristy3ed852e2009-09-05 21:47:34 +000012376 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012377 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012378 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012379 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012380
cristy3ed852e2009-09-05 21:47:34 +000012381 else
glennrpe610a072010-08-05 17:08:46 +000012382 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012383 Magick_RenderingIntent_to_PNG_RenderingIntent(
12384 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012385
cristy3ed852e2009-09-05 21:47:34 +000012386 (void) WriteBlob(image,5,chunk);
12387 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12388 mng_info->have_write_global_srgb=MagickTrue;
12389 }
glennrp0fe50b42010-11-16 03:52:51 +000012390
cristy3ed852e2009-09-05 21:47:34 +000012391 else
12392 {
12393 if (image->gamma && mng_info->equal_gammas)
12394 {
12395 /*
12396 Write MNG gAMA chunk
12397 */
12398 (void) WriteBlobMSBULong(image,4L);
12399 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012400 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012401 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012402 (void) WriteBlob(image,8,chunk);
12403 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12404 mng_info->have_write_global_gama=MagickTrue;
12405 }
12406 if (mng_info->equal_chrms)
12407 {
12408 PrimaryInfo
12409 primary;
12410
12411 /*
12412 Write MNG cHRM chunk
12413 */
12414 (void) WriteBlobMSBULong(image,32L);
12415 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012416 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012417 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012418 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12419 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012420 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012421 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12422 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012423 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012424 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12425 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012426 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012427 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12428 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012429 (void) WriteBlob(image,36,chunk);
12430 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12431 mng_info->have_write_global_chrm=MagickTrue;
12432 }
12433 }
12434 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12435 {
12436 /*
12437 Write MNG pHYs chunk
12438 */
12439 (void) WriteBlobMSBULong(image,9L);
12440 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012441 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012442
cristy3ed852e2009-09-05 21:47:34 +000012443 if (image->units == PixelsPerInchResolution)
12444 {
cristy35ef8242010-06-03 16:24:13 +000012445 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012446 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012447
cristy35ef8242010-06-03 16:24:13 +000012448 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012449 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012450
cristy3ed852e2009-09-05 21:47:34 +000012451 chunk[12]=1;
12452 }
glennrp0fe50b42010-11-16 03:52:51 +000012453
cristy3ed852e2009-09-05 21:47:34 +000012454 else
12455 {
12456 if (image->units == PixelsPerCentimeterResolution)
12457 {
cristy35ef8242010-06-03 16:24:13 +000012458 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012459 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012460
cristy35ef8242010-06-03 16:24:13 +000012461 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012462 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012463
cristy3ed852e2009-09-05 21:47:34 +000012464 chunk[12]=1;
12465 }
glennrp0fe50b42010-11-16 03:52:51 +000012466
cristy3ed852e2009-09-05 21:47:34 +000012467 else
12468 {
cristy35ef8242010-06-03 16:24:13 +000012469 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12470 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012471 chunk[12]=0;
12472 }
12473 }
12474 (void) WriteBlob(image,13,chunk);
12475 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12476 }
12477 /*
12478 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12479 or does not cover the entire frame.
12480 */
12481 if (write_mng && (image->matte || image->page.x > 0 ||
12482 image->page.y > 0 || (image->page.width &&
12483 (image->page.width+image->page.x < mng_info->page.width))
12484 || (image->page.height && (image->page.height+image->page.y
12485 < mng_info->page.height))))
12486 {
12487 (void) WriteBlobMSBULong(image,6L);
12488 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012489 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012490 red=ScaleQuantumToShort(image->background_color.red);
12491 green=ScaleQuantumToShort(image->background_color.green);
12492 blue=ScaleQuantumToShort(image->background_color.blue);
12493 PNGShort(chunk+4,red);
12494 PNGShort(chunk+6,green);
12495 PNGShort(chunk+8,blue);
12496 (void) WriteBlob(image,10,chunk);
12497 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12498 if (mng_info->equal_backgrounds)
12499 {
12500 (void) WriteBlobMSBULong(image,6L);
12501 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012502 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012503 (void) WriteBlob(image,10,chunk);
12504 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12505 }
12506 }
12507
12508#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12509 if ((need_local_plte == MagickFalse) &&
12510 (image->storage_class == PseudoClass) &&
12511 (all_images_are_gray == MagickFalse))
12512 {
cristybb503372010-05-27 20:51:26 +000012513 size_t
cristy3ed852e2009-09-05 21:47:34 +000012514 data_length;
12515
12516 /*
12517 Write MNG PLTE chunk
12518 */
12519 data_length=3*image->colors;
12520 (void) WriteBlobMSBULong(image,data_length);
12521 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012522 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012523
cristybb503372010-05-27 20:51:26 +000012524 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012525 {
12526 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
12527 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
12528 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
12529 }
glennrp0fe50b42010-11-16 03:52:51 +000012530
cristy3ed852e2009-09-05 21:47:34 +000012531 (void) WriteBlob(image,data_length+4,chunk);
12532 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12533 mng_info->have_write_global_plte=MagickTrue;
12534 }
12535#endif
12536 }
12537 scene=0;
12538 mng_info->delay=0;
12539#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12540 defined(PNG_MNG_FEATURES_SUPPORTED)
12541 mng_info->equal_palettes=MagickFalse;
12542#endif
12543 do
12544 {
12545 if (mng_info->adjoin)
12546 {
12547#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12548 defined(PNG_MNG_FEATURES_SUPPORTED)
12549 /*
12550 If we aren't using a global palette for the entire MNG, check to
12551 see if we can use one for two or more consecutive images.
12552 */
12553 if (need_local_plte && use_global_plte && !all_images_are_gray)
12554 {
12555 if (mng_info->IsPalette)
12556 {
12557 /*
12558 When equal_palettes is true, this image has the same palette
12559 as the previous PseudoClass image
12560 */
12561 mng_info->have_write_global_plte=mng_info->equal_palettes;
12562 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12563 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12564 {
12565 /*
12566 Write MNG PLTE chunk
12567 */
cristybb503372010-05-27 20:51:26 +000012568 size_t
cristy3ed852e2009-09-05 21:47:34 +000012569 data_length;
12570
12571 data_length=3*image->colors;
12572 (void) WriteBlobMSBULong(image,data_length);
12573 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012574 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012575
cristybb503372010-05-27 20:51:26 +000012576 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012577 {
12578 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12579 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12580 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12581 }
glennrp0fe50b42010-11-16 03:52:51 +000012582
cristy3ed852e2009-09-05 21:47:34 +000012583 (void) WriteBlob(image,data_length+4,chunk);
12584 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12585 (uInt) (data_length+4)));
12586 mng_info->have_write_global_plte=MagickTrue;
12587 }
12588 }
12589 else
12590 mng_info->have_write_global_plte=MagickFalse;
12591 }
12592#endif
12593 if (need_defi)
12594 {
cristybb503372010-05-27 20:51:26 +000012595 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012596 previous_x,
12597 previous_y;
12598
12599 if (scene)
12600 {
12601 previous_x=mng_info->page.x;
12602 previous_y=mng_info->page.y;
12603 }
12604 else
12605 {
12606 previous_x=0;
12607 previous_y=0;
12608 }
12609 mng_info->page=image->page;
12610 if ((mng_info->page.x != previous_x) ||
12611 (mng_info->page.y != previous_y))
12612 {
12613 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12614 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012615 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012616 chunk[4]=0; /* object 0 MSB */
12617 chunk[5]=0; /* object 0 LSB */
12618 chunk[6]=0; /* visible */
12619 chunk[7]=0; /* abstract */
12620 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12621 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12622 (void) WriteBlob(image,16,chunk);
12623 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12624 }
12625 }
12626 }
12627
12628 mng_info->write_mng=write_mng;
12629
12630 if ((int) image->dispose >= 3)
12631 mng_info->framing_mode=3;
12632
12633 if (mng_info->need_fram && mng_info->adjoin &&
12634 ((image->delay != mng_info->delay) ||
12635 (mng_info->framing_mode != mng_info->old_framing_mode)))
12636 {
12637 if (image->delay == mng_info->delay)
12638 {
12639 /*
12640 Write a MNG FRAM chunk with the new framing mode.
12641 */
12642 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12643 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012644 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012645 chunk[4]=(unsigned char) mng_info->framing_mode;
12646 (void) WriteBlob(image,5,chunk);
12647 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12648 }
12649 else
12650 {
12651 /*
12652 Write a MNG FRAM chunk with the delay.
12653 */
12654 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12655 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012656 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012657 chunk[4]=(unsigned char) mng_info->framing_mode;
12658 chunk[5]=0; /* frame name separator (no name) */
12659 chunk[6]=2; /* flag for changing default delay */
12660 chunk[7]=0; /* flag for changing frame timeout */
12661 chunk[8]=0; /* flag for changing frame clipping */
12662 chunk[9]=0; /* flag for changing frame sync_id */
12663 PNGLong(chunk+10,(png_uint_32)
12664 ((mng_info->ticks_per_second*
12665 image->delay)/MagickMax(image->ticks_per_second,1)));
12666 (void) WriteBlob(image,14,chunk);
12667 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012668 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012669 }
12670 mng_info->old_framing_mode=mng_info->framing_mode;
12671 }
12672
12673#if defined(JNG_SUPPORTED)
12674 if (image_info->compression == JPEGCompression)
12675 {
12676 ImageInfo
12677 *write_info;
12678
12679 if (logging != MagickFalse)
12680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12681 " Writing JNG object.");
12682 /* To do: specify the desired alpha compression method. */
12683 write_info=CloneImageInfo(image_info);
12684 write_info->compression=UndefinedCompression;
12685 status=WriteOneJNGImage(mng_info,write_info,image);
12686 write_info=DestroyImageInfo(write_info);
12687 }
12688 else
12689#endif
12690 {
12691 if (logging != MagickFalse)
12692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12693 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012694
glennrpb9cfe272010-12-21 15:08:06 +000012695 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012696 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012697
12698 /* We don't want any ancillary chunks written */
12699 mng_info->ping_exclude_bKGD=MagickTrue;
12700 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012701 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012702 mng_info->ping_exclude_EXIF=MagickTrue;
12703 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012704 mng_info->ping_exclude_iCCP=MagickTrue;
12705 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12706 mng_info->ping_exclude_oFFs=MagickTrue;
12707 mng_info->ping_exclude_pHYs=MagickTrue;
12708 mng_info->ping_exclude_sRGB=MagickTrue;
12709 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012710 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012711 mng_info->ping_exclude_vpAg=MagickTrue;
12712 mng_info->ping_exclude_zCCP=MagickTrue;
12713 mng_info->ping_exclude_zTXt=MagickTrue;
12714
cristy3ed852e2009-09-05 21:47:34 +000012715 status=WriteOnePNGImage(mng_info,image_info,image);
12716 }
12717
12718 if (status == MagickFalse)
12719 {
12720 MngInfoFreeStruct(mng_info,&have_mng_structure);
12721 (void) CloseBlob(image);
12722 return(MagickFalse);
12723 }
12724 (void) CatchImageException(image);
12725 if (GetNextImageInList(image) == (Image *) NULL)
12726 break;
12727 image=SyncNextImageInList(image);
12728 status=SetImageProgress(image,SaveImagesTag,scene++,
12729 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012730
cristy3ed852e2009-09-05 21:47:34 +000012731 if (status == MagickFalse)
12732 break;
glennrp0fe50b42010-11-16 03:52:51 +000012733
cristy3ed852e2009-09-05 21:47:34 +000012734 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012735
cristy3ed852e2009-09-05 21:47:34 +000012736 if (write_mng)
12737 {
12738 while (GetPreviousImageInList(image) != (Image *) NULL)
12739 image=GetPreviousImageInList(image);
12740 /*
12741 Write the MEND chunk.
12742 */
12743 (void) WriteBlobMSBULong(image,0x00000000L);
12744 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012745 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012746 (void) WriteBlob(image,4,chunk);
12747 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12748 }
12749 /*
12750 Relinquish resources.
12751 */
12752 (void) CloseBlob(image);
12753 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012754
cristy3ed852e2009-09-05 21:47:34 +000012755 if (logging != MagickFalse)
12756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012757
cristy3ed852e2009-09-05 21:47:34 +000012758 return(MagickTrue);
12759}
glennrpd5045b42010-03-24 12:40:35 +000012760#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012761
cristy3ed852e2009-09-05 21:47:34 +000012762static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12763{
12764 image=image;
12765 printf("Your PNG library is too old: You have libpng-%s\n",
12766 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012767
cristy3ed852e2009-09-05 21:47:34 +000012768 ThrowBinaryException(CoderError,"PNG library is too old",
12769 image_info->filename);
12770}
glennrp39992b42010-11-14 00:03:43 +000012771
cristy3ed852e2009-09-05 21:47:34 +000012772static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12773{
12774 return(WritePNGImage(image_info,image));
12775}
glennrpd5045b42010-03-24 12:40:35 +000012776#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012777#endif