blob: 528c961fbca7cb79aa952892a11855665ab346a9 [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++);
glennrp8b698592011-04-26 03:38:21 +00003110 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-quantum));
3111 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 {
glennrp8b698592011-04-26 03:38:21 +00003149 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-(*p++)));
3150 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 {
glennrp7c7b3152011-04-26 04:01:27 +00004386 SetOpacityPixelComponent(q,(Quantum) QuantumRange-
4387 GetRedPixelComponent(s));
4388 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
cristy3ed852e2009-09-05 21:47:34 +00004389 image->matte=MagickTrue;
4390 }
glennrp0fe50b42010-11-16 03:52:51 +00004391
cristy3ed852e2009-09-05 21:47:34 +00004392 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4393 break;
4394 }
4395 (void) RelinquishUniqueFileResource(alpha_image->filename);
4396 alpha_image=DestroyImage(alpha_image);
4397 alpha_image_info=DestroyImageInfo(alpha_image_info);
4398 if (jng_image != (Image *) NULL)
4399 jng_image=DestroyImage(jng_image);
4400 }
4401 }
4402
glennrp47b9dd52010-11-24 18:12:06 +00004403 /* Read the JNG image. */
4404
cristy3ed852e2009-09-05 21:47:34 +00004405 if (mng_info->mng_type == 0)
4406 {
4407 mng_info->mng_width=jng_width;
4408 mng_info->mng_height=jng_height;
4409 }
glennrp0fe50b42010-11-16 03:52:51 +00004410
cristy3ed852e2009-09-05 21:47:34 +00004411 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004412 {
4413 image->page.width=jng_width;
4414 image->page.height=jng_height;
4415 }
4416
cristy3ed852e2009-09-05 21:47:34 +00004417 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004418 {
4419 image->page.x=mng_info->x_off[mng_info->object_id];
4420 image->page.y=mng_info->y_off[mng_info->object_id];
4421 }
4422
cristy3ed852e2009-09-05 21:47:34 +00004423 else
glennrp0fe50b42010-11-16 03:52:51 +00004424 {
4425 image->page.y=mng_info->y_off[mng_info->object_id];
4426 }
4427
cristy3ed852e2009-09-05 21:47:34 +00004428 mng_info->image_found++;
4429 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4430 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004431
cristy3ed852e2009-09-05 21:47:34 +00004432 if (logging != MagickFalse)
4433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4434 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004435
cristy3ed852e2009-09-05 21:47:34 +00004436 return(image);
4437}
4438
4439/*
4440%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4441% %
4442% %
4443% %
4444% R e a d J N G I m a g e %
4445% %
4446% %
4447% %
4448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4449%
4450% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4451% (including the 8-byte signature) and returns it. It allocates the memory
4452% necessary for the new Image structure and returns a pointer to the new
4453% image.
4454%
4455% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4456%
4457% The format of the ReadJNGImage method is:
4458%
4459% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4460% *exception)
4461%
4462% A description of each parameter follows:
4463%
4464% o image_info: the image info.
4465%
4466% o exception: return any errors or warnings in this structure.
4467%
4468*/
4469
4470static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4471{
4472 Image
4473 *image,
4474 *previous;
4475
4476 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004477 have_mng_structure,
4478 logging,
cristy3ed852e2009-09-05 21:47:34 +00004479 status;
4480
4481 MngInfo
4482 *mng_info;
4483
4484 char
4485 magic_number[MaxTextExtent];
4486
cristy3ed852e2009-09-05 21:47:34 +00004487 size_t
4488 count;
4489
4490 /*
4491 Open image file.
4492 */
4493 assert(image_info != (const ImageInfo *) NULL);
4494 assert(image_info->signature == MagickSignature);
4495 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4496 assert(exception != (ExceptionInfo *) NULL);
4497 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004498 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004499 image=AcquireImage(image_info);
4500 mng_info=(MngInfo *) NULL;
4501 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004502
cristy3ed852e2009-09-05 21:47:34 +00004503 if (status == MagickFalse)
4504 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004505
cristy3ed852e2009-09-05 21:47:34 +00004506 if (LocaleCompare(image_info->magick,"JNG") != 0)
4507 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004508
glennrp47b9dd52010-11-24 18:12:06 +00004509 /* Verify JNG signature. */
4510
cristy3ed852e2009-09-05 21:47:34 +00004511 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004512
glennrp3b8763e2011-02-21 12:08:18 +00004513 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004514 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004515
glennrp47b9dd52010-11-24 18:12:06 +00004516 /* Allocate a MngInfo structure. */
4517
cristy3ed852e2009-09-05 21:47:34 +00004518 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004519 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004520
cristy3ed852e2009-09-05 21:47:34 +00004521 if (mng_info == (MngInfo *) NULL)
4522 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004523
glennrp47b9dd52010-11-24 18:12:06 +00004524 /* Initialize members of the MngInfo structure. */
4525
cristy3ed852e2009-09-05 21:47:34 +00004526 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4527 have_mng_structure=MagickTrue;
4528
4529 mng_info->image=image;
4530 previous=image;
4531 image=ReadOneJNGImage(mng_info,image_info,exception);
4532 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004533
cristy3ed852e2009-09-05 21:47:34 +00004534 if (image == (Image *) NULL)
4535 {
4536 if (IsImageObject(previous) != MagickFalse)
4537 {
4538 (void) CloseBlob(previous);
4539 (void) DestroyImageList(previous);
4540 }
glennrp0fe50b42010-11-16 03:52:51 +00004541
cristy3ed852e2009-09-05 21:47:34 +00004542 if (logging != MagickFalse)
4543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4544 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004545
cristy3ed852e2009-09-05 21:47:34 +00004546 return((Image *) NULL);
4547 }
4548 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004549
cristy3ed852e2009-09-05 21:47:34 +00004550 if (image->columns == 0 || image->rows == 0)
4551 {
4552 if (logging != MagickFalse)
4553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4554 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004555
cristy3ed852e2009-09-05 21:47:34 +00004556 ThrowReaderException(CorruptImageError,"CorruptImage");
4557 }
glennrp0fe50b42010-11-16 03:52:51 +00004558
cristy3ed852e2009-09-05 21:47:34 +00004559 if (logging != MagickFalse)
4560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004561
cristy3ed852e2009-09-05 21:47:34 +00004562 return(image);
4563}
4564#endif
4565
4566static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4567{
4568 char
4569 page_geometry[MaxTextExtent];
4570
4571 Image
4572 *image,
4573 *previous;
4574
cristy4383ec82011-01-05 15:42:32 +00004575 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004576 logging,
4577 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004578
cristy3ed852e2009-09-05 21:47:34 +00004579 volatile int
4580 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004581 object_id,
4582 term_chunk_found,
4583 skip_to_iend;
4584
cristybb503372010-05-27 20:51:26 +00004585 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004586 image_count=0;
4587
4588 MagickBooleanType
4589 status;
4590
4591 MagickOffsetType
4592 offset;
4593
4594 MngInfo
4595 *mng_info;
4596
4597 MngBox
4598 default_fb,
4599 fb,
4600 previous_fb;
4601
4602#if defined(MNG_INSERT_LAYERS)
4603 PixelPacket
4604 mng_background_color;
4605#endif
4606
4607 register unsigned char
4608 *p;
4609
cristybb503372010-05-27 20:51:26 +00004610 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004611 i;
4612
4613 size_t
4614 count;
4615
cristybb503372010-05-27 20:51:26 +00004616 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004617 loop_level;
4618
4619 volatile short
4620 skipping_loop;
4621
4622#if defined(MNG_INSERT_LAYERS)
4623 unsigned int
4624 mandatory_back=0;
4625#endif
4626
4627 volatile unsigned int
4628#ifdef MNG_OBJECT_BUFFERS
4629 mng_background_object=0,
4630#endif
4631 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4632
cristybb503372010-05-27 20:51:26 +00004633 size_t
cristy3ed852e2009-09-05 21:47:34 +00004634 default_frame_timeout,
4635 frame_timeout,
4636#if defined(MNG_INSERT_LAYERS)
4637 image_height,
4638 image_width,
4639#endif
4640 length;
4641
glennrp38ea0832010-06-02 18:50:28 +00004642 /* These delays are all measured in image ticks_per_second,
4643 * not in MNG ticks_per_second
4644 */
cristybb503372010-05-27 20:51:26 +00004645 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004646 default_frame_delay,
4647 final_delay,
4648 final_image_delay,
4649 frame_delay,
4650#if defined(MNG_INSERT_LAYERS)
4651 insert_layers,
4652#endif
4653 mng_iterations=1,
4654 simplicity=0,
4655 subframe_height=0,
4656 subframe_width=0;
4657
4658 previous_fb.top=0;
4659 previous_fb.bottom=0;
4660 previous_fb.left=0;
4661 previous_fb.right=0;
4662 default_fb.top=0;
4663 default_fb.bottom=0;
4664 default_fb.left=0;
4665 default_fb.right=0;
4666
glennrp47b9dd52010-11-24 18:12:06 +00004667 /* Open image file. */
4668
cristy3ed852e2009-09-05 21:47:34 +00004669 assert(image_info != (const ImageInfo *) NULL);
4670 assert(image_info->signature == MagickSignature);
4671 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4672 assert(exception != (ExceptionInfo *) NULL);
4673 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004674 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004675 image=AcquireImage(image_info);
4676 mng_info=(MngInfo *) NULL;
4677 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004678
cristy3ed852e2009-09-05 21:47:34 +00004679 if (status == MagickFalse)
4680 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004681
cristy3ed852e2009-09-05 21:47:34 +00004682 first_mng_object=MagickFalse;
4683 skipping_loop=(-1);
4684 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004685
4686 /* Allocate a MngInfo structure. */
4687
cristy73bd4a52010-10-05 11:24:23 +00004688 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004689
cristy3ed852e2009-09-05 21:47:34 +00004690 if (mng_info == (MngInfo *) NULL)
4691 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004692
glennrp47b9dd52010-11-24 18:12:06 +00004693 /* Initialize members of the MngInfo structure. */
4694
cristy3ed852e2009-09-05 21:47:34 +00004695 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4696 mng_info->image=image;
4697 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004698
4699 if (LocaleCompare(image_info->magick,"MNG") == 0)
4700 {
4701 char
4702 magic_number[MaxTextExtent];
4703
glennrp47b9dd52010-11-24 18:12:06 +00004704 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004705 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4706 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4707 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004708
4709 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004710 for (i=0; i < MNG_MAX_OBJECTS; i++)
4711 {
cristybb503372010-05-27 20:51:26 +00004712 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4713 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004714 }
4715 mng_info->exists[0]=MagickTrue;
4716 }
glennrp47b9dd52010-11-24 18:12:06 +00004717
cristy3ed852e2009-09-05 21:47:34 +00004718 first_mng_object=MagickTrue;
4719 mng_type=0;
4720#if defined(MNG_INSERT_LAYERS)
4721 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4722#endif
4723 default_frame_delay=0;
4724 default_frame_timeout=0;
4725 frame_delay=0;
4726 final_delay=1;
4727 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4728 object_id=0;
4729 skip_to_iend=MagickFalse;
4730 term_chunk_found=MagickFalse;
4731 mng_info->framing_mode=1;
4732#if defined(MNG_INSERT_LAYERS)
4733 mandatory_back=MagickFalse;
4734#endif
4735#if defined(MNG_INSERT_LAYERS)
4736 mng_background_color=image->background_color;
4737#endif
4738 default_fb=mng_info->frame;
4739 previous_fb=mng_info->frame;
4740 do
4741 {
4742 char
4743 type[MaxTextExtent];
4744
4745 if (LocaleCompare(image_info->magick,"MNG") == 0)
4746 {
4747 unsigned char
4748 *chunk;
4749
4750 /*
4751 Read a new chunk.
4752 */
4753 type[0]='\0';
4754 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4755 length=ReadBlobMSBLong(image);
4756 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4757
4758 if (logging != MagickFalse)
4759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004760 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4761 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004762
4763 if (length > PNG_UINT_31_MAX)
4764 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004765
cristy3ed852e2009-09-05 21:47:34 +00004766 if (count == 0)
4767 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004768
cristy3ed852e2009-09-05 21:47:34 +00004769 p=NULL;
4770 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004771
cristy3ed852e2009-09-05 21:47:34 +00004772 if (length)
4773 {
4774 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004775
cristy3ed852e2009-09-05 21:47:34 +00004776 if (chunk == (unsigned char *) NULL)
4777 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004778
cristybb503372010-05-27 20:51:26 +00004779 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004780 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004781
cristy3ed852e2009-09-05 21:47:34 +00004782 p=chunk;
4783 }
glennrp0fe50b42010-11-16 03:52:51 +00004784
cristy3ed852e2009-09-05 21:47:34 +00004785 (void) ReadBlobMSBLong(image); /* read crc word */
4786
4787#if !defined(JNG_SUPPORTED)
4788 if (memcmp(type,mng_JHDR,4) == 0)
4789 {
4790 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004791
cristy3ed852e2009-09-05 21:47:34 +00004792 if (mng_info->jhdr_warning == 0)
4793 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4794 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004795
cristy3ed852e2009-09-05 21:47:34 +00004796 mng_info->jhdr_warning++;
4797 }
4798#endif
4799 if (memcmp(type,mng_DHDR,4) == 0)
4800 {
4801 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004802
cristy3ed852e2009-09-05 21:47:34 +00004803 if (mng_info->dhdr_warning == 0)
4804 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4805 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004806
cristy3ed852e2009-09-05 21:47:34 +00004807 mng_info->dhdr_warning++;
4808 }
4809 if (memcmp(type,mng_MEND,4) == 0)
4810 break;
glennrp47b9dd52010-11-24 18:12:06 +00004811
cristy3ed852e2009-09-05 21:47:34 +00004812 if (skip_to_iend)
4813 {
4814 if (memcmp(type,mng_IEND,4) == 0)
4815 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004816
cristy3ed852e2009-09-05 21:47:34 +00004817 if (length)
4818 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004819
cristy3ed852e2009-09-05 21:47:34 +00004820 if (logging != MagickFalse)
4821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4822 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004823
cristy3ed852e2009-09-05 21:47:34 +00004824 continue;
4825 }
glennrp0fe50b42010-11-16 03:52:51 +00004826
cristy3ed852e2009-09-05 21:47:34 +00004827 if (memcmp(type,mng_MHDR,4) == 0)
4828 {
cristybb503372010-05-27 20:51:26 +00004829 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004830 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004831
cristybb503372010-05-27 20:51:26 +00004832 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004833 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004834
cristy3ed852e2009-09-05 21:47:34 +00004835 if (logging != MagickFalse)
4836 {
4837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004838 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004840 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004841 }
glennrp0fe50b42010-11-16 03:52:51 +00004842
cristy3ed852e2009-09-05 21:47:34 +00004843 p+=8;
cristy8182b072010-05-30 20:10:53 +00004844 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004845
cristy3ed852e2009-09-05 21:47:34 +00004846 if (mng_info->ticks_per_second == 0)
4847 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004848
cristy3ed852e2009-09-05 21:47:34 +00004849 else
4850 default_frame_delay=1UL*image->ticks_per_second/
4851 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004852
cristy3ed852e2009-09-05 21:47:34 +00004853 frame_delay=default_frame_delay;
4854 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004855
cristy3ed852e2009-09-05 21:47:34 +00004856 if (length > 16)
4857 {
4858 p+=16;
cristy8182b072010-05-30 20:10:53 +00004859 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004860 }
glennrp0fe50b42010-11-16 03:52:51 +00004861
cristy3ed852e2009-09-05 21:47:34 +00004862 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864 if ((simplicity != 0) && ((simplicity | 11) == 11))
4865 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004866
cristy3ed852e2009-09-05 21:47:34 +00004867 if ((simplicity != 0) && ((simplicity | 9) == 9))
4868 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004869
cristy3ed852e2009-09-05 21:47:34 +00004870#if defined(MNG_INSERT_LAYERS)
4871 if (mng_type != 3)
4872 insert_layers=MagickTrue;
4873#endif
4874 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4875 {
glennrp47b9dd52010-11-24 18:12:06 +00004876 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004877 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004878
cristy3ed852e2009-09-05 21:47:34 +00004879 if (GetNextImageInList(image) == (Image *) NULL)
4880 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004881
cristy3ed852e2009-09-05 21:47:34 +00004882 image=SyncNextImageInList(image);
4883 mng_info->image=image;
4884 }
4885
4886 if ((mng_info->mng_width > 65535L) ||
4887 (mng_info->mng_height > 65535L))
4888 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004889
cristy3b6fd2e2011-05-20 12:53:50 +00004890 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004891 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004892 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004893
cristy3ed852e2009-09-05 21:47:34 +00004894 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004895 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004896 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004897 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004898 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004899
cristy3ed852e2009-09-05 21:47:34 +00004900 for (i=0; i < MNG_MAX_OBJECTS; i++)
4901 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004902
cristy3ed852e2009-09-05 21:47:34 +00004903 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4904 continue;
4905 }
4906
4907 if (memcmp(type,mng_TERM,4) == 0)
4908 {
4909 int
4910 repeat=0;
4911
4912
4913 if (length)
4914 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004915
cristy3ed852e2009-09-05 21:47:34 +00004916 if (repeat == 3)
4917 {
cristy8182b072010-05-30 20:10:53 +00004918 final_delay=(png_uint_32) mng_get_long(&p[2]);
4919 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004920
cristy3ed852e2009-09-05 21:47:34 +00004921 if (mng_iterations == PNG_UINT_31_MAX)
4922 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004923
cristy3ed852e2009-09-05 21:47:34 +00004924 image->iterations=mng_iterations;
4925 term_chunk_found=MagickTrue;
4926 }
glennrp0fe50b42010-11-16 03:52:51 +00004927
cristy3ed852e2009-09-05 21:47:34 +00004928 if (logging != MagickFalse)
4929 {
4930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4931 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004932
cristy3ed852e2009-09-05 21:47:34 +00004933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004934 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004935
cristy3ed852e2009-09-05 21:47:34 +00004936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004937 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004938 }
glennrp0fe50b42010-11-16 03:52:51 +00004939
cristy3ed852e2009-09-05 21:47:34 +00004940 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4941 continue;
4942 }
4943 if (memcmp(type,mng_DEFI,4) == 0)
4944 {
4945 if (mng_type == 3)
4946 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4947 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4948 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004949
cristy3ed852e2009-09-05 21:47:34 +00004950 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004951
cristy3ed852e2009-09-05 21:47:34 +00004952 if (mng_type == 2 && object_id != 0)
4953 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4954 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4955 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004956
cristy3ed852e2009-09-05 21:47:34 +00004957 if (object_id > MNG_MAX_OBJECTS)
4958 {
4959 /*
4960 Instead ofsuing a warning we should allocate a larger
4961 MngInfo structure and continue.
4962 */
4963 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4964 CoderError,"object id too large","`%s'",image->filename);
4965 object_id=MNG_MAX_OBJECTS;
4966 }
glennrp0fe50b42010-11-16 03:52:51 +00004967
cristy3ed852e2009-09-05 21:47:34 +00004968 if (mng_info->exists[object_id])
4969 if (mng_info->frozen[object_id])
4970 {
4971 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4972 (void) ThrowMagickException(&image->exception,
4973 GetMagickModule(),CoderError,
4974 "DEFI cannot redefine a frozen MNG object","`%s'",
4975 image->filename);
4976 continue;
4977 }
glennrp0fe50b42010-11-16 03:52:51 +00004978
cristy3ed852e2009-09-05 21:47:34 +00004979 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004980
cristy3ed852e2009-09-05 21:47:34 +00004981 if (length > 2)
4982 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004983
cristy3ed852e2009-09-05 21:47:34 +00004984 /*
4985 Extract object offset info.
4986 */
4987 if (length > 11)
4988 {
glennrp0fe50b42010-11-16 03:52:51 +00004989 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4990 (p[5] << 16) | (p[6] << 8) | p[7]);
4991
4992 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4993 (p[9] << 16) | (p[10] << 8) | p[11]);
4994
cristy3ed852e2009-09-05 21:47:34 +00004995 if (logging != MagickFalse)
4996 {
4997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004998 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004999 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005000
cristy3ed852e2009-09-05 21:47:34 +00005001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005002 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005003 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005004 }
5005 }
glennrp0fe50b42010-11-16 03:52:51 +00005006
cristy3ed852e2009-09-05 21:47:34 +00005007 /*
5008 Extract object clipping info.
5009 */
5010 if (length > 27)
5011 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5012 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005013
cristy3ed852e2009-09-05 21:47:34 +00005014 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5015 continue;
5016 }
5017 if (memcmp(type,mng_bKGD,4) == 0)
5018 {
5019 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005020
cristy3ed852e2009-09-05 21:47:34 +00005021 if (length > 5)
5022 {
5023 mng_info->mng_global_bkgd.red=
5024 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005025
cristy3ed852e2009-09-05 21:47:34 +00005026 mng_info->mng_global_bkgd.green=
5027 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005028
cristy3ed852e2009-09-05 21:47:34 +00005029 mng_info->mng_global_bkgd.blue=
5030 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005031
cristy3ed852e2009-09-05 21:47:34 +00005032 mng_info->have_global_bkgd=MagickTrue;
5033 }
glennrp0fe50b42010-11-16 03:52:51 +00005034
cristy3ed852e2009-09-05 21:47:34 +00005035 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5036 continue;
5037 }
5038 if (memcmp(type,mng_BACK,4) == 0)
5039 {
5040#if defined(MNG_INSERT_LAYERS)
5041 if (length > 6)
5042 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005043
cristy3ed852e2009-09-05 21:47:34 +00005044 else
5045 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005046
cristy3ed852e2009-09-05 21:47:34 +00005047 if (mandatory_back && length > 5)
5048 {
5049 mng_background_color.red=
5050 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005051
cristy3ed852e2009-09-05 21:47:34 +00005052 mng_background_color.green=
5053 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005054
cristy3ed852e2009-09-05 21:47:34 +00005055 mng_background_color.blue=
5056 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005057
cristy3ed852e2009-09-05 21:47:34 +00005058 mng_background_color.opacity=OpaqueOpacity;
5059 }
glennrp0fe50b42010-11-16 03:52:51 +00005060
cristy3ed852e2009-09-05 21:47:34 +00005061#ifdef MNG_OBJECT_BUFFERS
5062 if (length > 8)
5063 mng_background_object=(p[7] << 8) | p[8];
5064#endif
5065#endif
5066 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5067 continue;
5068 }
glennrp47b9dd52010-11-24 18:12:06 +00005069
cristy3ed852e2009-09-05 21:47:34 +00005070 if (memcmp(type,mng_PLTE,4) == 0)
5071 {
glennrp47b9dd52010-11-24 18:12:06 +00005072 /* Read global PLTE. */
5073
cristy3ed852e2009-09-05 21:47:34 +00005074 if (length && (length < 769))
5075 {
5076 if (mng_info->global_plte == (png_colorp) NULL)
5077 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5078 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005079
cristybb503372010-05-27 20:51:26 +00005080 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005081 {
5082 mng_info->global_plte[i].red=p[3*i];
5083 mng_info->global_plte[i].green=p[3*i+1];
5084 mng_info->global_plte[i].blue=p[3*i+2];
5085 }
glennrp0fe50b42010-11-16 03:52:51 +00005086
cristy35ef8242010-06-03 16:24:13 +00005087 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005088 }
5089#ifdef MNG_LOOSE
5090 for ( ; i < 256; i++)
5091 {
5092 mng_info->global_plte[i].red=i;
5093 mng_info->global_plte[i].green=i;
5094 mng_info->global_plte[i].blue=i;
5095 }
glennrp0fe50b42010-11-16 03:52:51 +00005096
cristy3ed852e2009-09-05 21:47:34 +00005097 if (length)
5098 mng_info->global_plte_length=256;
5099#endif
5100 else
5101 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005102
cristy3ed852e2009-09-05 21:47:34 +00005103 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5104 continue;
5105 }
glennrp47b9dd52010-11-24 18:12:06 +00005106
cristy3ed852e2009-09-05 21:47:34 +00005107 if (memcmp(type,mng_tRNS,4) == 0)
5108 {
5109 /* read global tRNS */
5110
5111 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005112 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005113 mng_info->global_trns[i]=p[i];
5114
5115#ifdef MNG_LOOSE
5116 for ( ; i < 256; i++)
5117 mng_info->global_trns[i]=255;
5118#endif
cristy12560f32010-06-03 16:51:08 +00005119 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005120 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5121 continue;
5122 }
5123 if (memcmp(type,mng_gAMA,4) == 0)
5124 {
5125 if (length == 4)
5126 {
cristybb503372010-05-27 20:51:26 +00005127 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005128 igamma;
5129
cristy8182b072010-05-30 20:10:53 +00005130 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005131 mng_info->global_gamma=((float) igamma)*0.00001;
5132 mng_info->have_global_gama=MagickTrue;
5133 }
glennrp0fe50b42010-11-16 03:52:51 +00005134
cristy3ed852e2009-09-05 21:47:34 +00005135 else
5136 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005137
cristy3ed852e2009-09-05 21:47:34 +00005138 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5139 continue;
5140 }
5141
5142 if (memcmp(type,mng_cHRM,4) == 0)
5143 {
glennrp47b9dd52010-11-24 18:12:06 +00005144 /* Read global cHRM */
5145
cristy3ed852e2009-09-05 21:47:34 +00005146 if (length == 32)
5147 {
cristy8182b072010-05-30 20:10:53 +00005148 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5149 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5150 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005151 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005152 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005153 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005154 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005155 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005156 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005157 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005158 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005159 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005160 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005161 mng_info->have_global_chrm=MagickTrue;
5162 }
5163 else
5164 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005165
cristy3ed852e2009-09-05 21:47:34 +00005166 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5167 continue;
5168 }
glennrp47b9dd52010-11-24 18:12:06 +00005169
cristy3ed852e2009-09-05 21:47:34 +00005170 if (memcmp(type,mng_sRGB,4) == 0)
5171 {
5172 /*
5173 Read global sRGB.
5174 */
5175 if (length)
5176 {
glennrpe610a072010-08-05 17:08:46 +00005177 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005178 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005179 mng_info->have_global_srgb=MagickTrue;
5180 }
5181 else
5182 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005183
cristy3ed852e2009-09-05 21:47:34 +00005184 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5185 continue;
5186 }
glennrp47b9dd52010-11-24 18:12:06 +00005187
cristy3ed852e2009-09-05 21:47:34 +00005188 if (memcmp(type,mng_iCCP,4) == 0)
5189 {
glennrpfd05d622011-02-25 04:10:33 +00005190 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005191
5192 /*
5193 Read global iCCP.
5194 */
5195 if (length)
5196 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005197
cristy3ed852e2009-09-05 21:47:34 +00005198 continue;
5199 }
glennrp47b9dd52010-11-24 18:12:06 +00005200
cristy3ed852e2009-09-05 21:47:34 +00005201 if (memcmp(type,mng_FRAM,4) == 0)
5202 {
5203 if (mng_type == 3)
5204 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5205 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5206 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005207
cristy3ed852e2009-09-05 21:47:34 +00005208 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5209 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005210
cristy3ed852e2009-09-05 21:47:34 +00005211 frame_delay=default_frame_delay;
5212 frame_timeout=default_frame_timeout;
5213 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005214
cristy3ed852e2009-09-05 21:47:34 +00005215 if (length)
5216 if (p[0])
5217 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005218
cristy3ed852e2009-09-05 21:47:34 +00005219 if (logging != MagickFalse)
5220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5221 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005222
cristy3ed852e2009-09-05 21:47:34 +00005223 if (length > 6)
5224 {
glennrp47b9dd52010-11-24 18:12:06 +00005225 /* Note the delay and frame clipping boundaries. */
5226
cristy3ed852e2009-09-05 21:47:34 +00005227 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005228
cristybb503372010-05-27 20:51:26 +00005229 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005230 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005231
cristy3ed852e2009-09-05 21:47:34 +00005232 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005233
cristybb503372010-05-27 20:51:26 +00005234 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005235 {
5236 int
5237 change_delay,
5238 change_timeout,
5239 change_clipping;
5240
5241 change_delay=(*p++);
5242 change_timeout=(*p++);
5243 change_clipping=(*p++);
5244 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005245
cristy3ed852e2009-09-05 21:47:34 +00005246 if (change_delay)
5247 {
cristy8182b072010-05-30 20:10:53 +00005248 frame_delay=1UL*image->ticks_per_second*
5249 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005250
cristy8182b072010-05-30 20:10:53 +00005251 if (mng_info->ticks_per_second != 0)
5252 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005253
glennrpbb010dd2010-06-01 13:07:15 +00005254 else
5255 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005256
cristy3ed852e2009-09-05 21:47:34 +00005257 if (change_delay == 2)
5258 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005259
cristy3ed852e2009-09-05 21:47:34 +00005260 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005261
cristy3ed852e2009-09-05 21:47:34 +00005262 if (logging != MagickFalse)
5263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005264 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005265 }
glennrp47b9dd52010-11-24 18:12:06 +00005266
cristy3ed852e2009-09-05 21:47:34 +00005267 if (change_timeout)
5268 {
glennrpbb010dd2010-06-01 13:07:15 +00005269 frame_timeout=1UL*image->ticks_per_second*
5270 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005271
glennrpbb010dd2010-06-01 13:07:15 +00005272 if (mng_info->ticks_per_second != 0)
5273 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005274
glennrpbb010dd2010-06-01 13:07:15 +00005275 else
5276 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005277
cristy3ed852e2009-09-05 21:47:34 +00005278 if (change_delay == 2)
5279 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005280
cristy3ed852e2009-09-05 21:47:34 +00005281 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005282
cristy3ed852e2009-09-05 21:47:34 +00005283 if (logging != MagickFalse)
5284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005285 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005286 }
glennrp47b9dd52010-11-24 18:12:06 +00005287
cristy3ed852e2009-09-05 21:47:34 +00005288 if (change_clipping)
5289 {
5290 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5291 p+=17;
5292 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005293
cristy3ed852e2009-09-05 21:47:34 +00005294 if (logging != MagickFalse)
5295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005296 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005297 (double) fb.left,(double) fb.right,(double) fb.top,
5298 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005299
cristy3ed852e2009-09-05 21:47:34 +00005300 if (change_clipping == 2)
5301 default_fb=fb;
5302 }
5303 }
5304 }
5305 mng_info->clip=fb;
5306 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005307
cristybb503372010-05-27 20:51:26 +00005308 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005309 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005310
cristybb503372010-05-27 20:51:26 +00005311 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005312 -mng_info->clip.top);
5313 /*
5314 Insert a background layer behind the frame if framing_mode is 4.
5315 */
5316#if defined(MNG_INSERT_LAYERS)
5317 if (logging != MagickFalse)
5318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005319 " subframe_width=%.20g, subframe_height=%.20g",(double)
5320 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005321
cristy3ed852e2009-09-05 21:47:34 +00005322 if (insert_layers && (mng_info->framing_mode == 4) &&
5323 (subframe_width) && (subframe_height))
5324 {
glennrp47b9dd52010-11-24 18:12:06 +00005325 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00005326 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5327 {
5328 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005329
cristy3ed852e2009-09-05 21:47:34 +00005330 if (GetNextImageInList(image) == (Image *) NULL)
5331 {
5332 image=DestroyImageList(image);
5333 MngInfoFreeStruct(mng_info,&have_mng_structure);
5334 return((Image *) NULL);
5335 }
glennrp47b9dd52010-11-24 18:12:06 +00005336
cristy3ed852e2009-09-05 21:47:34 +00005337 image=SyncNextImageInList(image);
5338 }
glennrp0fe50b42010-11-16 03:52:51 +00005339
cristy3ed852e2009-09-05 21:47:34 +00005340 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005341
cristy3ed852e2009-09-05 21:47:34 +00005342 if (term_chunk_found)
5343 {
5344 image->start_loop=MagickTrue;
5345 image->iterations=mng_iterations;
5346 term_chunk_found=MagickFalse;
5347 }
glennrp0fe50b42010-11-16 03:52:51 +00005348
cristy3ed852e2009-09-05 21:47:34 +00005349 else
5350 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005351
cristy3ed852e2009-09-05 21:47:34 +00005352 image->columns=subframe_width;
5353 image->rows=subframe_height;
5354 image->page.width=subframe_width;
5355 image->page.height=subframe_height;
5356 image->page.x=mng_info->clip.left;
5357 image->page.y=mng_info->clip.top;
5358 image->background_color=mng_background_color;
5359 image->matte=MagickFalse;
5360 image->delay=0;
5361 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005362
cristy3ed852e2009-09-05 21:47:34 +00005363 if (logging != MagickFalse)
5364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005365 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005366 (double) mng_info->clip.left,(double) mng_info->clip.right,
5367 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005368 }
5369#endif
5370 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5371 continue;
5372 }
5373 if (memcmp(type,mng_CLIP,4) == 0)
5374 {
5375 unsigned int
5376 first_object,
5377 last_object;
5378
5379 /*
5380 Read CLIP.
5381 */
5382 first_object=(p[0] << 8) | p[1];
5383 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005384
cristy3ed852e2009-09-05 21:47:34 +00005385 for (i=(int) first_object; i <= (int) last_object; i++)
5386 {
5387 if (mng_info->exists[i] && !mng_info->frozen[i])
5388 {
5389 MngBox
5390 box;
5391
5392 box=mng_info->object_clip[i];
5393 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5394 }
5395 }
glennrp47b9dd52010-11-24 18:12:06 +00005396
cristy3ed852e2009-09-05 21:47:34 +00005397 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5398 continue;
5399 }
5400 if (memcmp(type,mng_SAVE,4) == 0)
5401 {
5402 for (i=1; i < MNG_MAX_OBJECTS; i++)
5403 if (mng_info->exists[i])
5404 {
5405 mng_info->frozen[i]=MagickTrue;
5406#ifdef MNG_OBJECT_BUFFERS
5407 if (mng_info->ob[i] != (MngBuffer *) NULL)
5408 mng_info->ob[i]->frozen=MagickTrue;
5409#endif
5410 }
glennrp0fe50b42010-11-16 03:52:51 +00005411
cristy3ed852e2009-09-05 21:47:34 +00005412 if (length)
5413 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005414
cristy3ed852e2009-09-05 21:47:34 +00005415 continue;
5416 }
5417
5418 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5419 {
glennrp47b9dd52010-11-24 18:12:06 +00005420 /* Read DISC or SEEK. */
5421
cristy3ed852e2009-09-05 21:47:34 +00005422 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5423 {
5424 for (i=1; i < MNG_MAX_OBJECTS; i++)
5425 MngInfoDiscardObject(mng_info,i);
5426 }
glennrp0fe50b42010-11-16 03:52:51 +00005427
cristy3ed852e2009-09-05 21:47:34 +00005428 else
5429 {
cristybb503372010-05-27 20:51:26 +00005430 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005431 j;
5432
cristybb503372010-05-27 20:51:26 +00005433 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005434 {
5435 i=p[j] << 8 | p[j+1];
5436 MngInfoDiscardObject(mng_info,i);
5437 }
5438 }
glennrp0fe50b42010-11-16 03:52:51 +00005439
cristy3ed852e2009-09-05 21:47:34 +00005440 if (length)
5441 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005442
cristy3ed852e2009-09-05 21:47:34 +00005443 continue;
5444 }
glennrp47b9dd52010-11-24 18:12:06 +00005445
cristy3ed852e2009-09-05 21:47:34 +00005446 if (memcmp(type,mng_MOVE,4) == 0)
5447 {
cristybb503372010-05-27 20:51:26 +00005448 size_t
cristy3ed852e2009-09-05 21:47:34 +00005449 first_object,
5450 last_object;
5451
glennrp47b9dd52010-11-24 18:12:06 +00005452 /* read MOVE */
5453
cristy3ed852e2009-09-05 21:47:34 +00005454 first_object=(p[0] << 8) | p[1];
5455 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005456 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005457 {
5458 if (mng_info->exists[i] && !mng_info->frozen[i])
5459 {
5460 MngPair
5461 new_pair;
5462
5463 MngPair
5464 old_pair;
5465
5466 old_pair.a=mng_info->x_off[i];
5467 old_pair.b=mng_info->y_off[i];
5468 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5469 mng_info->x_off[i]=new_pair.a;
5470 mng_info->y_off[i]=new_pair.b;
5471 }
5472 }
glennrp47b9dd52010-11-24 18:12:06 +00005473
cristy3ed852e2009-09-05 21:47:34 +00005474 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5475 continue;
5476 }
5477
5478 if (memcmp(type,mng_LOOP,4) == 0)
5479 {
cristybb503372010-05-27 20:51:26 +00005480 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005481 loop_level=chunk[0];
5482 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005483
5484 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005485 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005486
cristy3ed852e2009-09-05 21:47:34 +00005487 if (logging != MagickFalse)
5488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005489 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5490 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005491
cristy3ed852e2009-09-05 21:47:34 +00005492 if (loop_iters == 0)
5493 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005494
cristy3ed852e2009-09-05 21:47:34 +00005495 else
5496 {
5497 mng_info->loop_jump[loop_level]=TellBlob(image);
5498 mng_info->loop_count[loop_level]=loop_iters;
5499 }
glennrp0fe50b42010-11-16 03:52:51 +00005500
cristy3ed852e2009-09-05 21:47:34 +00005501 mng_info->loop_iteration[loop_level]=0;
5502 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5503 continue;
5504 }
glennrp47b9dd52010-11-24 18:12:06 +00005505
cristy3ed852e2009-09-05 21:47:34 +00005506 if (memcmp(type,mng_ENDL,4) == 0)
5507 {
5508 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005509
cristy3ed852e2009-09-05 21:47:34 +00005510 if (skipping_loop > 0)
5511 {
5512 if (skipping_loop == loop_level)
5513 {
5514 /*
5515 Found end of zero-iteration loop.
5516 */
5517 skipping_loop=(-1);
5518 mng_info->loop_active[loop_level]=0;
5519 }
5520 }
glennrp47b9dd52010-11-24 18:12:06 +00005521
cristy3ed852e2009-09-05 21:47:34 +00005522 else
5523 {
5524 if (mng_info->loop_active[loop_level] == 1)
5525 {
5526 mng_info->loop_count[loop_level]--;
5527 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005528
cristy3ed852e2009-09-05 21:47:34 +00005529 if (logging != MagickFalse)
5530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005531 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005532 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005533 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005534
cristy3ed852e2009-09-05 21:47:34 +00005535 if (mng_info->loop_count[loop_level] != 0)
5536 {
5537 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5538 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005539
cristy3ed852e2009-09-05 21:47:34 +00005540 if (offset < 0)
5541 ThrowReaderException(CorruptImageError,
5542 "ImproperImageHeader");
5543 }
glennrp47b9dd52010-11-24 18:12:06 +00005544
cristy3ed852e2009-09-05 21:47:34 +00005545 else
5546 {
5547 short
5548 last_level;
5549
5550 /*
5551 Finished loop.
5552 */
5553 mng_info->loop_active[loop_level]=0;
5554 last_level=(-1);
5555 for (i=0; i < loop_level; i++)
5556 if (mng_info->loop_active[i] == 1)
5557 last_level=(short) i;
5558 loop_level=last_level;
5559 }
5560 }
5561 }
glennrp47b9dd52010-11-24 18:12:06 +00005562
cristy3ed852e2009-09-05 21:47:34 +00005563 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5564 continue;
5565 }
glennrp47b9dd52010-11-24 18:12:06 +00005566
cristy3ed852e2009-09-05 21:47:34 +00005567 if (memcmp(type,mng_CLON,4) == 0)
5568 {
5569 if (mng_info->clon_warning == 0)
5570 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5571 CoderError,"CLON is not implemented yet","`%s'",
5572 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005573
cristy3ed852e2009-09-05 21:47:34 +00005574 mng_info->clon_warning++;
5575 }
glennrp47b9dd52010-11-24 18:12:06 +00005576
cristy3ed852e2009-09-05 21:47:34 +00005577 if (memcmp(type,mng_MAGN,4) == 0)
5578 {
5579 png_uint_16
5580 magn_first,
5581 magn_last,
5582 magn_mb,
5583 magn_ml,
5584 magn_mr,
5585 magn_mt,
5586 magn_mx,
5587 magn_my,
5588 magn_methx,
5589 magn_methy;
5590
5591 if (length > 1)
5592 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005593
cristy3ed852e2009-09-05 21:47:34 +00005594 else
5595 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005596
cristy3ed852e2009-09-05 21:47:34 +00005597 if (length > 3)
5598 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005599
cristy3ed852e2009-09-05 21:47:34 +00005600 else
5601 magn_last=magn_first;
5602#ifndef MNG_OBJECT_BUFFERS
5603 if (magn_first || magn_last)
5604 if (mng_info->magn_warning == 0)
5605 {
5606 (void) ThrowMagickException(&image->exception,
5607 GetMagickModule(),CoderError,
5608 "MAGN is not implemented yet for nonzero objects",
5609 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005610
cristy3ed852e2009-09-05 21:47:34 +00005611 mng_info->magn_warning++;
5612 }
5613#endif
5614 if (length > 4)
5615 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005616
cristy3ed852e2009-09-05 21:47:34 +00005617 else
5618 magn_methx=0;
5619
5620 if (length > 6)
5621 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005622
cristy3ed852e2009-09-05 21:47:34 +00005623 else
5624 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005625
cristy3ed852e2009-09-05 21:47:34 +00005626 if (magn_mx == 0)
5627 magn_mx=1;
5628
5629 if (length > 8)
5630 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005631
cristy3ed852e2009-09-05 21:47:34 +00005632 else
5633 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005634
cristy3ed852e2009-09-05 21:47:34 +00005635 if (magn_my == 0)
5636 magn_my=1;
5637
5638 if (length > 10)
5639 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005640
cristy3ed852e2009-09-05 21:47:34 +00005641 else
5642 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005643
cristy3ed852e2009-09-05 21:47:34 +00005644 if (magn_ml == 0)
5645 magn_ml=1;
5646
5647 if (length > 12)
5648 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005649
cristy3ed852e2009-09-05 21:47:34 +00005650 else
5651 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005652
cristy3ed852e2009-09-05 21:47:34 +00005653 if (magn_mr == 0)
5654 magn_mr=1;
5655
5656 if (length > 14)
5657 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005658
cristy3ed852e2009-09-05 21:47:34 +00005659 else
5660 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005661
cristy3ed852e2009-09-05 21:47:34 +00005662 if (magn_mt == 0)
5663 magn_mt=1;
5664
5665 if (length > 16)
5666 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005667
cristy3ed852e2009-09-05 21:47:34 +00005668 else
5669 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005670
cristy3ed852e2009-09-05 21:47:34 +00005671 if (magn_mb == 0)
5672 magn_mb=1;
5673
5674 if (length > 17)
5675 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005676
cristy3ed852e2009-09-05 21:47:34 +00005677 else
5678 magn_methy=magn_methx;
5679
glennrp47b9dd52010-11-24 18:12:06 +00005680
cristy3ed852e2009-09-05 21:47:34 +00005681 if (magn_methx > 5 || magn_methy > 5)
5682 if (mng_info->magn_warning == 0)
5683 {
5684 (void) ThrowMagickException(&image->exception,
5685 GetMagickModule(),CoderError,
5686 "Unknown MAGN method in MNG datastream","`%s'",
5687 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005688
cristy3ed852e2009-09-05 21:47:34 +00005689 mng_info->magn_warning++;
5690 }
5691#ifdef MNG_OBJECT_BUFFERS
5692 /* Magnify existing objects in the range magn_first to magn_last */
5693#endif
5694 if (magn_first == 0 || magn_last == 0)
5695 {
5696 /* Save the magnification factors for object 0 */
5697 mng_info->magn_mb=magn_mb;
5698 mng_info->magn_ml=magn_ml;
5699 mng_info->magn_mr=magn_mr;
5700 mng_info->magn_mt=magn_mt;
5701 mng_info->magn_mx=magn_mx;
5702 mng_info->magn_my=magn_my;
5703 mng_info->magn_methx=magn_methx;
5704 mng_info->magn_methy=magn_methy;
5705 }
5706 }
glennrp47b9dd52010-11-24 18:12:06 +00005707
cristy3ed852e2009-09-05 21:47:34 +00005708 if (memcmp(type,mng_PAST,4) == 0)
5709 {
5710 if (mng_info->past_warning == 0)
5711 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5712 CoderError,"PAST is not implemented yet","`%s'",
5713 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005714
cristy3ed852e2009-09-05 21:47:34 +00005715 mng_info->past_warning++;
5716 }
glennrp47b9dd52010-11-24 18:12:06 +00005717
cristy3ed852e2009-09-05 21:47:34 +00005718 if (memcmp(type,mng_SHOW,4) == 0)
5719 {
5720 if (mng_info->show_warning == 0)
5721 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5722 CoderError,"SHOW is not implemented yet","`%s'",
5723 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005724
cristy3ed852e2009-09-05 21:47:34 +00005725 mng_info->show_warning++;
5726 }
glennrp47b9dd52010-11-24 18:12:06 +00005727
cristy3ed852e2009-09-05 21:47:34 +00005728 if (memcmp(type,mng_sBIT,4) == 0)
5729 {
5730 if (length < 4)
5731 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005732
cristy3ed852e2009-09-05 21:47:34 +00005733 else
5734 {
5735 mng_info->global_sbit.gray=p[0];
5736 mng_info->global_sbit.red=p[0];
5737 mng_info->global_sbit.green=p[1];
5738 mng_info->global_sbit.blue=p[2];
5739 mng_info->global_sbit.alpha=p[3];
5740 mng_info->have_global_sbit=MagickTrue;
5741 }
5742 }
5743 if (memcmp(type,mng_pHYs,4) == 0)
5744 {
5745 if (length > 8)
5746 {
5747 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005748 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005749 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005750 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005751 mng_info->global_phys_unit_type=p[8];
5752 mng_info->have_global_phys=MagickTrue;
5753 }
glennrp47b9dd52010-11-24 18:12:06 +00005754
cristy3ed852e2009-09-05 21:47:34 +00005755 else
5756 mng_info->have_global_phys=MagickFalse;
5757 }
5758 if (memcmp(type,mng_pHYg,4) == 0)
5759 {
5760 if (mng_info->phyg_warning == 0)
5761 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5762 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005763
cristy3ed852e2009-09-05 21:47:34 +00005764 mng_info->phyg_warning++;
5765 }
5766 if (memcmp(type,mng_BASI,4) == 0)
5767 {
5768 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005769
cristy3ed852e2009-09-05 21:47:34 +00005770 if (mng_info->basi_warning == 0)
5771 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5772 CoderError,"BASI is not implemented yet","`%s'",
5773 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005774
cristy3ed852e2009-09-05 21:47:34 +00005775 mng_info->basi_warning++;
5776#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005777 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005778 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005779 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005780 (p[6] << 8) | p[7]);
5781 basi_color_type=p[8];
5782 basi_compression_method=p[9];
5783 basi_filter_type=p[10];
5784 basi_interlace_method=p[11];
5785 if (length > 11)
5786 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005787
cristy3ed852e2009-09-05 21:47:34 +00005788 else
5789 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005790
cristy3ed852e2009-09-05 21:47:34 +00005791 if (length > 13)
5792 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005793
cristy3ed852e2009-09-05 21:47:34 +00005794 else
5795 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005796
cristy3ed852e2009-09-05 21:47:34 +00005797 if (length > 15)
5798 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005799
cristy3ed852e2009-09-05 21:47:34 +00005800 else
5801 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005802
cristy3ed852e2009-09-05 21:47:34 +00005803 if (length > 17)
5804 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005805
cristy3ed852e2009-09-05 21:47:34 +00005806 else
5807 {
5808 if (basi_sample_depth == 16)
5809 basi_alpha=65535L;
5810 else
5811 basi_alpha=255;
5812 }
glennrp47b9dd52010-11-24 18:12:06 +00005813
cristy3ed852e2009-09-05 21:47:34 +00005814 if (length > 19)
5815 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 else
5818 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005819
cristy3ed852e2009-09-05 21:47:34 +00005820#endif
5821 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5822 continue;
5823 }
glennrp47b9dd52010-11-24 18:12:06 +00005824
cristy3ed852e2009-09-05 21:47:34 +00005825 if (memcmp(type,mng_IHDR,4)
5826#if defined(JNG_SUPPORTED)
5827 && memcmp(type,mng_JHDR,4)
5828#endif
5829 )
5830 {
5831 /* Not an IHDR or JHDR chunk */
5832 if (length)
5833 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristy3ed852e2009-09-05 21:47:34 +00005835 continue;
5836 }
5837/* Process IHDR */
5838 if (logging != MagickFalse)
5839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5840 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005841
cristy3ed852e2009-09-05 21:47:34 +00005842 mng_info->exists[object_id]=MagickTrue;
5843 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005844
cristy3ed852e2009-09-05 21:47:34 +00005845 if (mng_info->invisible[object_id])
5846 {
5847 if (logging != MagickFalse)
5848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5849 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005850
cristy3ed852e2009-09-05 21:47:34 +00005851 skip_to_iend=MagickTrue;
5852 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5853 continue;
5854 }
5855#if defined(MNG_INSERT_LAYERS)
5856 if (length < 8)
5857 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005858
cristy8182b072010-05-30 20:10:53 +00005859 image_width=(size_t) mng_get_long(p);
5860 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005861#endif
5862 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5863
5864 /*
5865 Insert a transparent background layer behind the entire animation
5866 if it is not full screen.
5867 */
5868#if defined(MNG_INSERT_LAYERS)
5869 if (insert_layers && mng_type && first_mng_object)
5870 {
5871 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5872 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005873 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005874 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005875 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005876 {
5877 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5878 {
5879 /*
5880 Allocate next image structure.
5881 */
5882 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005883
cristy3ed852e2009-09-05 21:47:34 +00005884 if (GetNextImageInList(image) == (Image *) NULL)
5885 {
5886 image=DestroyImageList(image);
5887 MngInfoFreeStruct(mng_info,&have_mng_structure);
5888 return((Image *) NULL);
5889 }
glennrp47b9dd52010-11-24 18:12:06 +00005890
cristy3ed852e2009-09-05 21:47:34 +00005891 image=SyncNextImageInList(image);
5892 }
5893 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005894
cristy3ed852e2009-09-05 21:47:34 +00005895 if (term_chunk_found)
5896 {
5897 image->start_loop=MagickTrue;
5898 image->iterations=mng_iterations;
5899 term_chunk_found=MagickFalse;
5900 }
glennrp47b9dd52010-11-24 18:12:06 +00005901
cristy3ed852e2009-09-05 21:47:34 +00005902 else
5903 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005904
5905 /* Make a background rectangle. */
5906
cristy3ed852e2009-09-05 21:47:34 +00005907 image->delay=0;
5908 image->columns=mng_info->mng_width;
5909 image->rows=mng_info->mng_height;
5910 image->page.width=mng_info->mng_width;
5911 image->page.height=mng_info->mng_height;
5912 image->page.x=0;
5913 image->page.y=0;
5914 image->background_color=mng_background_color;
5915 (void) SetImageBackgroundColor(image);
5916 if (logging != MagickFalse)
5917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005918 " Inserted transparent background layer, W=%.20g, H=%.20g",
5919 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005920 }
5921 }
5922 /*
5923 Insert a background layer behind the upcoming image if
5924 framing_mode is 3, and we haven't already inserted one.
5925 */
5926 if (insert_layers && (mng_info->framing_mode == 3) &&
5927 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5928 (simplicity & 0x08)))
5929 {
5930 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5931 {
5932 /*
5933 Allocate next image structure.
5934 */
5935 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005936
cristy3ed852e2009-09-05 21:47:34 +00005937 if (GetNextImageInList(image) == (Image *) NULL)
5938 {
5939 image=DestroyImageList(image);
5940 MngInfoFreeStruct(mng_info,&have_mng_structure);
5941 return((Image *) NULL);
5942 }
glennrp47b9dd52010-11-24 18:12:06 +00005943
cristy3ed852e2009-09-05 21:47:34 +00005944 image=SyncNextImageInList(image);
5945 }
glennrp0fe50b42010-11-16 03:52:51 +00005946
cristy3ed852e2009-09-05 21:47:34 +00005947 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005948
cristy3ed852e2009-09-05 21:47:34 +00005949 if (term_chunk_found)
5950 {
5951 image->start_loop=MagickTrue;
5952 image->iterations=mng_iterations;
5953 term_chunk_found=MagickFalse;
5954 }
glennrp0fe50b42010-11-16 03:52:51 +00005955
cristy3ed852e2009-09-05 21:47:34 +00005956 else
5957 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005958
cristy3ed852e2009-09-05 21:47:34 +00005959 image->delay=0;
5960 image->columns=subframe_width;
5961 image->rows=subframe_height;
5962 image->page.width=subframe_width;
5963 image->page.height=subframe_height;
5964 image->page.x=mng_info->clip.left;
5965 image->page.y=mng_info->clip.top;
5966 image->background_color=mng_background_color;
5967 image->matte=MagickFalse;
5968 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005969
cristy3ed852e2009-09-05 21:47:34 +00005970 if (logging != MagickFalse)
5971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005972 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005973 (double) mng_info->clip.left,(double) mng_info->clip.right,
5974 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005975 }
5976#endif /* MNG_INSERT_LAYERS */
5977 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005978
cristy3ed852e2009-09-05 21:47:34 +00005979 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5980 {
5981 /*
5982 Allocate next image structure.
5983 */
5984 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005985
cristy3ed852e2009-09-05 21:47:34 +00005986 if (GetNextImageInList(image) == (Image *) NULL)
5987 {
5988 image=DestroyImageList(image);
5989 MngInfoFreeStruct(mng_info,&have_mng_structure);
5990 return((Image *) NULL);
5991 }
glennrp47b9dd52010-11-24 18:12:06 +00005992
cristy3ed852e2009-09-05 21:47:34 +00005993 image=SyncNextImageInList(image);
5994 }
5995 mng_info->image=image;
5996 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5997 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005998
cristy3ed852e2009-09-05 21:47:34 +00005999 if (status == MagickFalse)
6000 break;
glennrp0fe50b42010-11-16 03:52:51 +00006001
cristy3ed852e2009-09-05 21:47:34 +00006002 if (term_chunk_found)
6003 {
6004 image->start_loop=MagickTrue;
6005 term_chunk_found=MagickFalse;
6006 }
glennrp0fe50b42010-11-16 03:52:51 +00006007
cristy3ed852e2009-09-05 21:47:34 +00006008 else
6009 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006010
cristy3ed852e2009-09-05 21:47:34 +00006011 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6012 {
6013 image->delay=frame_delay;
6014 frame_delay=default_frame_delay;
6015 }
glennrp0fe50b42010-11-16 03:52:51 +00006016
cristy3ed852e2009-09-05 21:47:34 +00006017 else
6018 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006019
cristy3ed852e2009-09-05 21:47:34 +00006020 image->page.width=mng_info->mng_width;
6021 image->page.height=mng_info->mng_height;
6022 image->page.x=mng_info->x_off[object_id];
6023 image->page.y=mng_info->y_off[object_id];
6024 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006025
cristy3ed852e2009-09-05 21:47:34 +00006026 /*
6027 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6028 */
glennrp47b9dd52010-11-24 18:12:06 +00006029
cristy3ed852e2009-09-05 21:47:34 +00006030 if (logging != MagickFalse)
6031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6032 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6033 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006034
cristybb503372010-05-27 20:51:26 +00006035 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006036
cristy3ed852e2009-09-05 21:47:34 +00006037 if (offset < 0)
6038 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6039 }
6040
6041 previous=image;
6042 mng_info->image=image;
6043 mng_info->mng_type=mng_type;
6044 mng_info->object_id=object_id;
6045
6046 if (memcmp(type,mng_IHDR,4) == 0)
6047 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006048
cristy3ed852e2009-09-05 21:47:34 +00006049#if defined(JNG_SUPPORTED)
6050 else
6051 image=ReadOneJNGImage(mng_info,image_info,exception);
6052#endif
6053
6054 if (image == (Image *) NULL)
6055 {
6056 if (IsImageObject(previous) != MagickFalse)
6057 {
6058 (void) DestroyImageList(previous);
6059 (void) CloseBlob(previous);
6060 }
glennrp47b9dd52010-11-24 18:12:06 +00006061
cristy3ed852e2009-09-05 21:47:34 +00006062 MngInfoFreeStruct(mng_info,&have_mng_structure);
6063 return((Image *) NULL);
6064 }
glennrp0fe50b42010-11-16 03:52:51 +00006065
cristy3ed852e2009-09-05 21:47:34 +00006066 if (image->columns == 0 || image->rows == 0)
6067 {
6068 (void) CloseBlob(image);
6069 image=DestroyImageList(image);
6070 MngInfoFreeStruct(mng_info,&have_mng_structure);
6071 return((Image *) NULL);
6072 }
glennrp0fe50b42010-11-16 03:52:51 +00006073
cristy3ed852e2009-09-05 21:47:34 +00006074 mng_info->image=image;
6075
6076 if (mng_type)
6077 {
6078 MngBox
6079 crop_box;
6080
6081 if (mng_info->magn_methx || mng_info->magn_methy)
6082 {
6083 png_uint_32
6084 magnified_height,
6085 magnified_width;
6086
6087 if (logging != MagickFalse)
6088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6089 " Processing MNG MAGN chunk");
6090
6091 if (mng_info->magn_methx == 1)
6092 {
6093 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006094
cristy3ed852e2009-09-05 21:47:34 +00006095 if (image->columns > 1)
6096 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006097
cristy3ed852e2009-09-05 21:47:34 +00006098 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006099 magnified_width += (png_uint_32)
6100 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006101 }
glennrp47b9dd52010-11-24 18:12:06 +00006102
cristy3ed852e2009-09-05 21:47:34 +00006103 else
6104 {
cristy4e5bc842010-06-09 13:56:01 +00006105 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006106
cristy3ed852e2009-09-05 21:47:34 +00006107 if (image->columns > 1)
6108 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006109
cristy3ed852e2009-09-05 21:47:34 +00006110 if (image->columns > 2)
6111 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006112
cristy3ed852e2009-09-05 21:47:34 +00006113 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006114 magnified_width += (png_uint_32)
6115 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006116 }
glennrp47b9dd52010-11-24 18:12:06 +00006117
cristy3ed852e2009-09-05 21:47:34 +00006118 if (mng_info->magn_methy == 1)
6119 {
6120 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006121
cristy3ed852e2009-09-05 21:47:34 +00006122 if (image->rows > 1)
6123 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006124
cristy3ed852e2009-09-05 21:47:34 +00006125 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006126 magnified_height += (png_uint_32)
6127 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006128 }
glennrp47b9dd52010-11-24 18:12:06 +00006129
cristy3ed852e2009-09-05 21:47:34 +00006130 else
6131 {
cristy4e5bc842010-06-09 13:56:01 +00006132 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006133
cristy3ed852e2009-09-05 21:47:34 +00006134 if (image->rows > 1)
6135 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006136
cristy3ed852e2009-09-05 21:47:34 +00006137 if (image->rows > 2)
6138 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006139
cristy3ed852e2009-09-05 21:47:34 +00006140 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006141 magnified_height += (png_uint_32)
6142 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006143 }
glennrp47b9dd52010-11-24 18:12:06 +00006144
cristy3ed852e2009-09-05 21:47:34 +00006145 if (magnified_height > image->rows ||
6146 magnified_width > image->columns)
6147 {
6148 Image
6149 *large_image;
6150
6151 int
6152 yy;
6153
cristybb503372010-05-27 20:51:26 +00006154 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006155 m,
6156 y;
6157
cristybb503372010-05-27 20:51:26 +00006158 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006159 x;
6160
6161 register PixelPacket
6162 *n,
6163 *q;
6164
6165 PixelPacket
6166 *next,
6167 *prev;
6168
6169 png_uint_16
6170 magn_methx,
6171 magn_methy;
6172
glennrp47b9dd52010-11-24 18:12:06 +00006173 /* Allocate next image structure. */
6174
cristy3ed852e2009-09-05 21:47:34 +00006175 if (logging != MagickFalse)
6176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6177 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006178
cristy3ed852e2009-09-05 21:47:34 +00006179 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00006180
cristy3ed852e2009-09-05 21:47:34 +00006181 if (GetNextImageInList(image) == (Image *) NULL)
6182 {
6183 image=DestroyImageList(image);
6184 MngInfoFreeStruct(mng_info,&have_mng_structure);
6185 return((Image *) NULL);
6186 }
6187
6188 large_image=SyncNextImageInList(image);
6189
6190 large_image->columns=magnified_width;
6191 large_image->rows=magnified_height;
6192
6193 magn_methx=mng_info->magn_methx;
6194 magn_methy=mng_info->magn_methy;
6195
glennrp3faa9a32011-04-23 14:00:25 +00006196#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006197#define QM unsigned short
6198 if (magn_methx != 1 || magn_methy != 1)
6199 {
6200 /*
6201 Scale pixels to unsigned shorts to prevent
6202 overflow of intermediate values of interpolations
6203 */
cristybb503372010-05-27 20:51:26 +00006204 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006205 {
6206 q=GetAuthenticPixels(image,0,y,image->columns,1,
6207 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006208
cristybb503372010-05-27 20:51:26 +00006209 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006210 {
glennrp7c7b3152011-04-26 04:01:27 +00006211 SetRedPixelComponent(q,ScaleQuantumToShort(
cristy3b6fd2e2011-05-20 12:53:50 +00006212 GetRedPixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006213 SetGreenPixelComponent(q,ScaleQuantumToShort(
cristy3b6fd2e2011-05-20 12:53:50 +00006214 GetGreenPixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006215 SetBluePixelComponent(q,ScaleQuantumToShort(
cristy3b6fd2e2011-05-20 12:53:50 +00006216 GetBluePixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006217 SetOpacityPixelComponent(q,ScaleQuantumToShort(
cristy3b6fd2e2011-05-20 12:53:50 +00006218 GetOpacityPixelComponent(q)));
cristy3ed852e2009-09-05 21:47:34 +00006219 q++;
6220 }
glennrp47b9dd52010-11-24 18:12:06 +00006221
cristy3ed852e2009-09-05 21:47:34 +00006222 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6223 break;
6224 }
6225 }
6226#else
6227#define QM Quantum
6228#endif
6229
6230 if (image->matte != MagickFalse)
6231 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006232
cristy3ed852e2009-09-05 21:47:34 +00006233 else
6234 {
6235 large_image->background_color.opacity=OpaqueOpacity;
6236 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006237
cristy3ed852e2009-09-05 21:47:34 +00006238 if (magn_methx == 4)
6239 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006240
cristy3ed852e2009-09-05 21:47:34 +00006241 if (magn_methx == 5)
6242 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006243
cristy3ed852e2009-09-05 21:47:34 +00006244 if (magn_methy == 4)
6245 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006246
cristy3ed852e2009-09-05 21:47:34 +00006247 if (magn_methy == 5)
6248 magn_methy=3;
6249 }
6250
6251 /* magnify the rows into the right side of the large image */
6252
6253 if (logging != MagickFalse)
6254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006255 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006256 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006257 yy=0;
6258 length=(size_t) image->columns;
6259 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
6260 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006261
cristy3ed852e2009-09-05 21:47:34 +00006262 if ((prev == (PixelPacket *) NULL) ||
6263 (next == (PixelPacket *) NULL))
6264 {
6265 image=DestroyImageList(image);
6266 MngInfoFreeStruct(mng_info,&have_mng_structure);
6267 ThrowReaderException(ResourceLimitError,
6268 "MemoryAllocationFailed");
6269 }
glennrp47b9dd52010-11-24 18:12:06 +00006270
cristy3ed852e2009-09-05 21:47:34 +00006271 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6272 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006273
cristybb503372010-05-27 20:51:26 +00006274 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006275 {
6276 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006277 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006278
cristybb503372010-05-27 20:51:26 +00006279 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6280 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006281
cristybb503372010-05-27 20:51:26 +00006282 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6283 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006284
cristybb503372010-05-27 20:51:26 +00006285 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006286 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006287
cristy3ed852e2009-09-05 21:47:34 +00006288 else
cristybb503372010-05-27 20:51:26 +00006289 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006290
cristy3ed852e2009-09-05 21:47:34 +00006291 n=prev;
6292 prev=next;
6293 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006294
cristybb503372010-05-27 20:51:26 +00006295 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006296 {
6297 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6298 exception);
6299 (void) CopyMagickMemory(next,n,length);
6300 }
glennrp47b9dd52010-11-24 18:12:06 +00006301
cristy3ed852e2009-09-05 21:47:34 +00006302 for (i=0; i < m; i++, yy++)
6303 {
6304 register PixelPacket
6305 *pixels;
6306
cristybb503372010-05-27 20:51:26 +00006307 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006308 pixels=prev;
6309 n=next;
6310 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006311 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00006312 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00006313
cristybb503372010-05-27 20:51:26 +00006314 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006315 {
glennrpfd05d622011-02-25 04:10:33 +00006316 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006317 /*
6318 if (image->storage_class == PseudoClass)
6319 {
6320 }
6321 */
6322
6323 if (magn_methy <= 1)
6324 {
glennrpbb4f99d2011-05-22 11:13:17 +00006325 /* replicate previous */
cristy8d74fc12011-05-25 23:02:47 +00006326 SetRGBOPixelComponents(q,(pixels));
cristy3ed852e2009-09-05 21:47:34 +00006327 }
glennrp47b9dd52010-11-24 18:12:06 +00006328
cristy3ed852e2009-09-05 21:47:34 +00006329 else if (magn_methy == 2 || magn_methy == 4)
6330 {
6331 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006332 {
cristy8d74fc12011-05-25 23:02:47 +00006333 SetRGBOPixelComponents(q,(pixels));
glennrpbb4f99d2011-05-22 11:13:17 +00006334 }
glennrp47b9dd52010-11-24 18:12:06 +00006335
cristy3ed852e2009-09-05 21:47:34 +00006336 else
6337 {
6338 /* Interpolate */
glennrpbb4f99d2011-05-22 11:13:17 +00006339 SetRedPixelComponent(q,
6340 ((QM) (((ssize_t)
6341 (2*i*(GetRedPixelComponent(n)
6342 -GetRedPixelComponent(pixels)+m))/
6343 ((ssize_t) (m*2))
6344 +GetRedPixelComponent(pixels)))));
6345 SetGreenPixelComponent(q,
6346 ((QM) (((ssize_t)
6347 (2*i*(GetGreenPixelComponent(n)
6348 -GetGreenPixelComponent(pixels)+m))/
6349 ((ssize_t) (m*2))
6350 +GetGreenPixelComponent(pixels)))));
6351 SetBluePixelComponent(q,
6352 ((QM) (((ssize_t)
6353 (2*i*(GetBluePixelComponent(n)
6354 -GetBluePixelComponent(pixels)+m))/
6355 ((ssize_t) (m*2))
6356 +GetBluePixelComponent(pixels)))));
glennrp47b9dd52010-11-24 18:12:06 +00006357
cristy3ed852e2009-09-05 21:47:34 +00006358 if (image->matte != MagickFalse)
glennrpbb4f99d2011-05-22 11:13:17 +00006359 SetOpacityPixelComponent(q,
6360 ((QM) (((ssize_t)
6361 (2*i*(GetOpacityPixelComponent(n)
6362 -GetOpacityPixelComponent(pixels)+m))
6363 /((ssize_t) (m*2))+
6364 GetOpacityPixelComponent(pixels)))));
cristy3ed852e2009-09-05 21:47:34 +00006365 }
glennrp47b9dd52010-11-24 18:12:06 +00006366
cristy3ed852e2009-09-05 21:47:34 +00006367 if (magn_methy == 4)
6368 {
6369 /* Replicate nearest */
6370 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006371 SetOpacityPixelComponent(q,
6372 (*pixels).opacity+0);
cristy3ed852e2009-09-05 21:47:34 +00006373 else
glennrpbb4f99d2011-05-22 11:13:17 +00006374 SetOpacityPixelComponent(q,
6375 (*n).opacity+0);
cristy3ed852e2009-09-05 21:47:34 +00006376 }
6377 }
glennrp47b9dd52010-11-24 18:12:06 +00006378
cristy3ed852e2009-09-05 21:47:34 +00006379 else /* if (magn_methy == 3 || magn_methy == 5) */
6380 {
6381 /* Replicate nearest */
6382 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006383 {
cristy8d74fc12011-05-25 23:02:47 +00006384 SetRGBOPixelComponents(q,(pixels));
glennrpbb4f99d2011-05-22 11:13:17 +00006385 }
glennrp47b9dd52010-11-24 18:12:06 +00006386
cristy3ed852e2009-09-05 21:47:34 +00006387 else
glennrpbb4f99d2011-05-22 11:13:17 +00006388 {
cristy8d74fc12011-05-25 23:02:47 +00006389 SetRGBOPixelComponents(q,(n));
glennrpbb4f99d2011-05-22 11:13:17 +00006390 }
glennrp47b9dd52010-11-24 18:12:06 +00006391
cristy3ed852e2009-09-05 21:47:34 +00006392 if (magn_methy == 5)
6393 {
glennrpbb4f99d2011-05-22 11:13:17 +00006394 SetOpacityPixelComponent(q,
6395 (QM) (((ssize_t) (2*i*
6396 (GetOpacityPixelComponent(n)
6397 -GetOpacityPixelComponent(pixels))
6398 +m))/((ssize_t) (m*2))
6399 +GetOpacityPixelComponent(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006400 }
6401 }
6402 n++;
6403 q++;
6404 pixels++;
6405 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006406
cristy3ed852e2009-09-05 21:47:34 +00006407 if (SyncAuthenticPixels(large_image,exception) == 0)
6408 break;
glennrp47b9dd52010-11-24 18:12:06 +00006409
cristy3ed852e2009-09-05 21:47:34 +00006410 } /* i */
6411 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006412
cristy3ed852e2009-09-05 21:47:34 +00006413 prev=(PixelPacket *) RelinquishMagickMemory(prev);
6414 next=(PixelPacket *) RelinquishMagickMemory(next);
6415
6416 length=image->columns;
6417
6418 if (logging != MagickFalse)
6419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6420 " Delete original image");
6421
6422 DeleteImageFromList(&image);
6423
6424 image=large_image;
6425
6426 mng_info->image=image;
6427
6428 /* magnify the columns */
6429 if (logging != MagickFalse)
6430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006431 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006432
cristybb503372010-05-27 20:51:26 +00006433 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006434 {
6435 register PixelPacket
6436 *pixels;
6437
6438 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6439 pixels=q+(image->columns-length);
6440 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00006441
cristybb503372010-05-27 20:51:26 +00006442 for (x=(ssize_t) (image->columns-length);
6443 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006444 {
glennrp7c7b3152011-04-26 04:01:27 +00006445 /* To do: Rewrite using Get/Set***PixelComponent() */
6446
cristybb503372010-05-27 20:51:26 +00006447 if (x == (ssize_t) (image->columns-length))
6448 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006449
cristybb503372010-05-27 20:51:26 +00006450 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6451 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006452
cristybb503372010-05-27 20:51:26 +00006453 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6454 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006455
cristybb503372010-05-27 20:51:26 +00006456 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006457 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006458
cristy3ed852e2009-09-05 21:47:34 +00006459 else
cristybb503372010-05-27 20:51:26 +00006460 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006461
cristy3ed852e2009-09-05 21:47:34 +00006462 for (i=0; i < m; i++)
6463 {
6464 if (magn_methx <= 1)
6465 {
6466 /* replicate previous */
cristy8d74fc12011-05-25 23:02:47 +00006467 SetRGBOPixelComponents(q,(pixels));
cristy3ed852e2009-09-05 21:47:34 +00006468 }
glennrp47b9dd52010-11-24 18:12:06 +00006469
cristy3ed852e2009-09-05 21:47:34 +00006470 else if (magn_methx == 2 || magn_methx == 4)
6471 {
6472 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006473 {
cristy8d74fc12011-05-25 23:02:47 +00006474 SetRGBOPixelComponents(q,(pixels));
glennrpbb4f99d2011-05-22 11:13:17 +00006475 }
glennrp47b9dd52010-11-24 18:12:06 +00006476
glennrpbb4f99d2011-05-22 11:13:17 +00006477 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00006478 else
6479 {
6480 /* Interpolate */
glennrpbb4f99d2011-05-22 11:13:17 +00006481 SetRedPixelComponent(q,
6482 (QM) ((2*i*(
6483 GetRedPixelComponent(n)
6484 -GetRedPixelComponent(pixels))+m)
6485 /((ssize_t) (m*2))+
6486 GetRedPixelComponent(pixels)));
6487
6488 SetGreenPixelComponent(q,
6489 (QM) ((2*i*(
6490 GetGreenPixelComponent(n)
6491 -GetGreenPixelComponent(pixels))+m)
6492 /((ssize_t) (m*2))+
6493 GetGreenPixelComponent(pixels)));
6494
6495 SetBluePixelComponent(q,
6496 (QM) ((2*i*(
6497 GetBluePixelComponent(n)
6498 -GetBluePixelComponent(pixels))+m)
6499 /((ssize_t) (m*2))+
6500 GetBluePixelComponent(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006501 if (image->matte != MagickFalse)
glennrpbb4f99d2011-05-22 11:13:17 +00006502 SetOpacityPixelComponent(q,
6503 (QM) ((2*i*(
6504 GetOpacityPixelComponent(n)
6505 -GetOpacityPixelComponent(pixels))+m)
6506 /((ssize_t) (m*2))+
6507 GetOpacityPixelComponent(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006508 }
glennrp47b9dd52010-11-24 18:12:06 +00006509
cristy3ed852e2009-09-05 21:47:34 +00006510 if (magn_methx == 4)
6511 {
6512 /* Replicate nearest */
6513 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006514 {
6515 SetOpacityPixelComponent(q,
6516 GetOpacityPixelComponent(pixels)+0);
6517 }
cristy3ed852e2009-09-05 21:47:34 +00006518 else
glennrpbb4f99d2011-05-22 11:13:17 +00006519 {
6520 SetOpacityPixelComponent(q,
6521 GetOpacityPixelComponent(n)+0);
6522 }
cristy3ed852e2009-09-05 21:47:34 +00006523 }
6524 }
glennrp47b9dd52010-11-24 18:12:06 +00006525
cristy3ed852e2009-09-05 21:47:34 +00006526 else /* if (magn_methx == 3 || magn_methx == 5) */
6527 {
6528 /* Replicate nearest */
6529 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006530 {
cristy8d74fc12011-05-25 23:02:47 +00006531 SetRGBOPixelComponents(q,(pixels));
glennrpbb4f99d2011-05-22 11:13:17 +00006532 }
glennrp47b9dd52010-11-24 18:12:06 +00006533
cristy3ed852e2009-09-05 21:47:34 +00006534 else
glennrpbb4f99d2011-05-22 11:13:17 +00006535 {
cristy8d74fc12011-05-25 23:02:47 +00006536 SetRGBOPixelComponents(q,(n));
glennrpbb4f99d2011-05-22 11:13:17 +00006537 }
glennrp47b9dd52010-11-24 18:12:06 +00006538
cristy3ed852e2009-09-05 21:47:34 +00006539 if (magn_methx == 5)
6540 {
6541 /* Interpolate */
glennrpbb4f99d2011-05-22 11:13:17 +00006542 SetOpacityPixelComponent(q,
6543 (QM) ((2*i*( GetOpacityPixelComponent(n)
6544 -GetOpacityPixelComponent(pixels))+m)/
6545 ((ssize_t) (m*2))
6546 +GetOpacityPixelComponent(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006547 }
6548 }
6549 q++;
6550 }
6551 n++;
6552 p++;
6553 }
glennrp47b9dd52010-11-24 18:12:06 +00006554
cristy3ed852e2009-09-05 21:47:34 +00006555 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6556 break;
6557 }
glennrp3faa9a32011-04-23 14:00:25 +00006558#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006559 if (magn_methx != 1 || magn_methy != 1)
6560 {
6561 /*
6562 Rescale pixels to Quantum
6563 */
cristybb503372010-05-27 20:51:26 +00006564 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006565 {
6566 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006567
cristybb503372010-05-27 20:51:26 +00006568 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006569 {
glennrp7c7b3152011-04-26 04:01:27 +00006570 SetRedPixelComponent(q,ScaleShortToQuantum(
cristy3b6fd2e2011-05-20 12:53:50 +00006571 GetRedPixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006572 SetGreenPixelComponent(q,ScaleShortToQuantum(
cristy3b6fd2e2011-05-20 12:53:50 +00006573 GetGreenPixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006574 SetBluePixelComponent(q,ScaleShortToQuantum(
cristy3b6fd2e2011-05-20 12:53:50 +00006575 GetBluePixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006576 SetOpacityPixelComponent(q,ScaleShortToQuantum(
cristy3b6fd2e2011-05-20 12:53:50 +00006577 GetOpacityPixelComponent(q)));
cristy3ed852e2009-09-05 21:47:34 +00006578 q++;
6579 }
glennrp47b9dd52010-11-24 18:12:06 +00006580
cristy3ed852e2009-09-05 21:47:34 +00006581 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6582 break;
6583 }
6584 }
6585#endif
6586 if (logging != MagickFalse)
6587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6588 " Finished MAGN processing");
6589 }
6590 }
6591
6592 /*
6593 Crop_box is with respect to the upper left corner of the MNG.
6594 */
6595 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6596 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6597 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6598 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6599 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6600 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6601 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6602 if ((crop_box.left != (mng_info->image_box.left
6603 +mng_info->x_off[object_id])) ||
6604 (crop_box.right != (mng_info->image_box.right
6605 +mng_info->x_off[object_id])) ||
6606 (crop_box.top != (mng_info->image_box.top
6607 +mng_info->y_off[object_id])) ||
6608 (crop_box.bottom != (mng_info->image_box.bottom
6609 +mng_info->y_off[object_id])))
6610 {
6611 if (logging != MagickFalse)
6612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6613 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006614
cristy3ed852e2009-09-05 21:47:34 +00006615 if ((crop_box.left < crop_box.right) &&
6616 (crop_box.top < crop_box.bottom))
6617 {
6618 Image
6619 *im;
6620
6621 RectangleInfo
6622 crop_info;
6623
6624 /*
6625 Crop_info is with respect to the upper left corner of
6626 the image.
6627 */
6628 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6629 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006630 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6631 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006632 image->page.width=image->columns;
6633 image->page.height=image->rows;
6634 image->page.x=0;
6635 image->page.y=0;
6636 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006637
cristy3ed852e2009-09-05 21:47:34 +00006638 if (im != (Image *) NULL)
6639 {
6640 image->columns=im->columns;
6641 image->rows=im->rows;
6642 im=DestroyImage(im);
6643 image->page.width=image->columns;
6644 image->page.height=image->rows;
6645 image->page.x=crop_box.left;
6646 image->page.y=crop_box.top;
6647 }
6648 }
glennrp47b9dd52010-11-24 18:12:06 +00006649
cristy3ed852e2009-09-05 21:47:34 +00006650 else
6651 {
6652 /*
6653 No pixels in crop area. The MNG spec still requires
6654 a layer, though, so make a single transparent pixel in
6655 the top left corner.
6656 */
6657 image->columns=1;
6658 image->rows=1;
6659 image->colors=2;
6660 (void) SetImageBackgroundColor(image);
6661 image->page.width=1;
6662 image->page.height=1;
6663 image->page.x=0;
6664 image->page.y=0;
6665 }
6666 }
6667#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6668 image=mng_info->image;
6669#endif
6670 }
6671
glennrp2b013e42010-11-24 16:55:50 +00006672#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6673 /* PNG does not handle depths greater than 16 so reduce it even
6674 * if lossy
6675 */
6676 if (image->depth > 16)
6677 image->depth=16;
6678#endif
6679
glennrp3faa9a32011-04-23 14:00:25 +00006680#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006681 if (LosslessReduceDepthOK(image) != MagickFalse)
6682 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006683#endif
glennrpd6afd542010-11-19 01:53:05 +00006684
cristy3ed852e2009-09-05 21:47:34 +00006685 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006686
cristy3ed852e2009-09-05 21:47:34 +00006687 if (image_info->number_scenes != 0)
6688 {
6689 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006690 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006691 break;
6692 }
glennrpd6afd542010-11-19 01:53:05 +00006693
cristy3ed852e2009-09-05 21:47:34 +00006694 if (logging != MagickFalse)
6695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6696 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006697
cristy3ed852e2009-09-05 21:47:34 +00006698 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006699
cristy3ed852e2009-09-05 21:47:34 +00006700 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006701
cristy3ed852e2009-09-05 21:47:34 +00006702 if (logging != MagickFalse)
6703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6704 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006705
cristy3ed852e2009-09-05 21:47:34 +00006706#if defined(MNG_INSERT_LAYERS)
6707 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6708 (mng_info->mng_height))
6709 {
6710 /*
6711 Insert a background layer if nothing else was found.
6712 */
6713 if (logging != MagickFalse)
6714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6715 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006716
cristy3ed852e2009-09-05 21:47:34 +00006717 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6718 {
6719 /*
6720 Allocate next image structure.
6721 */
6722 AcquireNextImage(image_info,image);
6723 if (GetNextImageInList(image) == (Image *) NULL)
6724 {
6725 image=DestroyImageList(image);
6726 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006727
cristy3ed852e2009-09-05 21:47:34 +00006728 if (logging != MagickFalse)
6729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6730 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006731
cristy3ed852e2009-09-05 21:47:34 +00006732 return((Image *) NULL);
6733 }
6734 image=SyncNextImageInList(image);
6735 }
6736 image->columns=mng_info->mng_width;
6737 image->rows=mng_info->mng_height;
6738 image->page.width=mng_info->mng_width;
6739 image->page.height=mng_info->mng_height;
6740 image->page.x=0;
6741 image->page.y=0;
6742 image->background_color=mng_background_color;
6743 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006744
cristy3ed852e2009-09-05 21:47:34 +00006745 if (image_info->ping == MagickFalse)
6746 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006747
cristy3ed852e2009-09-05 21:47:34 +00006748 mng_info->image_found++;
6749 }
6750#endif
6751 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006752
cristy3ed852e2009-09-05 21:47:34 +00006753 if (mng_iterations == 1)
6754 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006755
cristy3ed852e2009-09-05 21:47:34 +00006756 while (GetPreviousImageInList(image) != (Image *) NULL)
6757 {
6758 image_count++;
6759 if (image_count > 10*mng_info->image_found)
6760 {
6761 if (logging != MagickFalse)
6762 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006763
cristy3ed852e2009-09-05 21:47:34 +00006764 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6765 CoderError,"Linked list is corrupted, beginning of list not found",
6766 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006767
cristy3ed852e2009-09-05 21:47:34 +00006768 return((Image *) NULL);
6769 }
glennrp0fe50b42010-11-16 03:52:51 +00006770
cristy3ed852e2009-09-05 21:47:34 +00006771 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006772
cristy3ed852e2009-09-05 21:47:34 +00006773 if (GetNextImageInList(image) == (Image *) NULL)
6774 {
6775 if (logging != MagickFalse)
6776 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006777
cristy3ed852e2009-09-05 21:47:34 +00006778 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6779 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6780 image_info->filename);
6781 }
6782 }
glennrp47b9dd52010-11-24 18:12:06 +00006783
cristy3ed852e2009-09-05 21:47:34 +00006784 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6785 GetNextImageInList(image) ==
6786 (Image *) NULL)
6787 {
6788 if (logging != MagickFalse)
6789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6790 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006791
cristy3ed852e2009-09-05 21:47:34 +00006792 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6793 CoderError,"image->next for first image is NULL but shouldn't be.",
6794 "`%s'",image_info->filename);
6795 }
glennrp47b9dd52010-11-24 18:12:06 +00006796
cristy3ed852e2009-09-05 21:47:34 +00006797 if (mng_info->image_found == 0)
6798 {
6799 if (logging != MagickFalse)
6800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6801 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006802
cristy3ed852e2009-09-05 21:47:34 +00006803 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6804 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006805
cristy3ed852e2009-09-05 21:47:34 +00006806 if (image != (Image *) NULL)
6807 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006808
cristy3ed852e2009-09-05 21:47:34 +00006809 MngInfoFreeStruct(mng_info,&have_mng_structure);
6810 return((Image *) NULL);
6811 }
6812
6813 if (mng_info->ticks_per_second)
6814 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6815 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006816
cristy3ed852e2009-09-05 21:47:34 +00006817 else
6818 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006819
cristy3ed852e2009-09-05 21:47:34 +00006820 /* Find final nonzero image delay */
6821 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006822
cristy3ed852e2009-09-05 21:47:34 +00006823 while (GetNextImageInList(image) != (Image *) NULL)
6824 {
6825 if (image->delay)
6826 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006827
cristy3ed852e2009-09-05 21:47:34 +00006828 image=GetNextImageInList(image);
6829 }
glennrp0fe50b42010-11-16 03:52:51 +00006830
cristy3ed852e2009-09-05 21:47:34 +00006831 if (final_delay < final_image_delay)
6832 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006833
cristy3ed852e2009-09-05 21:47:34 +00006834 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006835
cristy3ed852e2009-09-05 21:47:34 +00006836 if (logging != MagickFalse)
6837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006838 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6839 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006840
cristy3ed852e2009-09-05 21:47:34 +00006841 if (logging != MagickFalse)
6842 {
6843 int
6844 scene;
6845
6846 scene=0;
6847 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006848
cristy3ed852e2009-09-05 21:47:34 +00006849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6850 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006851
cristy3ed852e2009-09-05 21:47:34 +00006852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006853 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006854
cristy3ed852e2009-09-05 21:47:34 +00006855 while (GetNextImageInList(image) != (Image *) NULL)
6856 {
6857 image=GetNextImageInList(image);
6858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006859 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006860 }
6861 }
6862
6863 image=GetFirstImageInList(image);
6864#ifdef MNG_COALESCE_LAYERS
6865 if (insert_layers)
6866 {
6867 Image
6868 *next_image,
6869 *next;
6870
cristybb503372010-05-27 20:51:26 +00006871 size_t
cristy3ed852e2009-09-05 21:47:34 +00006872 scene;
6873
6874 if (logging != MagickFalse)
6875 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006876
cristy3ed852e2009-09-05 21:47:34 +00006877 scene=image->scene;
6878 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006879
cristy3ed852e2009-09-05 21:47:34 +00006880 if (next_image == (Image *) NULL)
6881 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006882
cristy3ed852e2009-09-05 21:47:34 +00006883 image=DestroyImageList(image);
6884 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006885
cristy3ed852e2009-09-05 21:47:34 +00006886 for (next=image; next != (Image *) NULL; next=next_image)
6887 {
6888 next->page.width=mng_info->mng_width;
6889 next->page.height=mng_info->mng_height;
6890 next->page.x=0;
6891 next->page.y=0;
6892 next->scene=scene++;
6893 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006894
cristy3ed852e2009-09-05 21:47:34 +00006895 if (next_image == (Image *) NULL)
6896 break;
glennrp47b9dd52010-11-24 18:12:06 +00006897
cristy3ed852e2009-09-05 21:47:34 +00006898 if (next->delay == 0)
6899 {
6900 scene--;
6901 next_image->previous=GetPreviousImageInList(next);
6902 if (GetPreviousImageInList(next) == (Image *) NULL)
6903 image=next_image;
6904 else
6905 next->previous->next=next_image;
6906 next=DestroyImage(next);
6907 }
6908 }
6909 }
6910#endif
6911
6912 while (GetNextImageInList(image) != (Image *) NULL)
6913 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006914
cristy3ed852e2009-09-05 21:47:34 +00006915 image->dispose=BackgroundDispose;
6916
6917 if (logging != MagickFalse)
6918 {
6919 int
6920 scene;
6921
6922 scene=0;
6923 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006924
cristy3ed852e2009-09-05 21:47:34 +00006925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6926 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006927
cristy3ed852e2009-09-05 21:47:34 +00006928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006929 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6930 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006931
cristy3ed852e2009-09-05 21:47:34 +00006932 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006933 {
6934 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006935
cristyf2faecf2010-05-28 19:19:36 +00006936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006937 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6938 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006939 }
6940 }
glennrp47b9dd52010-11-24 18:12:06 +00006941
cristy3ed852e2009-09-05 21:47:34 +00006942 image=GetFirstImageInList(image);
6943 MngInfoFreeStruct(mng_info,&have_mng_structure);
6944 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006945
cristy3ed852e2009-09-05 21:47:34 +00006946 if (logging != MagickFalse)
6947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006948
cristy3ed852e2009-09-05 21:47:34 +00006949 return(GetFirstImageInList(image));
6950}
glennrp25c1e2b2010-03-25 01:39:56 +00006951#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006952static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6953{
6954 printf("Your PNG library is too old: You have libpng-%s\n",
6955 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006956
cristy3ed852e2009-09-05 21:47:34 +00006957 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6958 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006959
cristy3ed852e2009-09-05 21:47:34 +00006960 return(Image *) NULL;
6961}
glennrp47b9dd52010-11-24 18:12:06 +00006962
cristy3ed852e2009-09-05 21:47:34 +00006963static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6964{
6965 return(ReadPNGImage(image_info,exception));
6966}
glennrp25c1e2b2010-03-25 01:39:56 +00006967#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006968#endif
6969
6970/*
6971%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6972% %
6973% %
6974% %
6975% R e g i s t e r P N G I m a g e %
6976% %
6977% %
6978% %
6979%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6980%
6981% RegisterPNGImage() adds properties for the PNG image format to
6982% the list of supported formats. The properties include the image format
6983% tag, a method to read and/or write the format, whether the format
6984% supports the saving of more than one frame to the same file or blob,
6985% whether the format supports native in-memory I/O, and a brief
6986% description of the format.
6987%
6988% The format of the RegisterPNGImage method is:
6989%
cristybb503372010-05-27 20:51:26 +00006990% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006991%
6992*/
cristybb503372010-05-27 20:51:26 +00006993ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006994{
6995 char
6996 version[MaxTextExtent];
6997
6998 MagickInfo
6999 *entry;
7000
7001 static const char
7002 *PNGNote=
7003 {
7004 "See http://www.libpng.org/ for details about the PNG format."
7005 },
glennrp47b9dd52010-11-24 18:12:06 +00007006
cristy3ed852e2009-09-05 21:47:34 +00007007 *JNGNote=
7008 {
7009 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7010 "format."
7011 },
glennrp47b9dd52010-11-24 18:12:06 +00007012
cristy3ed852e2009-09-05 21:47:34 +00007013 *MNGNote=
7014 {
7015 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7016 "format."
7017 };
7018
7019 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007020
cristy3ed852e2009-09-05 21:47:34 +00007021#if defined(PNG_LIBPNG_VER_STRING)
7022 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7023 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007024
cristy3ed852e2009-09-05 21:47:34 +00007025 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7026 {
7027 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7028 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7029 MaxTextExtent);
7030 }
7031#endif
glennrp47b9dd52010-11-24 18:12:06 +00007032
cristy3ed852e2009-09-05 21:47:34 +00007033 entry=SetMagickInfo("MNG");
7034 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007035
cristy3ed852e2009-09-05 21:47:34 +00007036#if defined(MAGICKCORE_PNG_DELEGATE)
7037 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7038 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7039#endif
glennrp47b9dd52010-11-24 18:12:06 +00007040
cristy3ed852e2009-09-05 21:47:34 +00007041 entry->magick=(IsImageFormatHandler *) IsMNG;
7042 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007043
cristy3ed852e2009-09-05 21:47:34 +00007044 if (*version != '\0')
7045 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007046
cristy3ed852e2009-09-05 21:47:34 +00007047 entry->module=ConstantString("PNG");
7048 entry->note=ConstantString(MNGNote);
7049 (void) RegisterMagickInfo(entry);
7050
7051 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007052
cristy3ed852e2009-09-05 21:47:34 +00007053#if defined(MAGICKCORE_PNG_DELEGATE)
7054 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7055 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7056#endif
glennrp47b9dd52010-11-24 18:12:06 +00007057
cristy3ed852e2009-09-05 21:47:34 +00007058 entry->magick=(IsImageFormatHandler *) IsPNG;
7059 entry->adjoin=MagickFalse;
7060 entry->description=ConstantString("Portable Network Graphics");
7061 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007062
cristy3ed852e2009-09-05 21:47:34 +00007063 if (*version != '\0')
7064 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007065
cristy3ed852e2009-09-05 21:47:34 +00007066 entry->note=ConstantString(PNGNote);
7067 (void) RegisterMagickInfo(entry);
7068
7069 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007070
cristy3ed852e2009-09-05 21:47:34 +00007071#if defined(MAGICKCORE_PNG_DELEGATE)
7072 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7073 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7074#endif
glennrp47b9dd52010-11-24 18:12:06 +00007075
cristy3ed852e2009-09-05 21:47:34 +00007076 entry->magick=(IsImageFormatHandler *) IsPNG;
7077 entry->adjoin=MagickFalse;
7078 entry->description=ConstantString(
7079 "8-bit indexed with optional binary transparency");
7080 entry->module=ConstantString("PNG");
7081 (void) RegisterMagickInfo(entry);
7082
7083 entry=SetMagickInfo("PNG24");
7084 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007085
cristy3ed852e2009-09-05 21:47:34 +00007086#if defined(ZLIB_VERSION)
7087 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7088 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007089
cristy3ed852e2009-09-05 21:47:34 +00007090 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7091 {
7092 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7093 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7094 }
7095#endif
glennrp47b9dd52010-11-24 18:12:06 +00007096
cristy3ed852e2009-09-05 21:47:34 +00007097 if (*version != '\0')
7098 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007099
cristy3ed852e2009-09-05 21:47:34 +00007100#if defined(MAGICKCORE_PNG_DELEGATE)
7101 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7102 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7103#endif
glennrp47b9dd52010-11-24 18:12:06 +00007104
cristy3ed852e2009-09-05 21:47:34 +00007105 entry->magick=(IsImageFormatHandler *) IsPNG;
7106 entry->adjoin=MagickFalse;
7107 entry->description=ConstantString("opaque 24-bit RGB");
7108 entry->module=ConstantString("PNG");
7109 (void) RegisterMagickInfo(entry);
7110
7111 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007112
cristy3ed852e2009-09-05 21:47:34 +00007113#if defined(MAGICKCORE_PNG_DELEGATE)
7114 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7115 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7116#endif
glennrp47b9dd52010-11-24 18:12:06 +00007117
cristy3ed852e2009-09-05 21:47:34 +00007118 entry->magick=(IsImageFormatHandler *) IsPNG;
7119 entry->adjoin=MagickFalse;
7120 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7121 entry->module=ConstantString("PNG");
7122 (void) RegisterMagickInfo(entry);
7123
7124 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007125
cristy3ed852e2009-09-05 21:47:34 +00007126#if defined(JNG_SUPPORTED)
7127#if defined(MAGICKCORE_PNG_DELEGATE)
7128 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7129 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7130#endif
7131#endif
glennrp47b9dd52010-11-24 18:12:06 +00007132
cristy3ed852e2009-09-05 21:47:34 +00007133 entry->magick=(IsImageFormatHandler *) IsJNG;
7134 entry->adjoin=MagickFalse;
7135 entry->description=ConstantString("JPEG Network Graphics");
7136 entry->module=ConstantString("PNG");
7137 entry->note=ConstantString(JNGNote);
7138 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007139
cristy18b17442009-10-25 18:36:48 +00007140#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007141 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007142#endif
glennrp47b9dd52010-11-24 18:12:06 +00007143
cristy3ed852e2009-09-05 21:47:34 +00007144 return(MagickImageCoderSignature);
7145}
7146
7147/*
7148%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7149% %
7150% %
7151% %
7152% U n r e g i s t e r P N G I m a g e %
7153% %
7154% %
7155% %
7156%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7157%
7158% UnregisterPNGImage() removes format registrations made by the
7159% PNG module from the list of supported formats.
7160%
7161% The format of the UnregisterPNGImage method is:
7162%
7163% UnregisterPNGImage(void)
7164%
7165*/
7166ModuleExport void UnregisterPNGImage(void)
7167{
7168 (void) UnregisterMagickInfo("MNG");
7169 (void) UnregisterMagickInfo("PNG");
7170 (void) UnregisterMagickInfo("PNG8");
7171 (void) UnregisterMagickInfo("PNG24");
7172 (void) UnregisterMagickInfo("PNG32");
7173 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007174
cristy3ed852e2009-09-05 21:47:34 +00007175#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007176 if (ping_semaphore != (SemaphoreInfo *) NULL)
7177 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007178#endif
7179}
7180
7181#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007182#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007183/*
7184%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7185% %
7186% %
7187% %
7188% W r i t e M N G I m a g e %
7189% %
7190% %
7191% %
7192%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7193%
7194% WriteMNGImage() writes an image in the Portable Network Graphics
7195% Group's "Multiple-image Network Graphics" encoded image format.
7196%
7197% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7198%
7199% The format of the WriteMNGImage method is:
7200%
7201% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
7202%
7203% A description of each parameter follows.
7204%
7205% o image_info: the image info.
7206%
7207% o image: The image.
7208%
7209%
7210% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7211% "To do" under ReadPNGImage):
7212%
cristy3ed852e2009-09-05 21:47:34 +00007213% Preserve all unknown and not-yet-handled known chunks found in input
7214% PNG file and copy them into output PNG files according to the PNG
7215% copying rules.
7216%
7217% Write the iCCP chunk at MNG level when (icc profile length > 0)
7218%
7219% Improve selection of color type (use indexed-colour or indexed-colour
7220% with tRNS when 256 or fewer unique RGBA values are present).
7221%
7222% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7223% This will be complicated if we limit ourselves to generating MNG-LC
7224% files. For now we ignore disposal method 3 and simply overlay the next
7225% image on it.
7226%
7227% Check for identical PLTE's or PLTE/tRNS combinations and use a
7228% global MNG PLTE or PLTE/tRNS combination when appropriate.
7229% [mostly done 15 June 1999 but still need to take care of tRNS]
7230%
7231% Check for identical sRGB and replace with a global sRGB (and remove
7232% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7233% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7234% local gAMA/cHRM with local sRGB if appropriate).
7235%
7236% Check for identical sBIT chunks and write global ones.
7237%
7238% Provide option to skip writing the signature tEXt chunks.
7239%
7240% Use signatures to detect identical objects and reuse the first
7241% instance of such objects instead of writing duplicate objects.
7242%
7243% Use a smaller-than-32k value of compression window size when
7244% appropriate.
7245%
7246% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7247% ancillary text chunks and save profiles.
7248%
7249% Provide an option to force LC files (to ensure exact framing rate)
7250% instead of VLC.
7251%
7252% Provide an option to force VLC files instead of LC, even when offsets
7253% are present. This will involve expanding the embedded images with a
7254% transparent region at the top and/or left.
7255*/
7256
cristy3ed852e2009-09-05 21:47:34 +00007257static void
glennrpcf002022011-01-30 02:38:15 +00007258Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007259 png_info *ping_info, unsigned char *profile_type, unsigned char
7260 *profile_description, unsigned char *profile_data, png_uint_32 length)
7261{
cristy3ed852e2009-09-05 21:47:34 +00007262 png_textp
7263 text;
7264
cristybb503372010-05-27 20:51:26 +00007265 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007266 i;
7267
7268 unsigned char
7269 *sp;
7270
7271 png_charp
7272 dp;
7273
7274 png_uint_32
7275 allocated_length,
7276 description_length;
7277
7278 unsigned char
7279 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007280
cristy3ed852e2009-09-05 21:47:34 +00007281 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7282 return;
7283
7284 if (image_info->verbose)
7285 {
glennrp0fe50b42010-11-16 03:52:51 +00007286 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7287 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007288 }
glennrp0fe50b42010-11-16 03:52:51 +00007289
cristy3ed852e2009-09-05 21:47:34 +00007290 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7291 description_length=(png_uint_32) strlen((const char *) profile_description);
7292 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7293 + description_length);
7294 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7295 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7296 text[0].key[0]='\0';
7297 (void) ConcatenateMagickString(text[0].key,
7298 "Raw profile type ",MaxTextExtent);
7299 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7300 sp=profile_data;
7301 dp=text[0].text;
7302 *dp++='\n';
7303 (void) CopyMagickString(dp,(const char *) profile_description,
7304 allocated_length);
7305 dp+=description_length;
7306 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007307 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007308 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007309 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007310
cristybb503372010-05-27 20:51:26 +00007311 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007312 {
7313 if (i%36 == 0)
7314 *dp++='\n';
7315 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7316 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7317 }
glennrp47b9dd52010-11-24 18:12:06 +00007318
cristy3ed852e2009-09-05 21:47:34 +00007319 *dp++='\n';
7320 *dp='\0';
7321 text[0].text_length=(png_size_t) (dp-text[0].text);
7322 text[0].compression=image_info->compression == NoCompression ||
7323 (image_info->compression == UndefinedCompression &&
7324 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007325
cristy3ed852e2009-09-05 21:47:34 +00007326 if (text[0].text_length <= allocated_length)
7327 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007328
cristy3ed852e2009-09-05 21:47:34 +00007329 png_free(ping,text[0].text);
7330 png_free(ping,text[0].key);
7331 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007332}
7333
glennrpcf002022011-01-30 02:38:15 +00007334static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007335 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007336{
7337 char
7338 *name;
7339
7340 const StringInfo
7341 *profile;
7342
7343 unsigned char
7344 *data;
7345
7346 png_uint_32 length;
7347
7348 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007349
7350 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7351 {
cristy3ed852e2009-09-05 21:47:34 +00007352 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007353
cristy3ed852e2009-09-05 21:47:34 +00007354 if (profile != (const StringInfo *) NULL)
7355 {
7356 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007357 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007358
glennrp47b9dd52010-11-24 18:12:06 +00007359 if (LocaleNCompare(name,string,11) == 0)
7360 {
7361 if (logging != MagickFalse)
7362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7363 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007364
glennrpcf002022011-01-30 02:38:15 +00007365 ping_profile=CloneStringInfo(profile);
7366 data=GetStringInfoDatum(ping_profile),
7367 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007368 data[4]=data[3];
7369 data[3]=data[2];
7370 data[2]=data[1];
7371 data[1]=data[0];
7372 (void) WriteBlobMSBULong(image,length-5); /* data length */
7373 (void) WriteBlob(image,length-1,data+1);
7374 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007375 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007376 }
cristy3ed852e2009-09-05 21:47:34 +00007377 }
glennrp47b9dd52010-11-24 18:12:06 +00007378
cristy3ed852e2009-09-05 21:47:34 +00007379 name=GetNextImageProfile(image);
7380 }
glennrp47b9dd52010-11-24 18:12:06 +00007381
cristy3ed852e2009-09-05 21:47:34 +00007382 return(MagickTrue);
7383}
7384
glennrpb9cfe272010-12-21 15:08:06 +00007385
cristy3ed852e2009-09-05 21:47:34 +00007386/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007387static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7388 const ImageInfo *IMimage_info,Image *IMimage)
7389{
7390 Image
7391 *image;
7392
7393 ImageInfo
7394 *image_info;
7395
cristy3ed852e2009-09-05 21:47:34 +00007396 char
7397 s[2];
7398
7399 const char
7400 *name,
7401 *property,
7402 *value;
7403
7404 const StringInfo
7405 *profile;
7406
cristy3ed852e2009-09-05 21:47:34 +00007407 int
cristy3ed852e2009-09-05 21:47:34 +00007408 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007409 pass;
7410
glennrpe9c26dc2010-05-30 01:56:35 +00007411 png_byte
7412 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007413
glennrp39992b42010-11-14 00:03:43 +00007414 png_color
7415 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007416
glennrp5af765f2010-03-30 11:12:18 +00007417 png_color_16
7418 ping_background,
7419 ping_trans_color;
7420
cristy3ed852e2009-09-05 21:47:34 +00007421 png_info
7422 *ping_info;
7423
7424 png_struct
7425 *ping;
7426
glennrp5af765f2010-03-30 11:12:18 +00007427 png_uint_32
7428 ping_height,
7429 ping_width;
7430
cristybb503372010-05-27 20:51:26 +00007431 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007432 y;
7433
7434 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007435 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007436 logging,
glennrp58e01762011-01-07 15:28:54 +00007437 matte,
7438
glennrpda8f3a72011-02-27 23:54:12 +00007439 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007440 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007441 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007442 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007443 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007444 ping_have_bKGD,
7445 ping_have_pHYs,
7446 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007447
7448 ping_exclude_bKGD,
7449 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007450 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007451 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007452 ping_exclude_gAMA,
7453 ping_exclude_iCCP,
7454 /* ping_exclude_iTXt, */
7455 ping_exclude_oFFs,
7456 ping_exclude_pHYs,
7457 ping_exclude_sRGB,
7458 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007459 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007460 ping_exclude_vpAg,
7461 ping_exclude_zCCP, /* hex-encoded iCCP */
7462 ping_exclude_zTXt,
7463
glennrp8d3d6e52011-04-19 04:39:51 +00007464 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007465 ping_need_colortype_warning,
7466
glennrp82b3c532011-03-22 19:20:54 +00007467 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007468 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007469 tried_333,
7470 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007471
7472 QuantumInfo
7473 *quantum_info;
7474
cristybb503372010-05-27 20:51:26 +00007475 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007476 i,
7477 x;
7478
7479 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007480 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007481
glennrp5af765f2010-03-30 11:12:18 +00007482 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007483 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007484 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007485 ping_color_type,
7486 ping_interlace_method,
7487 ping_compression_method,
7488 ping_filter_method,
7489 ping_num_trans;
7490
cristybb503372010-05-27 20:51:26 +00007491 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007492 image_depth,
7493 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007494
cristybb503372010-05-27 20:51:26 +00007495 size_t
cristy3ed852e2009-09-05 21:47:34 +00007496 quality,
7497 rowbytes,
7498 save_image_depth;
7499
glennrpdfd70802010-11-14 01:23:35 +00007500 int
glennrpfd05d622011-02-25 04:10:33 +00007501 j,
glennrpf09bded2011-01-08 01:15:59 +00007502 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007503 number_opaque,
7504 number_semitransparent,
7505 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007506 ping_pHYs_unit_type;
7507
7508 png_uint_32
7509 ping_pHYs_x_resolution,
7510 ping_pHYs_y_resolution;
7511
cristy3ed852e2009-09-05 21:47:34 +00007512 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007513 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007514
glennrpb9cfe272010-12-21 15:08:06 +00007515 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7516 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007517 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007518 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007519
cristy3ed852e2009-09-05 21:47:34 +00007520#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007521 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007522#endif
7523
glennrp5af765f2010-03-30 11:12:18 +00007524 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007525 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007526 ping_color_type=0,
7527 ping_interlace_method=0,
7528 ping_compression_method=0,
7529 ping_filter_method=0,
7530 ping_num_trans = 0;
7531
7532 ping_background.red = 0;
7533 ping_background.green = 0;
7534 ping_background.blue = 0;
7535 ping_background.gray = 0;
7536 ping_background.index = 0;
7537
7538 ping_trans_color.red=0;
7539 ping_trans_color.green=0;
7540 ping_trans_color.blue=0;
7541 ping_trans_color.gray=0;
7542
glennrpdfd70802010-11-14 01:23:35 +00007543 ping_pHYs_unit_type = 0;
7544 ping_pHYs_x_resolution = 0;
7545 ping_pHYs_y_resolution = 0;
7546
glennrpda8f3a72011-02-27 23:54:12 +00007547 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007548 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007549 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007550 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007551 ping_have_bKGD=MagickFalse;
7552 ping_have_pHYs=MagickFalse;
7553 ping_have_tRNS=MagickFalse;
7554
glennrp0e8ea192010-12-24 18:00:33 +00007555 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7556 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007557 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007558 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007559 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007560 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7561 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7562 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7563 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7564 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7565 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007566 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007567 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7568 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7569 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7570
glennrp8d3d6e52011-04-19 04:39:51 +00007571 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007572 ping_need_colortype_warning = MagickFalse;
7573
glennrp8bb3a022010-12-13 20:40:04 +00007574 number_opaque = 0;
7575 number_semitransparent = 0;
7576 number_transparent = 0;
7577
glennrpfd05d622011-02-25 04:10:33 +00007578 if (logging != MagickFalse)
7579 {
7580 if (image->storage_class == UndefinedClass)
7581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7582 " storage_class=UndefinedClass");
7583 if (image->storage_class == DirectClass)
7584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7585 " storage_class=DirectClass");
7586 if (image->storage_class == PseudoClass)
7587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7588 " storage_class=PseudoClass");
7589 }
glennrp28af3712011-04-06 18:07:30 +00007590
glennrpc6c391a2011-04-27 02:23:56 +00007591 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007592 {
glennrpc6c391a2011-04-27 02:23:56 +00007593 if (image->storage_class != PseudoClass && image->colormap != NULL)
7594 {
7595 /* Free the bogus colormap; it can cause trouble later */
7596 if (logging != MagickFalse)
7597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7598 " Freeing bogus colormap");
7599 (void *) RelinquishMagickMemory(image->colormap);
7600 image->colormap=NULL;
7601 }
glennrp28af3712011-04-06 18:07:30 +00007602 }
glennrpbb4f99d2011-05-22 11:13:17 +00007603
cristy3ed852e2009-09-05 21:47:34 +00007604 if (image->colorspace != RGBColorspace)
7605 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007606
glennrp3241bd02010-12-12 04:36:28 +00007607 /*
7608 Sometimes we get PseudoClass images whose RGB values don't match
7609 the colors in the colormap. This code syncs the RGB values.
7610 */
7611 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7612 (void) SyncImage(image);
7613
glennrpa6a06632011-01-19 15:15:34 +00007614#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7615 if (image->depth > 8)
7616 {
7617 if (logging != MagickFalse)
7618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7619 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7620
7621 image->depth=8;
7622 }
7623#endif
7624
glennrp8e58efd2011-05-20 12:16:29 +00007625 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007626 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7627 {
glennrp8e58efd2011-05-20 12:16:29 +00007628 register PixelPacket
7629 *r;
7630
7631 ExceptionInfo
7632 *exception;
7633
7634 exception=(&image->exception);
7635
7636 if (image->depth > 8)
7637 {
7638#if MAGICKCORE_QUANTUM_DEPTH > 16
7639 /* Scale to 16-bit */
7640 LBR16RGBOPixelPacketComponent(image->background_color);
7641
7642 for (y=0; y < (ssize_t) image->rows; y++)
7643 {
7644 r=GetAuthenticPixels(image,0,y,image->columns,1,
7645 exception);
7646
7647 if (r == (PixelPacket *) NULL)
7648 break;
7649
7650 for (x=0; x < (ssize_t) image->columns; x++)
7651 {
7652 LBR16RGBOPixelComponent(r);
7653 r++;
7654 }
glennrpbb4f99d2011-05-22 11:13:17 +00007655
glennrp8e58efd2011-05-20 12:16:29 +00007656 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7657 break;
7658 }
7659
7660 if (image->storage_class == PseudoClass && image->colormap != NULL)
7661 {
cristy3e08f112011-05-24 13:19:30 +00007662 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007663 {
7664 LBR16RGBOPixelPacketComponent(image->colormap[i]);
7665 }
7666 }
7667#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7668 }
7669
7670 else if (image->depth > 4)
7671 {
7672#if MAGICKCORE_QUANTUM_DEPTH > 8
7673 /* Scale to 8-bit */
7674 LBR08RGBOPixelPacketComponent(image->background_color);
7675
7676 for (y=0; y < (ssize_t) image->rows; y++)
7677 {
7678 r=GetAuthenticPixels(image,0,y,image->columns,1,
7679 exception);
7680
7681 if (r == (PixelPacket *) NULL)
7682 break;
7683
7684 for (x=0; x < (ssize_t) image->columns; x++)
7685 {
7686 LBR08RGBOPixelComponent(r);
7687 r++;
7688 }
glennrpbb4f99d2011-05-22 11:13:17 +00007689
glennrp8e58efd2011-05-20 12:16:29 +00007690 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7691 break;
7692 }
7693
7694 if (image->storage_class == PseudoClass && image->colormap != NULL)
7695 {
cristy3e08f112011-05-24 13:19:30 +00007696 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007697 {
7698 LBR08RGBOPixelPacketComponent(image->colormap[i]);
7699 }
7700 }
7701#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7702 }
7703 else
7704 if (image->depth > 2)
7705 {
7706 /* Scale to 4-bit */
7707 LBR04RGBOPixelPacketComponent(image->background_color);
7708
7709 for (y=0; y < (ssize_t) image->rows; y++)
7710 {
7711 r=GetAuthenticPixels(image,0,y,image->columns,1,
7712 exception);
7713
7714 if (r == (PixelPacket *) NULL)
7715 break;
7716
7717 for (x=0; x < (ssize_t) image->columns; x++)
7718 {
7719 LBR04RGBOPixelComponent(r);
7720 r++;
7721 }
glennrpbb4f99d2011-05-22 11:13:17 +00007722
glennrp8e58efd2011-05-20 12:16:29 +00007723 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7724 break;
7725 }
7726
7727 if (image->storage_class == PseudoClass && image->colormap != NULL)
7728 {
cristy3e08f112011-05-24 13:19:30 +00007729 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007730 {
7731 LBR04RGBOPixelPacketComponent(image->colormap[i]);
7732 }
7733 }
7734 }
7735
7736 else if (image->depth > 1)
7737 {
7738 /* Scale to 2-bit */
7739 LBR02RGBOPixelPacketComponent(image->background_color);
7740
7741 for (y=0; y < (ssize_t) image->rows; y++)
7742 {
7743 r=GetAuthenticPixels(image,0,y,image->columns,1,
7744 exception);
7745
7746 if (r == (PixelPacket *) NULL)
7747 break;
7748
7749 for (x=0; x < (ssize_t) image->columns; x++)
7750 {
7751 LBR02RGBOPixelComponent(r);
7752 r++;
7753 }
glennrpbb4f99d2011-05-22 11:13:17 +00007754
glennrp8e58efd2011-05-20 12:16:29 +00007755 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7756 break;
7757 }
7758
7759 if (image->storage_class == PseudoClass && image->colormap != NULL)
7760 {
cristy3e08f112011-05-24 13:19:30 +00007761 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007762 {
7763 LBR02RGBOPixelPacketComponent(image->colormap[i]);
7764 }
7765 }
7766 }
7767 else
7768 {
7769 /* Scale to 1-bit */
7770 LBR01RGBOPixelPacketComponent(image->background_color);
7771
7772 for (y=0; y < (ssize_t) image->rows; y++)
7773 {
7774 r=GetAuthenticPixels(image,0,y,image->columns,1,
7775 exception);
7776
7777 if (r == (PixelPacket *) NULL)
7778 break;
7779
7780 for (x=0; x < (ssize_t) image->columns; x++)
7781 {
7782 LBR01RGBOPixelComponent(r);
7783 r++;
7784 }
glennrpbb4f99d2011-05-22 11:13:17 +00007785
glennrp8e58efd2011-05-20 12:16:29 +00007786 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7787 break;
7788 }
7789
7790 if (image->storage_class == PseudoClass && image->colormap != NULL)
7791 {
cristy3e08f112011-05-24 13:19:30 +00007792 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007793 {
7794 LBR01RGBOPixelPacketComponent(image->colormap[i]);
7795 }
7796 }
7797 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007798 }
7799
glennrp67b9c1a2011-04-22 18:47:36 +00007800 /* To do: set to next higher multiple of 8 */
7801 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007802 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007803
glennrp2b013e42010-11-24 16:55:50 +00007804#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7805 /* PNG does not handle depths greater than 16 so reduce it even
7806 * if lossy
7807 */
glennrp8e58efd2011-05-20 12:16:29 +00007808 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007809 image->depth=16;
7810#endif
7811
glennrp3faa9a32011-04-23 14:00:25 +00007812#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007813 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007814 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007815 image->depth = 8;
7816#endif
7817
glennrpc8c2f062011-02-25 19:00:33 +00007818 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007819 * we reduce the transparency to binary and run again, then if there
7820 * 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 +00007821 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7822 * palette. Then (To do) we take care of a final reduction that is only
7823 * needed if there are still 256 colors present and one of them has both
7824 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007825 */
glennrp82b3c532011-03-22 19:20:54 +00007826
glennrp8ca51ad2011-05-12 21:22:32 +00007827 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007828 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007829 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007830
glennrp8ca51ad2011-05-12 21:22:32 +00007831 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007832 {
7833 /* BUILD_PALETTE
7834 *
7835 * Sometimes we get DirectClass images that have 256 colors or fewer.
7836 * This code will build a colormap.
7837 *
7838 * Also, sometimes we get PseudoClass images with an out-of-date
7839 * colormap. This code will replace the colormap with a new one.
7840 * Sometimes we get PseudoClass images that have more than 256 colors.
7841 * This code will delete the colormap and change the image to
7842 * DirectClass.
7843 *
7844 * If image->matte is MagickFalse, we ignore the opacity channel
7845 * even though it sometimes contains left-over non-opaque values.
7846 *
7847 * Also we gather some information (number of opaque, transparent,
7848 * and semitransparent pixels, and whether the image has any non-gray
7849 * pixels or only black-and-white pixels) that we might need later.
7850 *
7851 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7852 * we need to check for bogus non-opaque values, at least.
7853 */
glennrp3c218112010-11-27 15:31:26 +00007854
glennrpd71e86a2011-02-24 01:28:37 +00007855 ExceptionInfo
7856 *exception;
glennrp3c218112010-11-27 15:31:26 +00007857
glennrpd71e86a2011-02-24 01:28:37 +00007858 int
7859 n;
glennrp3c218112010-11-27 15:31:26 +00007860
glennrpd71e86a2011-02-24 01:28:37 +00007861 PixelPacket
7862 opaque[260],
7863 semitransparent[260],
7864 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007865
glennrpd71e86a2011-02-24 01:28:37 +00007866 register IndexPacket
7867 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007868
glennrpd71e86a2011-02-24 01:28:37 +00007869 register const PixelPacket
7870 *s,
7871 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007872
glennrpfd05d622011-02-25 04:10:33 +00007873 register PixelPacket
7874 *r;
7875
glennrpd71e86a2011-02-24 01:28:37 +00007876 if (logging != MagickFalse)
7877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7878 " Enter BUILD_PALETTE:");
7879
7880 if (logging != MagickFalse)
7881 {
glennrp03812ae2010-12-24 01:31:34 +00007882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007883 " image->columns=%.20g",(double) image->columns);
7884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7885 " image->rows=%.20g",(double) image->rows);
7886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7887 " image->matte=%.20g",(double) image->matte);
7888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7889 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007890
glennrpfd05d622011-02-25 04:10:33 +00007891 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007892 {
7893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007894 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007896 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007897
glennrpd71e86a2011-02-24 01:28:37 +00007898 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007899 {
glennrpd71e86a2011-02-24 01:28:37 +00007900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7901 " %d (%d,%d,%d,%d)",
7902 (int) i,
7903 (int) image->colormap[i].red,
7904 (int) image->colormap[i].green,
7905 (int) image->colormap[i].blue,
7906 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007907 }
glennrp2cc891a2010-12-24 13:44:32 +00007908
glennrpd71e86a2011-02-24 01:28:37 +00007909 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7910 {
7911 if (i > 255)
7912 {
7913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7914 " %d (%d,%d,%d,%d)",
7915 (int) i,
7916 (int) image->colormap[i].red,
7917 (int) image->colormap[i].green,
7918 (int) image->colormap[i].blue,
7919 (int) image->colormap[i].opacity);
7920 }
7921 }
glennrp03812ae2010-12-24 01:31:34 +00007922 }
glennrp7ddcc222010-12-11 05:01:05 +00007923
glennrpd71e86a2011-02-24 01:28:37 +00007924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7925 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007926
glennrpd71e86a2011-02-24 01:28:37 +00007927 if (image->colors == 0)
7928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7929 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007930
glennrp8d3d6e52011-04-19 04:39:51 +00007931 if (ping_preserve_colormap == MagickFalse)
7932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7933 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007934 }
7935
7936 exception=(&image->exception);
7937
7938 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007939 number_opaque = 0;
7940 number_semitransparent = 0;
7941 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007942
7943 for (y=0; y < (ssize_t) image->rows; y++)
7944 {
7945 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7946
7947 if (q == (PixelPacket *) NULL)
7948 break;
7949
7950 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007951 {
glennrp4737d522011-04-29 03:33:42 +00007952 if (image->matte == MagickFalse ||
7953 GetOpacityPixelComponent(q) == OpaqueOpacity)
glennrpd71e86a2011-02-24 01:28:37 +00007954 {
7955 if (number_opaque < 259)
7956 {
7957 if (number_opaque == 0)
7958 {
cristy8d74fc12011-05-25 23:02:47 +00007959 GetRGBPixelComponents(q, opaque);
glennrpd71e86a2011-02-24 01:28:37 +00007960 opaque[0].opacity=OpaqueOpacity;
7961 number_opaque=1;
7962 }
glennrp2cc891a2010-12-24 13:44:32 +00007963
glennrpd71e86a2011-02-24 01:28:37 +00007964 for (i=0; i< (ssize_t) number_opaque; i++)
7965 {
glennrp0e68fac2011-04-26 04:51:47 +00007966 if (IsColorEqual(q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00007967 break;
7968 }
glennrp7ddcc222010-12-11 05:01:05 +00007969
glennrpd71e86a2011-02-24 01:28:37 +00007970 if (i == (ssize_t) number_opaque &&
7971 number_opaque < 259)
7972 {
7973 number_opaque++;
cristy8d74fc12011-05-25 23:02:47 +00007974 GetRGBPixelComponents(q, opaque+i);
glennrpca7ad3a2011-04-26 04:44:54 +00007975 opaque[i].opacity=OpaqueOpacity;
glennrpd71e86a2011-02-24 01:28:37 +00007976 }
7977 }
7978 }
7979 else if (q->opacity == TransparentOpacity)
7980 {
7981 if (number_transparent < 259)
7982 {
7983 if (number_transparent == 0)
7984 {
cristy8d74fc12011-05-25 23:02:47 +00007985 GetRGBOPixelComponents(q, transparent);
glennrpa18d5bc2011-04-23 14:51:34 +00007986 ping_trans_color.red=
7987 (unsigned short) GetRedPixelComponent(q);
7988 ping_trans_color.green=
7989 (unsigned short) GetGreenPixelComponent(q);
7990 ping_trans_color.blue=
7991 (unsigned short) GetBluePixelComponent(q);
7992 ping_trans_color.gray=
7993 (unsigned short) GetRedPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00007994 number_transparent = 1;
7995 }
7996
7997 for (i=0; i< (ssize_t) number_transparent; i++)
7998 {
glennrp0e68fac2011-04-26 04:51:47 +00007999 if (IsColorEqual(q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008000 break;
8001 }
8002
8003 if (i == (ssize_t) number_transparent &&
8004 number_transparent < 259)
8005 {
8006 number_transparent++;
cristy8d74fc12011-05-25 23:02:47 +00008007 GetRGBOPixelComponents(q, transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008008 }
8009 }
8010 }
8011 else
8012 {
8013 if (number_semitransparent < 259)
8014 {
8015 if (number_semitransparent == 0)
8016 {
cristy8d74fc12011-05-25 23:02:47 +00008017 GetRGBOPixelComponents(q, semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008018 number_semitransparent = 1;
8019 }
8020
8021 for (i=0; i< (ssize_t) number_semitransparent; i++)
8022 {
glennrp0e68fac2011-04-26 04:51:47 +00008023 if (IsColorEqual(q, semitransparent+i)
glennrpca7ad3a2011-04-26 04:44:54 +00008024 && GetOpacityPixelComponent(q) ==
8025 semitransparent[i].opacity)
glennrpd71e86a2011-02-24 01:28:37 +00008026 break;
8027 }
8028
8029 if (i == (ssize_t) number_semitransparent &&
8030 number_semitransparent < 259)
8031 {
8032 number_semitransparent++;
cristy8d74fc12011-05-25 23:02:47 +00008033 GetRGBOPixelComponents(q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008034 }
8035 }
8036 }
8037 q++;
8038 }
8039 }
8040
8041 if (ping_exclude_bKGD == MagickFalse)
8042 {
8043 /* Add the background color to the palette, if it
8044 * isn't already there.
8045 */
glennrpc6c391a2011-04-27 02:23:56 +00008046 if (logging != MagickFalse)
8047 {
8048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8049 " Check colormap for background (%d,%d,%d)",
8050 (int) image->background_color.red,
8051 (int) image->background_color.green,
8052 (int) image->background_color.blue);
8053 }
glennrpd71e86a2011-02-24 01:28:37 +00008054 for (i=0; i<number_opaque; i++)
8055 {
glennrpca7ad3a2011-04-26 04:44:54 +00008056 if (opaque[i].red == image->background_color.red &&
8057 opaque[i].green == image->background_color.green &&
8058 opaque[i].blue == image->background_color.blue)
8059 break;
glennrpd71e86a2011-02-24 01:28:37 +00008060 }
glennrpd71e86a2011-02-24 01:28:37 +00008061 if (number_opaque < 259 && i == number_opaque)
8062 {
glennrp8e045c82011-04-27 16:40:27 +00008063 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008064 ping_background.index = i;
8065 if (logging != MagickFalse)
8066 {
8067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8068 " background_color index is %d",(int) i);
8069 }
8070
glennrpd71e86a2011-02-24 01:28:37 +00008071 }
glennrpa080bc32011-03-11 18:03:44 +00008072 else if (logging != MagickFalse)
8073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8074 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008075 }
8076
8077 image_colors=number_opaque+number_transparent+number_semitransparent;
8078
glennrpa080bc32011-03-11 18:03:44 +00008079 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8080 {
8081 /* No room for the background color; remove it. */
8082 number_opaque--;
8083 image_colors--;
8084 }
8085
glennrpd71e86a2011-02-24 01:28:37 +00008086 if (logging != MagickFalse)
8087 {
8088 if (image_colors > 256)
8089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8090 " image has more than 256 colors");
8091
8092 else
8093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8094 " image has %d colors",image_colors);
8095 }
8096
glennrp8d3d6e52011-04-19 04:39:51 +00008097 if (ping_preserve_colormap != MagickFalse)
8098 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008099
glennrpfd05d622011-02-25 04:10:33 +00008100 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008101 {
8102 ping_have_color=MagickFalse;
8103 ping_have_non_bw=MagickFalse;
8104
8105 if(image_colors > 256)
8106 {
8107 for (y=0; y < (ssize_t) image->rows; y++)
8108 {
8109 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8110
8111 if (q == (PixelPacket *) NULL)
8112 break;
8113
8114 /* Worst case is black-and-white; we are looking at every
8115 * pixel twice.
8116 */
8117
8118 if (ping_have_color == MagickFalse)
8119 {
8120 s=q;
8121 for (x=0; x < (ssize_t) image->columns; x++)
8122 {
glennrpa18d5bc2011-04-23 14:51:34 +00008123 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
8124 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
glennrpd71e86a2011-02-24 01:28:37 +00008125 {
8126 ping_have_color=MagickTrue;
8127 ping_have_non_bw=MagickTrue;
8128 break;
8129 }
8130 s++;
8131 }
8132 }
8133
8134 if (ping_have_non_bw == MagickFalse)
8135 {
8136 s=q;
8137 for (x=0; x < (ssize_t) image->columns; x++)
8138 {
glennrpa18d5bc2011-04-23 14:51:34 +00008139 if (GetRedPixelComponent(s) != 0 &&
8140 GetRedPixelComponent(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008141 {
8142 ping_have_non_bw=MagickTrue;
8143 }
8144 s++;
8145 }
8146 }
8147 }
glennrpbb4f99d2011-05-22 11:13:17 +00008148 }
8149 }
glennrpd71e86a2011-02-24 01:28:37 +00008150
8151 if (image_colors < 257)
8152 {
8153 PixelPacket
8154 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008155
glennrpd71e86a2011-02-24 01:28:37 +00008156 /*
8157 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008158 */
8159
glennrpd71e86a2011-02-24 01:28:37 +00008160 if (logging != MagickFalse)
8161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8162 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008163
glennrpd71e86a2011-02-24 01:28:37 +00008164 /* Sort palette, transparent first */;
8165
8166 n = 0;
8167
8168 for (i=0; i<number_transparent; i++)
8169 colormap[n++] = transparent[i];
8170
8171 for (i=0; i<number_semitransparent; i++)
8172 colormap[n++] = semitransparent[i];
8173
8174 for (i=0; i<number_opaque; i++)
8175 colormap[n++] = opaque[i];
8176
glennrpc6c391a2011-04-27 02:23:56 +00008177 ping_background.index +=
8178 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008179
glennrpd71e86a2011-02-24 01:28:37 +00008180 /* image_colors < 257; search the colormap instead of the pixels
8181 * to get ping_have_color and ping_have_non_bw
8182 */
8183 for (i=0; i<n; i++)
8184 {
8185 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008186 {
glennrpd71e86a2011-02-24 01:28:37 +00008187 if (colormap[i].red != colormap[i].green ||
8188 colormap[i].red != colormap[i].blue)
8189 {
8190 ping_have_color=MagickTrue;
8191 ping_have_non_bw=MagickTrue;
8192 break;
8193 }
8194 }
8195
8196 if (ping_have_non_bw == MagickFalse)
8197 {
8198 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008199 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008200 }
glennrp8bb3a022010-12-13 20:40:04 +00008201 }
8202
glennrpd71e86a2011-02-24 01:28:37 +00008203 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8204 (number_transparent == 0 && number_semitransparent == 0)) &&
8205 (((mng_info->write_png_colortype-1) ==
8206 PNG_COLOR_TYPE_PALETTE) ||
8207 (mng_info->write_png_colortype == 0)))
8208 {
glennrp6185c532011-01-14 17:58:40 +00008209 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008210 {
glennrpd71e86a2011-02-24 01:28:37 +00008211 if (n != (ssize_t) image_colors)
8212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8213 " image_colors (%d) and n (%d) don't match",
8214 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008215
glennrpd71e86a2011-02-24 01:28:37 +00008216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8217 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008218 }
glennrp03812ae2010-12-24 01:31:34 +00008219
glennrpd71e86a2011-02-24 01:28:37 +00008220 image->colors = image_colors;
8221
8222 if (AcquireImageColormap(image,image_colors) ==
8223 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008224 ThrowWriterException(ResourceLimitError,
8225 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008226
8227 for (i=0; i< (ssize_t) image_colors; i++)
8228 image->colormap[i] = colormap[i];
8229
8230 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008231 {
glennrpd71e86a2011-02-24 01:28:37 +00008232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8233 " image->colors=%d (%d)",
8234 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008235
glennrpd71e86a2011-02-24 01:28:37 +00008236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8237 " Update the pixel indexes");
8238 }
glennrp6185c532011-01-14 17:58:40 +00008239
glennrpfd05d622011-02-25 04:10:33 +00008240 /* Sync the pixel indices with the new colormap */
8241
glennrpd71e86a2011-02-24 01:28:37 +00008242 for (y=0; y < (ssize_t) image->rows; y++)
8243 {
8244 q=GetAuthenticPixels(image,0,y,image->columns,1,
8245 exception);
glennrp6185c532011-01-14 17:58:40 +00008246
glennrpd71e86a2011-02-24 01:28:37 +00008247 if (q == (PixelPacket *) NULL)
8248 break;
glennrp6185c532011-01-14 17:58:40 +00008249
glennrpd71e86a2011-02-24 01:28:37 +00008250 indexes=GetAuthenticIndexQueue(image);
glennrpbb4f99d2011-05-22 11:13:17 +00008251
glennrpd71e86a2011-02-24 01:28:37 +00008252 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008253 {
glennrpd71e86a2011-02-24 01:28:37 +00008254 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008255 {
glennrpd71e86a2011-02-24 01:28:37 +00008256 if ((image->matte == MagickFalse ||
glennrpbb4f99d2011-05-22 11:13:17 +00008257 image->colormap[i].opacity ==
glennrpca7ad3a2011-04-26 04:44:54 +00008258 GetOpacityPixelComponent(q)) &&
glennrpbb4f99d2011-05-22 11:13:17 +00008259 image->colormap[i].red ==
glennrpca7ad3a2011-04-26 04:44:54 +00008260 GetRedPixelComponent(q) &&
glennrpbb4f99d2011-05-22 11:13:17 +00008261 image->colormap[i].green ==
glennrpca7ad3a2011-04-26 04:44:54 +00008262 GetGreenPixelComponent(q) &&
glennrpbb4f99d2011-05-22 11:13:17 +00008263 image->colormap[i].blue ==
glennrpca7ad3a2011-04-26 04:44:54 +00008264 GetBluePixelComponent(q))
glennrp6185c532011-01-14 17:58:40 +00008265 {
cristy9fff7b42011-04-29 01:09:31 +00008266 SetIndexPixelComponent(indexes+x,i);
glennrpd71e86a2011-02-24 01:28:37 +00008267 break;
glennrp6185c532011-01-14 17:58:40 +00008268 }
glennrp6185c532011-01-14 17:58:40 +00008269 }
glennrpd71e86a2011-02-24 01:28:37 +00008270 q++;
8271 }
glennrp6185c532011-01-14 17:58:40 +00008272
glennrpd71e86a2011-02-24 01:28:37 +00008273 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8274 break;
8275 }
8276 }
8277 }
8278
8279 if (logging != MagickFalse)
8280 {
8281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8282 " image->colors=%d", (int) image->colors);
8283
8284 if (image->colormap != NULL)
8285 {
8286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8287 " i (red,green,blue,opacity)");
8288
8289 for (i=0; i < (ssize_t) image->colors; i++)
8290 {
cristy72988482011-03-29 16:34:38 +00008291 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008292 {
8293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8294 " %d (%d,%d,%d,%d)",
8295 (int) i,
8296 (int) image->colormap[i].red,
8297 (int) image->colormap[i].green,
8298 (int) image->colormap[i].blue,
8299 (int) image->colormap[i].opacity);
8300 }
glennrp6185c532011-01-14 17:58:40 +00008301 }
8302 }
glennrp03812ae2010-12-24 01:31:34 +00008303
glennrpd71e86a2011-02-24 01:28:37 +00008304 if (number_transparent < 257)
8305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8306 " number_transparent = %d",
8307 number_transparent);
8308 else
glennrp03812ae2010-12-24 01:31:34 +00008309
glennrpd71e86a2011-02-24 01:28:37 +00008310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8311 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008312
glennrpd71e86a2011-02-24 01:28:37 +00008313 if (number_opaque < 257)
8314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8315 " number_opaque = %d",
8316 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008317
glennrpd71e86a2011-02-24 01:28:37 +00008318 else
8319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8320 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008321
glennrpd71e86a2011-02-24 01:28:37 +00008322 if (number_semitransparent < 257)
8323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8324 " number_semitransparent = %d",
8325 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008326
glennrpd71e86a2011-02-24 01:28:37 +00008327 else
8328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8329 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008330
glennrpd71e86a2011-02-24 01:28:37 +00008331 if (ping_have_non_bw == MagickFalse)
8332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8333 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008334
glennrpd71e86a2011-02-24 01:28:37 +00008335 else if (ping_have_color == MagickFalse)
8336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8337 " All pixels and the background are gray");
8338
8339 else
8340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8341 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008342
glennrp03812ae2010-12-24 01:31:34 +00008343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8344 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008345 }
glennrpfd05d622011-02-25 04:10:33 +00008346
glennrpc8c2f062011-02-25 19:00:33 +00008347 if (mng_info->write_png8 == MagickFalse)
8348 break;
glennrpfd05d622011-02-25 04:10:33 +00008349
glennrpc8c2f062011-02-25 19:00:33 +00008350 /* Make any reductions necessary for the PNG8 format */
8351 if (image_colors <= 256 &&
8352 image_colors != 0 && image->colormap != NULL &&
8353 number_semitransparent == 0 &&
8354 number_transparent <= 1)
8355 break;
8356
8357 /* PNG8 can't have semitransparent colors so we threshold the
8358 * opacity to 0 or OpaqueOpacity
8359 */
8360 if (number_semitransparent != 0)
8361 {
8362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8363 " Thresholding the alpha channel to binary");
8364
8365 for (y=0; y < (ssize_t) image->rows; y++)
8366 {
8367 r=GetAuthenticPixels(image,0,y,image->columns,1,
8368 exception);
8369
8370 if (r == (PixelPacket *) NULL)
8371 break;
8372
8373 for (x=0; x < (ssize_t) image->columns; x++)
8374 {
glennrp8ca51ad2011-05-12 21:22:32 +00008375 if (GetOpacityPixelComponent(r) > TransparentOpacity/2)
8376 {
8377 SetOpacityPixelComponent(r,TransparentOpacity);
cristy8d74fc12011-05-25 23:02:47 +00008378 SetRGBPixelComponents(r,&image->background_color);
glennrp8ca51ad2011-05-12 21:22:32 +00008379 }
8380 else
8381 SetOpacityPixelComponent(r,OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00008382 r++;
8383 }
glennrpbb4f99d2011-05-22 11:13:17 +00008384
glennrpc8c2f062011-02-25 19:00:33 +00008385 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8386 break;
8387
8388 if (image_colors != 0 && image_colors <= 256 &&
8389 image->colormap != NULL)
8390 for (i=0; i<image_colors; i++)
8391 image->colormap[i].opacity =
glennrp77110c32011-05-03 05:25:16 +00008392 (image->colormap[i].opacity > TransparentOpacity/2 ?
8393 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00008394 }
8395 continue;
8396 }
8397
8398 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008399 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8400 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8401 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008402 */
glennrpd3371642011-03-22 19:42:23 +00008403 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8404 {
8405 if (logging != MagickFalse)
8406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8407 " Quantizing the background color to 4-4-4");
8408
8409 tried_444 = MagickTrue;
8410
glennrp8e58efd2011-05-20 12:16:29 +00008411 LBR04RGBPixelPacketComponent(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008412
8413 if (logging != MagickFalse)
8414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8415 " Quantizing the pixel colors to 4-4-4");
8416
8417 if (image->colormap == NULL)
8418 {
8419 for (y=0; y < (ssize_t) image->rows; y++)
8420 {
8421 r=GetAuthenticPixels(image,0,y,image->columns,1,
8422 exception);
8423
8424 if (r == (PixelPacket *) NULL)
8425 break;
8426
8427 for (x=0; x < (ssize_t) image->columns; x++)
8428 {
glennrp8ca51ad2011-05-12 21:22:32 +00008429 if (GetOpacityPixelComponent(r) == OpaqueOpacity)
glennrp8e58efd2011-05-20 12:16:29 +00008430 LBR04RGBPixelComponent(r);
glennrpd3371642011-03-22 19:42:23 +00008431 r++;
8432 }
glennrpbb4f99d2011-05-22 11:13:17 +00008433
glennrpd3371642011-03-22 19:42:23 +00008434 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8435 break;
8436 }
8437 }
8438
8439 else /* Should not reach this; colormap already exists and
8440 must be <= 256 */
8441 {
8442 if (logging != MagickFalse)
8443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8444 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008445
glennrpd3371642011-03-22 19:42:23 +00008446 for (i=0; i<image_colors; i++)
8447 {
glennrp8e58efd2011-05-20 12:16:29 +00008448 LBR04RGBPixelPacketComponent(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008449 }
8450 }
8451 continue;
8452 }
8453
glennrp82b3c532011-03-22 19:20:54 +00008454 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8455 {
8456 if (logging != MagickFalse)
8457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8458 " Quantizing the background color to 3-3-3");
8459
8460 tried_333 = MagickTrue;
8461
glennrp8e58efd2011-05-20 12:16:29 +00008462 LBR03RGBPixelPacketComponent(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008463
8464 if (logging != MagickFalse)
8465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008466 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008467
8468 if (image->colormap == NULL)
8469 {
8470 for (y=0; y < (ssize_t) image->rows; y++)
8471 {
8472 r=GetAuthenticPixels(image,0,y,image->columns,1,
8473 exception);
8474
8475 if (r == (PixelPacket *) NULL)
8476 break;
8477
8478 for (x=0; x < (ssize_t) image->columns; x++)
8479 {
glennrp8ca51ad2011-05-12 21:22:32 +00008480 if (GetOpacityPixelComponent(r) == OpaqueOpacity)
glennrp8e58efd2011-05-20 12:16:29 +00008481 LBR03RGBPixelComponent(r);
glennrp82b3c532011-03-22 19:20:54 +00008482 r++;
8483 }
glennrpbb4f99d2011-05-22 11:13:17 +00008484
glennrp82b3c532011-03-22 19:20:54 +00008485 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8486 break;
8487 }
8488 }
8489
8490 else /* Should not reach this; colormap already exists and
8491 must be <= 256 */
8492 {
8493 if (logging != MagickFalse)
8494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008495 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008496 for (i=0; i<image_colors; i++)
8497 {
glennrp8e58efd2011-05-20 12:16:29 +00008498 LBR03RGBPixelPacketComponent(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008499 }
glennrpd3371642011-03-22 19:42:23 +00008500 }
8501 continue;
glennrp82b3c532011-03-22 19:20:54 +00008502 }
glennrpc8c2f062011-02-25 19:00:33 +00008503
glennrp8ca51ad2011-05-12 21:22:32 +00008504 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008505 {
8506 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008508 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008509
glennrp8ca51ad2011-05-12 21:22:32 +00008510 tried_332 = MagickTrue;
8511
glennrp3faa9a32011-04-23 14:00:25 +00008512 /* Red and green were already done so we only quantize the blue
8513 * channel
8514 */
8515
glennrp8e58efd2011-05-20 12:16:29 +00008516 LBR02BluePixelPacketComponent(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008517
glennrpc8c2f062011-02-25 19:00:33 +00008518 if (logging != MagickFalse)
8519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008520 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008521
glennrpc8c2f062011-02-25 19:00:33 +00008522 if (image->colormap == NULL)
8523 {
8524 for (y=0; y < (ssize_t) image->rows; y++)
8525 {
8526 r=GetAuthenticPixels(image,0,y,image->columns,1,
8527 exception);
8528
8529 if (r == (PixelPacket *) NULL)
8530 break;
8531
8532 for (x=0; x < (ssize_t) image->columns; x++)
8533 {
glennrp8ca51ad2011-05-12 21:22:32 +00008534 if (GetOpacityPixelComponent(r) == OpaqueOpacity)
glennrp8e58efd2011-05-20 12:16:29 +00008535 LBR02BluePixelComponent(r);
glennrp52a479c2011-02-26 21:14:38 +00008536 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008537 }
glennrpbb4f99d2011-05-22 11:13:17 +00008538
glennrpc8c2f062011-02-25 19:00:33 +00008539 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8540 break;
8541 }
8542 }
glennrpfd05d622011-02-25 04:10:33 +00008543
glennrpc8c2f062011-02-25 19:00:33 +00008544 else /* Should not reach this; colormap already exists and
8545 must be <= 256 */
8546 {
8547 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008549 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008550 for (i=0; i<image_colors; i++)
8551 {
glennrp8e58efd2011-05-20 12:16:29 +00008552 LBR02BluePixelPacketComponent(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008553 }
8554 }
8555 continue;
8556 }
8557 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008558
8559 if (image_colors == 0 || image_colors > 256)
8560 {
8561 /* Take care of special case with 256 colors + 1 transparent
8562 * color. We don't need to quantize to 2-3-2-1; we only need to
8563 * eliminate one color, so we'll merge the two darkest red
8564 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8565 */
8566 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8567 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8568 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8569 {
8570 image->background_color.red=ScaleCharToQuantum(0x24);
8571 }
glennrpbb4f99d2011-05-22 11:13:17 +00008572
glennrp8ca51ad2011-05-12 21:22:32 +00008573 if (image->colormap == NULL)
8574 {
8575 for (y=0; y < (ssize_t) image->rows; y++)
8576 {
8577 r=GetAuthenticPixels(image,0,y,image->columns,1,
8578 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008579
glennrp8ca51ad2011-05-12 21:22:32 +00008580 if (r == (PixelPacket *) NULL)
8581 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008582
glennrp8ca51ad2011-05-12 21:22:32 +00008583 for (x=0; x < (ssize_t) image->columns; x++)
8584 {
8585 if (ScaleQuantumToChar(GetRedPixelComponent(r)) == 0x49 &&
8586 ScaleQuantumToChar(GetGreenPixelComponent(r)) == 0x00 &&
8587 ScaleQuantumToChar(GetBluePixelComponent(r)) == 0x00 &&
8588 GetOpacityPixelComponent(r) == OpaqueOpacity)
8589 {
8590 SetRedPixelComponent(r,ScaleCharToQuantum(0x24));
8591 }
8592 r++;
8593 }
glennrpbb4f99d2011-05-22 11:13:17 +00008594
glennrp8ca51ad2011-05-12 21:22:32 +00008595 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8596 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008597
glennrp8ca51ad2011-05-12 21:22:32 +00008598 }
8599 }
8600
8601 else
8602 {
8603 for (i=0; i<image_colors; i++)
8604 {
8605 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8606 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8607 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8608 {
8609 image->colormap[i].red=ScaleCharToQuantum(0x24);
8610 }
8611 }
8612 }
8613 }
glennrpd71e86a2011-02-24 01:28:37 +00008614 }
glennrpfd05d622011-02-25 04:10:33 +00008615 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008616
glennrpfd05d622011-02-25 04:10:33 +00008617 /* If we are excluding the tRNS chunk and there is transparency,
8618 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8619 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008620 */
glennrp0e8ea192010-12-24 18:00:33 +00008621 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8622 (number_transparent != 0 || number_semitransparent != 0))
8623 {
glennrpd17915c2011-04-29 14:24:22 +00008624 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008625
8626 if (ping_have_color == MagickFalse)
8627 mng_info->write_png_colortype = 5;
8628
8629 else
8630 mng_info->write_png_colortype = 7;
8631
glennrp8d579662011-02-23 02:05:02 +00008632 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008633 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008634 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008635
glennrp0e8ea192010-12-24 18:00:33 +00008636 }
8637
glennrpfd05d622011-02-25 04:10:33 +00008638 /* See if cheap transparency is possible. It is only possible
8639 * when there is a single transparent color, no semitransparent
8640 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008641 * as the transparent color. We only need this information if
8642 * we are writing a PNG with colortype 0 or 2, and we have not
8643 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008644 */
glennrp5a39f372011-02-25 04:52:16 +00008645 if (number_transparent == 1 &&
8646 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008647 {
8648 ping_have_cheap_transparency = MagickTrue;
8649
8650 if (number_semitransparent != 0)
8651 ping_have_cheap_transparency = MagickFalse;
8652
8653 else if (image_colors == 0 || image_colors > 256 ||
8654 image->colormap == NULL)
8655 {
8656 ExceptionInfo
8657 *exception;
8658
8659 register const PixelPacket
8660 *q;
8661
8662 exception=(&image->exception);
8663
8664 for (y=0; y < (ssize_t) image->rows; y++)
8665 {
8666 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8667
8668 if (q == (PixelPacket *) NULL)
8669 break;
8670
8671 for (x=0; x < (ssize_t) image->columns; x++)
8672 {
8673 if (q->opacity != TransparentOpacity &&
glennrp7c7b3152011-04-26 04:01:27 +00008674 (unsigned short) GetRedPixelComponent(q) ==
8675 ping_trans_color.red &&
8676 (unsigned short) GetGreenPixelComponent(q) ==
8677 ping_trans_color.green &&
8678 (unsigned short) GetBluePixelComponent(q) ==
8679 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008680 {
8681 ping_have_cheap_transparency = MagickFalse;
8682 break;
8683 }
8684
8685 q++;
8686 }
glennrpbb4f99d2011-05-22 11:13:17 +00008687
glennrpfd05d622011-02-25 04:10:33 +00008688 if (ping_have_cheap_transparency == MagickFalse)
8689 break;
8690 }
8691 }
8692 else
8693 {
glennrp67b9c1a2011-04-22 18:47:36 +00008694 /* Assuming that image->colormap[0] is the one transparent color
8695 * and that all others are opaque.
8696 */
glennrpfd05d622011-02-25 04:10:33 +00008697 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008698 for (i=1; i<image_colors; i++)
8699 if (image->colormap[i].red == image->colormap[0].red &&
8700 image->colormap[i].green == image->colormap[0].green &&
8701 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008702 {
glennrp67b9c1a2011-04-22 18:47:36 +00008703 ping_have_cheap_transparency = MagickFalse;
8704 break;
glennrpfd05d622011-02-25 04:10:33 +00008705 }
8706 }
glennrpbb4f99d2011-05-22 11:13:17 +00008707
glennrpfd05d622011-02-25 04:10:33 +00008708 if (logging != MagickFalse)
8709 {
8710 if (ping_have_cheap_transparency == MagickFalse)
8711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8712 " Cheap transparency is not possible.");
8713
8714 else
8715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8716 " Cheap transparency is possible.");
8717 }
8718 }
8719 else
8720 ping_have_cheap_transparency = MagickFalse;
8721
glennrp8640fb52010-11-23 15:48:26 +00008722 image_depth=image->depth;
8723
glennrp26c990a2010-11-23 02:23:20 +00008724 quantum_info = (QuantumInfo *) NULL;
8725 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008726 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008727 image_matte=image->matte;
8728
glennrp0fe50b42010-11-16 03:52:51 +00008729 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008730 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008731
glennrp52a479c2011-02-26 21:14:38 +00008732 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8733 (image->colors == 0 || image->colormap == NULL))
8734 {
glennrp52a479c2011-02-26 21:14:38 +00008735 image_info=DestroyImageInfo(image_info);
8736 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008737 (void) ThrowMagickException(&IMimage->exception,
8738 GetMagickModule(),CoderError,
8739 "Cannot write PNG8 or color-type 3; colormap is NULL",
8740 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008741#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8742 UnlockSemaphoreInfo(ping_semaphore);
8743#endif
8744 return(MagickFalse);
8745 }
8746
cristy3ed852e2009-09-05 21:47:34 +00008747 /*
8748 Allocate the PNG structures
8749 */
8750#ifdef PNG_USER_MEM_SUPPORTED
8751 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008752 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8753 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008754
cristy3ed852e2009-09-05 21:47:34 +00008755#else
8756 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008757 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008758
cristy3ed852e2009-09-05 21:47:34 +00008759#endif
8760 if (ping == (png_struct *) NULL)
8761 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008762
cristy3ed852e2009-09-05 21:47:34 +00008763 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008764
cristy3ed852e2009-09-05 21:47:34 +00008765 if (ping_info == (png_info *) NULL)
8766 {
8767 png_destroy_write_struct(&ping,(png_info **) NULL);
8768 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8769 }
glennrp0fe50b42010-11-16 03:52:51 +00008770
cristy3ed852e2009-09-05 21:47:34 +00008771 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008772 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008773
glennrp5af765f2010-03-30 11:12:18 +00008774 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008775 {
8776 /*
8777 PNG write failed.
8778 */
8779#ifdef PNG_DEBUG
8780 if (image_info->verbose)
8781 (void) printf("PNG write has failed.\n");
8782#endif
8783 png_destroy_write_struct(&ping,&ping_info);
8784#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008785 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008786#endif
glennrpda8f3a72011-02-27 23:54:12 +00008787 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008788 (void) CloseBlob(image);
8789 image_info=DestroyImageInfo(image_info);
8790 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008791 return(MagickFalse);
8792 }
8793 /*
8794 Prepare PNG for writing.
8795 */
8796#if defined(PNG_MNG_FEATURES_SUPPORTED)
8797 if (mng_info->write_mng)
8798 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008799
cristy3ed852e2009-09-05 21:47:34 +00008800#else
8801# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8802 if (mng_info->write_mng)
8803 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008804
cristy3ed852e2009-09-05 21:47:34 +00008805# endif
8806#endif
glennrp2b013e42010-11-24 16:55:50 +00008807
cristy3ed852e2009-09-05 21:47:34 +00008808 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008809
cristy4e5bc842010-06-09 13:56:01 +00008810 ping_width=(png_uint_32) image->columns;
8811 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008812
cristy3ed852e2009-09-05 21:47:34 +00008813 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8814 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008815
cristy3ed852e2009-09-05 21:47:34 +00008816 if (mng_info->write_png_depth != 0)
8817 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008818
cristy3ed852e2009-09-05 21:47:34 +00008819 /* Adjust requested depth to next higher valid depth if necessary */
8820 if (image_depth > 8)
8821 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008822
cristy3ed852e2009-09-05 21:47:34 +00008823 if ((image_depth > 4) && (image_depth < 8))
8824 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008825
cristy3ed852e2009-09-05 21:47:34 +00008826 if (image_depth == 3)
8827 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008828
cristy3ed852e2009-09-05 21:47:34 +00008829 if (logging != MagickFalse)
8830 {
8831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008832 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008834 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008836 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008838 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008840 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008841 }
glennrp8640fb52010-11-23 15:48:26 +00008842
cristy3ed852e2009-09-05 21:47:34 +00008843 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008844 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008845
glennrp26f37912010-12-23 16:22:42 +00008846
cristy3ed852e2009-09-05 21:47:34 +00008847#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008848 if (ping_exclude_pHYs == MagickFalse)
8849 {
cristy3ed852e2009-09-05 21:47:34 +00008850 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8851 (!mng_info->write_mng || !mng_info->equal_physs))
8852 {
glennrp0fe50b42010-11-16 03:52:51 +00008853 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8855 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008856
8857 if (image->units == PixelsPerInchResolution)
8858 {
glennrpdfd70802010-11-14 01:23:35 +00008859 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008860 ping_pHYs_x_resolution=
8861 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8862 ping_pHYs_y_resolution=
8863 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008864 }
glennrpdfd70802010-11-14 01:23:35 +00008865
cristy3ed852e2009-09-05 21:47:34 +00008866 else if (image->units == PixelsPerCentimeterResolution)
8867 {
glennrpdfd70802010-11-14 01:23:35 +00008868 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008869 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8870 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008871 }
glennrp991d11d2010-11-12 21:55:28 +00008872
cristy3ed852e2009-09-05 21:47:34 +00008873 else
8874 {
glennrpdfd70802010-11-14 01:23:35 +00008875 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8876 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8877 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008878 }
glennrp991d11d2010-11-12 21:55:28 +00008879
glennrp823b55c2011-03-14 18:46:46 +00008880 if (logging != MagickFalse)
8881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8882 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8883 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8884 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008885 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008886 }
glennrp26f37912010-12-23 16:22:42 +00008887 }
cristy3ed852e2009-09-05 21:47:34 +00008888#endif
glennrpa521b2f2010-10-29 04:11:03 +00008889
glennrp26f37912010-12-23 16:22:42 +00008890 if (ping_exclude_bKGD == MagickFalse)
8891 {
glennrpa521b2f2010-10-29 04:11:03 +00008892 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008893 {
glennrpa521b2f2010-10-29 04:11:03 +00008894 unsigned int
8895 mask;
cristy3ed852e2009-09-05 21:47:34 +00008896
glennrpa521b2f2010-10-29 04:11:03 +00008897 mask=0xffff;
8898 if (ping_bit_depth == 8)
8899 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008900
glennrpa521b2f2010-10-29 04:11:03 +00008901 if (ping_bit_depth == 4)
8902 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008903
glennrpa521b2f2010-10-29 04:11:03 +00008904 if (ping_bit_depth == 2)
8905 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008906
glennrpa521b2f2010-10-29 04:11:03 +00008907 if (ping_bit_depth == 1)
8908 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008909
glennrpa521b2f2010-10-29 04:11:03 +00008910 ping_background.red=(png_uint_16)
8911 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008912
glennrpa521b2f2010-10-29 04:11:03 +00008913 ping_background.green=(png_uint_16)
8914 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008915
glennrpa521b2f2010-10-29 04:11:03 +00008916 ping_background.blue=(png_uint_16)
8917 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00008918
8919 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00008920 }
cristy3ed852e2009-09-05 21:47:34 +00008921
glennrp0fe50b42010-11-16 03:52:51 +00008922 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008923 {
8924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8925 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00008926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8927 " background_color index is %d",
8928 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008929
8930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8931 " ping_bit_depth=%d",ping_bit_depth);
8932 }
glennrp0fe50b42010-11-16 03:52:51 +00008933
8934 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008935 }
glennrp0fe50b42010-11-16 03:52:51 +00008936
cristy3ed852e2009-09-05 21:47:34 +00008937 /*
8938 Select the color type.
8939 */
8940 matte=image_matte;
8941 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008942
glennrp1273f7b2011-02-24 03:20:30 +00008943 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008944 {
glennrp0fe50b42010-11-16 03:52:51 +00008945
glennrpfd05d622011-02-25 04:10:33 +00008946 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008947 for reducing the sample depth from 8. */
8948
glennrp0fe50b42010-11-16 03:52:51 +00008949 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008950
glennrp8bb3a022010-12-13 20:40:04 +00008951 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008952
8953 /*
8954 Set image palette.
8955 */
8956 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8957
glennrp0fe50b42010-11-16 03:52:51 +00008958 if (logging != MagickFalse)
8959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8960 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008961 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008962
8963 for (i=0; i < (ssize_t) number_colors; i++)
8964 {
8965 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8966 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8967 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8968 if (logging != MagickFalse)
8969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008970#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008971 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008972#else
8973 " %5ld (%5d,%5d,%5d)",
8974#endif
glennrp0fe50b42010-11-16 03:52:51 +00008975 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8976
8977 }
glennrp2b013e42010-11-24 16:55:50 +00008978
glennrp8bb3a022010-12-13 20:40:04 +00008979 ping_have_PLTE=MagickTrue;
8980 image_depth=ping_bit_depth;
8981 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008982
glennrp58e01762011-01-07 15:28:54 +00008983 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008984 {
glennrp0fe50b42010-11-16 03:52:51 +00008985 /*
8986 Identify which colormap entry is transparent.
8987 */
8988 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008989 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008990
glennrp8bb3a022010-12-13 20:40:04 +00008991 for (i=0; i < (ssize_t) number_transparent; i++)
8992 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008993
glennrp0fe50b42010-11-16 03:52:51 +00008994
glennrp2cc891a2010-12-24 13:44:32 +00008995 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008996 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008997
8998 if (ping_num_trans == 0)
8999 ping_have_tRNS=MagickFalse;
9000
glennrp8bb3a022010-12-13 20:40:04 +00009001 else
9002 ping_have_tRNS=MagickTrue;
9003 }
glennrp0fe50b42010-11-16 03:52:51 +00009004
glennrp1273f7b2011-02-24 03:20:30 +00009005 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009006 {
glennrp1273f7b2011-02-24 03:20:30 +00009007 /*
9008 * Identify which colormap entry is the background color.
9009 */
9010
glennrp4f25bd02011-01-01 18:51:28 +00009011 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9012 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9013 break;
glennrp0fe50b42010-11-16 03:52:51 +00009014
glennrp4f25bd02011-01-01 18:51:28 +00009015 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009016
9017 if (logging != MagickFalse)
9018 {
9019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9020 " background_color index is %d",
9021 (int) ping_background.index);
9022 }
glennrp4f25bd02011-01-01 18:51:28 +00009023 }
cristy3ed852e2009-09-05 21:47:34 +00009024 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009025
cristy3ed852e2009-09-05 21:47:34 +00009026 else if (mng_info->write_png24)
9027 {
9028 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009029 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009030 }
glennrp0fe50b42010-11-16 03:52:51 +00009031
cristy3ed852e2009-09-05 21:47:34 +00009032 else if (mng_info->write_png32)
9033 {
9034 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009035 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009036 }
glennrp0fe50b42010-11-16 03:52:51 +00009037
glennrp8bb3a022010-12-13 20:40:04 +00009038 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009039 {
glennrp5af765f2010-03-30 11:12:18 +00009040 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009041
glennrp8bb3a022010-12-13 20:40:04 +00009042 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009043 {
glennrp5af765f2010-03-30 11:12:18 +00009044 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009045
glennrp5af765f2010-03-30 11:12:18 +00009046 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9047 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009048 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009049
glennrp8bb3a022010-12-13 20:40:04 +00009050 else
9051 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009052
9053 if (logging != MagickFalse)
9054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9055 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009056 }
glennrp0fe50b42010-11-16 03:52:51 +00009057
glennrp7c4c9e62011-03-21 20:23:32 +00009058 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009059 {
9060 if (logging != MagickFalse)
9061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009062 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009063
glennrpd6bf1612010-12-17 17:28:54 +00009064 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009065 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009066
glennrpd6bf1612010-12-17 17:28:54 +00009067 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009068 {
glennrp5af765f2010-03-30 11:12:18 +00009069 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009070 image_matte=MagickFalse;
9071 }
glennrp0fe50b42010-11-16 03:52:51 +00009072
glennrpd6bf1612010-12-17 17:28:54 +00009073 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009074 {
glennrp5af765f2010-03-30 11:12:18 +00009075 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009076 image_matte=MagickTrue;
9077 }
glennrp0fe50b42010-11-16 03:52:51 +00009078
glennrp5aa37f62011-01-02 03:07:57 +00009079 if (image_info->type == PaletteType ||
9080 image_info->type == PaletteMatteType)
9081 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9082
glennrp7c4c9e62011-03-21 20:23:32 +00009083 if (mng_info->write_png_colortype == 0 &&
9084 (image_info->type == UndefinedType ||
9085 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009086 {
glennrp5aa37f62011-01-02 03:07:57 +00009087 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009088 {
glennrp5aa37f62011-01-02 03:07:57 +00009089 if (image_matte == MagickFalse)
9090 {
9091 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9092 image_matte=MagickFalse;
9093 }
glennrp0fe50b42010-11-16 03:52:51 +00009094
glennrp0b206f52011-01-07 04:55:32 +00009095 else
glennrp5aa37f62011-01-02 03:07:57 +00009096 {
9097 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9098 image_matte=MagickTrue;
9099 }
9100 }
9101 else
glennrp8bb3a022010-12-13 20:40:04 +00009102 {
glennrp5aa37f62011-01-02 03:07:57 +00009103 if (image_matte == MagickFalse)
9104 {
9105 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9106 image_matte=MagickFalse;
9107 }
glennrp8bb3a022010-12-13 20:40:04 +00009108
glennrp0b206f52011-01-07 04:55:32 +00009109 else
glennrp5aa37f62011-01-02 03:07:57 +00009110 {
9111 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9112 image_matte=MagickTrue;
9113 }
9114 }
glennrp0fe50b42010-11-16 03:52:51 +00009115 }
glennrp5aa37f62011-01-02 03:07:57 +00009116
cristy3ed852e2009-09-05 21:47:34 +00009117 }
glennrp0fe50b42010-11-16 03:52:51 +00009118
cristy3ed852e2009-09-05 21:47:34 +00009119 if (logging != MagickFalse)
9120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009121 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009122
glennrp5af765f2010-03-30 11:12:18 +00009123 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009124 {
9125 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9126 ping_color_type == PNG_COLOR_TYPE_RGB ||
9127 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9128 ping_bit_depth=8;
9129 }
cristy3ed852e2009-09-05 21:47:34 +00009130
glennrpd6bf1612010-12-17 17:28:54 +00009131 old_bit_depth=ping_bit_depth;
9132
glennrp5af765f2010-03-30 11:12:18 +00009133 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009134 {
glennrp8d579662011-02-23 02:05:02 +00009135 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9136 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009137 }
glennrp8640fb52010-11-23 15:48:26 +00009138
glennrp5af765f2010-03-30 11:12:18 +00009139 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009140 {
cristy35ef8242010-06-03 16:24:13 +00009141 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009142 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009143
9144 if (image->colors == 0)
9145 {
glennrp0fe50b42010-11-16 03:52:51 +00009146 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00009147 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00009148 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009149 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009150 }
9151
cristy35ef8242010-06-03 16:24:13 +00009152 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009153 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009154 }
glennrp2b013e42010-11-24 16:55:50 +00009155
glennrpd6bf1612010-12-17 17:28:54 +00009156 if (logging != MagickFalse)
9157 {
9158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9159 " Number of colors: %.20g",(double) image_colors);
9160
9161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9162 " Tentative PNG bit depth: %d",ping_bit_depth);
9163 }
9164
9165 if (ping_bit_depth < (int) mng_info->write_png_depth)
9166 ping_bit_depth = mng_info->write_png_depth;
9167 }
glennrp2cc891a2010-12-24 13:44:32 +00009168
glennrp5af765f2010-03-30 11:12:18 +00009169 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009170
cristy3ed852e2009-09-05 21:47:34 +00009171 if (logging != MagickFalse)
9172 {
9173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009174 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009175
cristy3ed852e2009-09-05 21:47:34 +00009176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009177 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009178
cristy3ed852e2009-09-05 21:47:34 +00009179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009180 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009181
cristy3ed852e2009-09-05 21:47:34 +00009182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009183
glennrp8640fb52010-11-23 15:48:26 +00009184 " image->depth: %.20g",(double) image->depth);
9185
9186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009187 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009188 }
9189
glennrp58e01762011-01-07 15:28:54 +00009190 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009191 {
glennrp4f25bd02011-01-01 18:51:28 +00009192 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009193 {
glennrp7c4c9e62011-03-21 20:23:32 +00009194 if (mng_info->write_png_colortype == 0)
9195 {
9196 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009197
glennrp7c4c9e62011-03-21 20:23:32 +00009198 if (ping_have_color != MagickFalse)
9199 ping_color_type=PNG_COLOR_TYPE_RGBA;
9200 }
glennrp4f25bd02011-01-01 18:51:28 +00009201
9202 /*
9203 * Determine if there is any transparent color.
9204 */
9205 if (number_transparent + number_semitransparent == 0)
9206 {
9207 /*
9208 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9209 */
glennrpa6a06632011-01-19 15:15:34 +00009210
glennrp4f25bd02011-01-01 18:51:28 +00009211 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009212
9213 if (mng_info->write_png_colortype == 0)
9214 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009215 }
9216
9217 else
9218 {
9219 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009220 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009221
9222 mask=0xffff;
9223
9224 if (ping_bit_depth == 8)
9225 mask=0x00ff;
9226
9227 if (ping_bit_depth == 4)
9228 mask=0x000f;
9229
9230 if (ping_bit_depth == 2)
9231 mask=0x0003;
9232
9233 if (ping_bit_depth == 1)
9234 mask=0x0001;
9235
9236 ping_trans_color.red=(png_uint_16)
9237 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9238
9239 ping_trans_color.green=(png_uint_16)
9240 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9241
9242 ping_trans_color.blue=(png_uint_16)
9243 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9244
9245 ping_trans_color.gray=(png_uint_16)
9246 (ScaleQuantumToShort(PixelIntensityToQuantum(
9247 image->colormap)) & mask);
9248
9249 ping_trans_color.index=(png_byte) 0;
9250
9251 ping_have_tRNS=MagickTrue;
9252 }
9253
9254 if (ping_have_tRNS != MagickFalse)
9255 {
9256 /*
glennrpfd05d622011-02-25 04:10:33 +00009257 * Determine if there is one and only one transparent color
9258 * and if so if it is fully transparent.
9259 */
9260 if (ping_have_cheap_transparency == MagickFalse)
9261 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009262 }
9263
9264 if (ping_have_tRNS != MagickFalse)
9265 {
glennrp7c4c9e62011-03-21 20:23:32 +00009266 if (mng_info->write_png_colortype == 0)
9267 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009268
9269 if (image_depth == 8)
9270 {
9271 ping_trans_color.red&=0xff;
9272 ping_trans_color.green&=0xff;
9273 ping_trans_color.blue&=0xff;
9274 ping_trans_color.gray&=0xff;
9275 }
9276 }
9277 }
cristy3ed852e2009-09-05 21:47:34 +00009278 else
9279 {
cristy3ed852e2009-09-05 21:47:34 +00009280 if (image_depth == 8)
9281 {
glennrp5af765f2010-03-30 11:12:18 +00009282 ping_trans_color.red&=0xff;
9283 ping_trans_color.green&=0xff;
9284 ping_trans_color.blue&=0xff;
9285 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009286 }
9287 }
9288 }
glennrp8640fb52010-11-23 15:48:26 +00009289
cristy3ed852e2009-09-05 21:47:34 +00009290 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009291
glennrp2e09f552010-11-14 00:38:48 +00009292 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009293 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009294
glennrp39992b42010-11-14 00:03:43 +00009295 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009296 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009297 ping_have_color == MagickFalse &&
9298 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009299 {
cristy35ef8242010-06-03 16:24:13 +00009300 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009301
cristy3ed852e2009-09-05 21:47:34 +00009302 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009303 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009304
glennrp7c4c9e62011-03-21 20:23:32 +00009305 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009306 {
glennrp5af765f2010-03-30 11:12:18 +00009307 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009308
cristy3ed852e2009-09-05 21:47:34 +00009309 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009310 {
9311 if (logging != MagickFalse)
9312 {
9313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9314 " Scaling ping_trans_color (0)");
9315 }
9316 ping_trans_color.gray*=0x0101;
9317 }
cristy3ed852e2009-09-05 21:47:34 +00009318 }
glennrp0fe50b42010-11-16 03:52:51 +00009319
cristy3ed852e2009-09-05 21:47:34 +00009320 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9321 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009322
glennrp136ee3a2011-04-27 15:47:45 +00009323 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009324 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009325 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009326
cristy3ed852e2009-09-05 21:47:34 +00009327 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009328 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009329
cristy3ed852e2009-09-05 21:47:34 +00009330 else
9331 {
glennrp5af765f2010-03-30 11:12:18 +00009332 ping_bit_depth=8;
9333 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009334 {
9335 if(!mng_info->write_png_depth)
9336 {
glennrp5af765f2010-03-30 11:12:18 +00009337 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009338
cristy35ef8242010-06-03 16:24:13 +00009339 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009340 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009341 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009342 }
9343 }
glennrp2b013e42010-11-24 16:55:50 +00009344
glennrp0fe50b42010-11-16 03:52:51 +00009345 else if (ping_color_type ==
9346 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009347 mng_info->IsPalette)
9348 {
cristy3ed852e2009-09-05 21:47:34 +00009349 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009350
cristy3ed852e2009-09-05 21:47:34 +00009351 int
9352 depth_4_ok=MagickTrue,
9353 depth_2_ok=MagickTrue,
9354 depth_1_ok=MagickTrue;
9355
cristybb503372010-05-27 20:51:26 +00009356 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009357 {
9358 unsigned char
9359 intensity;
9360
9361 intensity=ScaleQuantumToChar(image->colormap[i].red);
9362
9363 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9364 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9365 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9366 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009367 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009368 depth_1_ok=MagickFalse;
9369 }
glennrp2b013e42010-11-24 16:55:50 +00009370
cristy3ed852e2009-09-05 21:47:34 +00009371 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009372 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009373
cristy3ed852e2009-09-05 21:47:34 +00009374 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009375 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009376
cristy3ed852e2009-09-05 21:47:34 +00009377 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009378 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009379 }
9380 }
glennrp2b013e42010-11-24 16:55:50 +00009381
glennrp5af765f2010-03-30 11:12:18 +00009382 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009383 }
glennrp0fe50b42010-11-16 03:52:51 +00009384
cristy3ed852e2009-09-05 21:47:34 +00009385 else
glennrp0fe50b42010-11-16 03:52:51 +00009386
cristy3ed852e2009-09-05 21:47:34 +00009387 if (mng_info->IsPalette)
9388 {
glennrp17a14852010-05-10 03:01:59 +00009389 number_colors=image_colors;
9390
cristy3ed852e2009-09-05 21:47:34 +00009391 if (image_depth <= 8)
9392 {
cristy3ed852e2009-09-05 21:47:34 +00009393 /*
9394 Set image palette.
9395 */
glennrp5af765f2010-03-30 11:12:18 +00009396 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009397
glennrp58e01762011-01-07 15:28:54 +00009398 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009399 {
glennrp9c1eb072010-06-06 22:19:15 +00009400 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009401
glennrp3b51f0e2010-11-27 18:14:08 +00009402 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9404 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009405 }
glennrp0fe50b42010-11-16 03:52:51 +00009406
cristy3ed852e2009-09-05 21:47:34 +00009407 else
9408 {
cristybb503372010-05-27 20:51:26 +00009409 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009410 {
9411 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9412 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9413 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9414 }
glennrp0fe50b42010-11-16 03:52:51 +00009415
glennrp3b51f0e2010-11-27 18:14:08 +00009416 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009418 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009419 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009420
glennrp39992b42010-11-14 00:03:43 +00009421 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009422 }
glennrp0fe50b42010-11-16 03:52:51 +00009423
cristy3ed852e2009-09-05 21:47:34 +00009424 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009425 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009426 {
cristybefe4d22010-06-07 01:18:58 +00009427 size_t
9428 one;
9429
glennrp5af765f2010-03-30 11:12:18 +00009430 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009431 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009432
glennrpd17915c2011-04-29 14:24:22 +00009433 while ((one << ping_bit_depth) < (ssize_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009434 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009435 }
glennrp0fe50b42010-11-16 03:52:51 +00009436
glennrp5af765f2010-03-30 11:12:18 +00009437 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009438
glennrp58e01762011-01-07 15:28:54 +00009439 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009440 {
glennrp0fe50b42010-11-16 03:52:51 +00009441 /*
glennrpd6bf1612010-12-17 17:28:54 +00009442 * Set up trans_colors array.
9443 */
glennrp0fe50b42010-11-16 03:52:51 +00009444 assert(number_colors <= 256);
9445
glennrpd6bf1612010-12-17 17:28:54 +00009446 ping_num_trans=(unsigned short) (number_transparent +
9447 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009448
9449 if (ping_num_trans == 0)
9450 ping_have_tRNS=MagickFalse;
9451
glennrpd6bf1612010-12-17 17:28:54 +00009452 else
glennrp0fe50b42010-11-16 03:52:51 +00009453 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009454 if (logging != MagickFalse)
9455 {
9456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9457 " Scaling ping_trans_color (1)");
9458 }
glennrpd6bf1612010-12-17 17:28:54 +00009459 ping_have_tRNS=MagickTrue;
9460
9461 for (i=0; i < ping_num_trans; i++)
9462 {
9463 ping_trans_alpha[i]= (png_byte) (255-
9464 ScaleQuantumToChar(image->colormap[i].opacity));
9465 }
glennrp0fe50b42010-11-16 03:52:51 +00009466 }
9467 }
cristy3ed852e2009-09-05 21:47:34 +00009468 }
9469 }
glennrp0fe50b42010-11-16 03:52:51 +00009470
cristy3ed852e2009-09-05 21:47:34 +00009471 else
9472 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009473
cristy3ed852e2009-09-05 21:47:34 +00009474 if (image_depth < 8)
9475 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009476
cristy3ed852e2009-09-05 21:47:34 +00009477 if ((save_image_depth == 16) && (image_depth == 8))
9478 {
glennrp4f25bd02011-01-01 18:51:28 +00009479 if (logging != MagickFalse)
9480 {
9481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9482 " Scaling ping_trans_color from (%d,%d,%d)",
9483 (int) ping_trans_color.red,
9484 (int) ping_trans_color.green,
9485 (int) ping_trans_color.blue);
9486 }
9487
glennrp5af765f2010-03-30 11:12:18 +00009488 ping_trans_color.red*=0x0101;
9489 ping_trans_color.green*=0x0101;
9490 ping_trans_color.blue*=0x0101;
9491 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009492
9493 if (logging != MagickFalse)
9494 {
9495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9496 " to (%d,%d,%d)",
9497 (int) ping_trans_color.red,
9498 (int) ping_trans_color.green,
9499 (int) ping_trans_color.blue);
9500 }
cristy3ed852e2009-09-05 21:47:34 +00009501 }
9502 }
9503
cristy4383ec82011-01-05 15:42:32 +00009504 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9505 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009506
cristy3ed852e2009-09-05 21:47:34 +00009507 /*
9508 Adjust background and transparency samples in sub-8-bit grayscale files.
9509 */
glennrp5af765f2010-03-30 11:12:18 +00009510 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009511 PNG_COLOR_TYPE_GRAY)
9512 {
9513 png_uint_16
9514 maxval;
9515
cristy35ef8242010-06-03 16:24:13 +00009516 size_t
9517 one=1;
9518
cristy22ffd972010-06-03 16:51:47 +00009519 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009520
glennrp4f25bd02011-01-01 18:51:28 +00009521 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009522 {
cristy3ed852e2009-09-05 21:47:34 +00009523
glennrpa521b2f2010-10-29 04:11:03 +00009524 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00009525 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
9526
9527 if (logging != MagickFalse)
9528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009529 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9531 " background_color index is %d",
9532 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009533
glennrp991d11d2010-11-12 21:55:28 +00009534 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009535 }
cristy3ed852e2009-09-05 21:47:34 +00009536
glennrp5af765f2010-03-30 11:12:18 +00009537 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
9538 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00009539 }
glennrp17a14852010-05-10 03:01:59 +00009540
glennrp26f37912010-12-23 16:22:42 +00009541 if (ping_exclude_bKGD == MagickFalse)
9542 {
glennrp1273f7b2011-02-24 03:20:30 +00009543 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009544 {
9545 /*
9546 Identify which colormap entry is the background color.
9547 */
9548
glennrp17a14852010-05-10 03:01:59 +00009549 number_colors=image_colors;
9550
glennrpa521b2f2010-10-29 04:11:03 +00009551 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9552 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009553 break;
9554
9555 ping_background.index=(png_byte) i;
9556
glennrp3b51f0e2010-11-27 18:14:08 +00009557 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009558 {
9559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009560 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009561 }
glennrp0fe50b42010-11-16 03:52:51 +00009562
cristy13d07042010-11-21 20:56:18 +00009563 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009564 {
9565 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009566
9567 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009568 {
9569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9570 " background =(%d,%d,%d)",
9571 (int) ping_background.red,
9572 (int) ping_background.green,
9573 (int) ping_background.blue);
9574 }
9575 }
glennrpa521b2f2010-10-29 04:11:03 +00009576
glennrpd6bf1612010-12-17 17:28:54 +00009577 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009578 {
glennrp3b51f0e2010-11-27 18:14:08 +00009579 if (logging != MagickFalse)
9580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9581 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009582 ping_have_bKGD = MagickFalse;
9583 }
glennrp17a14852010-05-10 03:01:59 +00009584 }
glennrp26f37912010-12-23 16:22:42 +00009585 }
glennrp17a14852010-05-10 03:01:59 +00009586
cristy3ed852e2009-09-05 21:47:34 +00009587 if (logging != MagickFalse)
9588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009589 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009590 /*
9591 Initialize compression level and filtering.
9592 */
9593 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009594 {
9595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9596 " Setting up deflate compression");
9597
9598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9599 " Compression buffer size: 32768");
9600 }
9601
cristy3ed852e2009-09-05 21:47:34 +00009602 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009603
cristy3ed852e2009-09-05 21:47:34 +00009604 if (logging != MagickFalse)
9605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9606 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009607
cristy3ed852e2009-09-05 21:47:34 +00009608 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00009609
cristy3ed852e2009-09-05 21:47:34 +00009610 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9611 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009612
cristy3ed852e2009-09-05 21:47:34 +00009613 if (quality > 9)
9614 {
9615 int
9616 level;
9617
cristybb503372010-05-27 20:51:26 +00009618 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009619
cristy3ed852e2009-09-05 21:47:34 +00009620 if (logging != MagickFalse)
9621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9622 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00009623
cristy3ed852e2009-09-05 21:47:34 +00009624 png_set_compression_level(ping,level);
9625 }
glennrp0fe50b42010-11-16 03:52:51 +00009626
cristy3ed852e2009-09-05 21:47:34 +00009627 else
9628 {
9629 if (logging != MagickFalse)
9630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00009632
cristy3ed852e2009-09-05 21:47:34 +00009633 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9634 }
glennrp0fe50b42010-11-16 03:52:51 +00009635
cristy3ed852e2009-09-05 21:47:34 +00009636 if (logging != MagickFalse)
9637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9638 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009639
glennrp2b013e42010-11-24 16:55:50 +00009640#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00009641 /* This became available in libpng-1.0.9. Output must be a MNG. */
9642 if (mng_info->write_mng && ((quality % 10) == 7))
9643 {
9644 if (logging != MagickFalse)
9645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9646 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00009647
glennrp5af765f2010-03-30 11:12:18 +00009648 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00009649 }
glennrp0fe50b42010-11-16 03:52:51 +00009650
cristy3ed852e2009-09-05 21:47:34 +00009651 else
9652 if (logging != MagickFalse)
9653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9654 " Filter_type: 0");
9655#endif
glennrp2b013e42010-11-24 16:55:50 +00009656
cristy3ed852e2009-09-05 21:47:34 +00009657 {
9658 int
9659 base_filter;
9660
9661 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00009662 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009663
glennrp26c990a2010-11-23 02:23:20 +00009664 else
glennrp8640fb52010-11-23 15:48:26 +00009665 if ((quality % 10) != 5)
9666 base_filter=(int) quality % 10;
9667
9668 else
9669 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9670 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9671 (quality < 50))
9672 base_filter=PNG_NO_FILTERS;
9673
9674 else
9675 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009676
cristy3ed852e2009-09-05 21:47:34 +00009677 if (logging != MagickFalse)
9678 {
9679 if (base_filter == PNG_ALL_FILTERS)
9680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9681 " Base filter method: ADAPTIVE");
9682 else
9683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9684 " Base filter method: NONE");
9685 }
glennrp2b013e42010-11-24 16:55:50 +00009686
cristy3ed852e2009-09-05 21:47:34 +00009687 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9688 }
9689
glennrp823b55c2011-03-14 18:46:46 +00009690 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9691 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009692 {
9693 ResetImageProfileIterator(image);
9694 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009695 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009696 profile=GetImageProfile(image,name);
9697
9698 if (profile != (StringInfo *) NULL)
9699 {
glennrp5af765f2010-03-30 11:12:18 +00009700#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009701 if ((LocaleCompare(name,"ICC") == 0) ||
9702 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009703 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009704
9705 if (ping_exclude_iCCP == MagickFalse)
9706 {
9707 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009708#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009709 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009710#else
9711 (png_const_bytep) GetStringInfoDatum(profile),
9712#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009713 (png_uint_32) GetStringInfoLength(profile));
9714 }
glennrp26f37912010-12-23 16:22:42 +00009715 }
glennrp0fe50b42010-11-16 03:52:51 +00009716
glennrpc8cbc5d2011-01-01 00:12:34 +00009717 else
cristy3ed852e2009-09-05 21:47:34 +00009718#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009719 if (ping_exclude_zCCP == MagickFalse)
9720 {
glennrpcf002022011-01-30 02:38:15 +00009721 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009722 (unsigned char *) name,(unsigned char *) name,
9723 GetStringInfoDatum(profile),
9724 (png_uint_32) GetStringInfoLength(profile));
9725 }
9726 }
glennrp0b206f52011-01-07 04:55:32 +00009727
glennrpc8cbc5d2011-01-01 00:12:34 +00009728 if (logging != MagickFalse)
9729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9730 " Setting up text chunk with %s profile",name);
9731
9732 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009733 }
cristy3ed852e2009-09-05 21:47:34 +00009734 }
9735
9736#if defined(PNG_WRITE_sRGB_SUPPORTED)
9737 if ((mng_info->have_write_global_srgb == 0) &&
9738 ((image->rendering_intent != UndefinedIntent) ||
9739 (image->colorspace == sRGBColorspace)))
9740 {
glennrp26f37912010-12-23 16:22:42 +00009741 if (ping_exclude_sRGB == MagickFalse)
9742 {
9743 /*
9744 Note image rendering intent.
9745 */
9746 if (logging != MagickFalse)
9747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9748 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009749
glennrp26f37912010-12-23 16:22:42 +00009750 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009751 Magick_RenderingIntent_to_PNG_RenderingIntent(
9752 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009753
glennrp26f37912010-12-23 16:22:42 +00009754 if (ping_exclude_gAMA == MagickFalse)
9755 png_set_gAMA(ping,ping_info,0.45455);
9756 }
cristy3ed852e2009-09-05 21:47:34 +00009757 }
glennrp26f37912010-12-23 16:22:42 +00009758
glennrp5af765f2010-03-30 11:12:18 +00009759 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009760#endif
9761 {
glennrp2cc891a2010-12-24 13:44:32 +00009762 if (ping_exclude_gAMA == MagickFalse &&
9763 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009764 (image->gamma < .45 || image->gamma > .46)))
9765 {
cristy3ed852e2009-09-05 21:47:34 +00009766 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9767 {
9768 /*
9769 Note image gamma.
9770 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9771 */
9772 if (logging != MagickFalse)
9773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9774 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009775
cristy3ed852e2009-09-05 21:47:34 +00009776 png_set_gAMA(ping,ping_info,image->gamma);
9777 }
glennrp26f37912010-12-23 16:22:42 +00009778 }
glennrp2b013e42010-11-24 16:55:50 +00009779
glennrp26f37912010-12-23 16:22:42 +00009780 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009781 {
glennrp26f37912010-12-23 16:22:42 +00009782 if ((mng_info->have_write_global_chrm == 0) &&
9783 (image->chromaticity.red_primary.x != 0.0))
9784 {
9785 /*
9786 Note image chromaticity.
9787 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9788 */
9789 PrimaryInfo
9790 bp,
9791 gp,
9792 rp,
9793 wp;
cristy3ed852e2009-09-05 21:47:34 +00009794
glennrp26f37912010-12-23 16:22:42 +00009795 wp=image->chromaticity.white_point;
9796 rp=image->chromaticity.red_primary;
9797 gp=image->chromaticity.green_primary;
9798 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009799
glennrp26f37912010-12-23 16:22:42 +00009800 if (logging != MagickFalse)
9801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9802 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009803
glennrp26f37912010-12-23 16:22:42 +00009804 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9805 bp.x,bp.y);
9806 }
9807 }
cristy3ed852e2009-09-05 21:47:34 +00009808 }
glennrpdfd70802010-11-14 01:23:35 +00009809
glennrp5af765f2010-03-30 11:12:18 +00009810 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009811
9812 if (mng_info->write_mng)
9813 png_set_sig_bytes(ping,8);
9814
9815 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9816
glennrpd6bf1612010-12-17 17:28:54 +00009817 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009818 {
9819 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009820 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009821 {
glennrp5af765f2010-03-30 11:12:18 +00009822 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009823
glennrp5af765f2010-03-30 11:12:18 +00009824 if (ping_bit_depth < 8)
9825 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009826 }
glennrp0fe50b42010-11-16 03:52:51 +00009827
cristy3ed852e2009-09-05 21:47:34 +00009828 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009829 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009830 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009831 }
9832
glennrp0e8ea192010-12-24 18:00:33 +00009833 if (ping_need_colortype_warning != MagickFalse ||
9834 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009835 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009836 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009837 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009838 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009839 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009840 {
9841 if (logging != MagickFalse)
9842 {
glennrp0e8ea192010-12-24 18:00:33 +00009843 if (ping_need_colortype_warning != MagickFalse)
9844 {
9845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9846 " Image has transparency but tRNS chunk was excluded");
9847 }
9848
cristy3ed852e2009-09-05 21:47:34 +00009849 if (mng_info->write_png_depth)
9850 {
9851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9852 " Defined PNG:bit-depth=%u, Computed depth=%u",
9853 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009854 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009855 }
glennrp0e8ea192010-12-24 18:00:33 +00009856
cristy3ed852e2009-09-05 21:47:34 +00009857 if (mng_info->write_png_colortype)
9858 {
9859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9860 " Defined PNG:color-type=%u, Computed color type=%u",
9861 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009862 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009863 }
9864 }
glennrp0e8ea192010-12-24 18:00:33 +00009865
glennrp3bd2e412010-08-10 13:34:52 +00009866 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009867 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9868 }
9869
glennrp58e01762011-01-07 15:28:54 +00009870 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009871 {
9872 /* Add an opaque matte channel */
9873 image->matte = MagickTrue;
9874 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009875
glennrpb4a13412010-05-05 12:47:19 +00009876 if (logging != MagickFalse)
9877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9878 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009879 }
9880
glennrp0e319732011-01-25 21:53:13 +00009881 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009882 {
glennrp991d11d2010-11-12 21:55:28 +00009883 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009884 {
glennrp991d11d2010-11-12 21:55:28 +00009885 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009886 if (logging != MagickFalse)
9887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9888 " Setting ping_have_tRNS=MagickTrue.");
9889 }
glennrpe9c26dc2010-05-30 01:56:35 +00009890 }
9891
cristy3ed852e2009-09-05 21:47:34 +00009892 if (logging != MagickFalse)
9893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9894 " Writing PNG header chunks");
9895
glennrp5af765f2010-03-30 11:12:18 +00009896 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9897 ping_bit_depth,ping_color_type,
9898 ping_interlace_method,ping_compression_method,
9899 ping_filter_method);
9900
glennrp39992b42010-11-14 00:03:43 +00009901 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9902 {
glennrpf09bded2011-01-08 01:15:59 +00009903 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009904
glennrp3b51f0e2010-11-27 18:14:08 +00009905 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009906 {
glennrp8640fb52010-11-23 15:48:26 +00009907 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009908 {
glennrpd6bf1612010-12-17 17:28:54 +00009909 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009911 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9912 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009913 (int) palette[i].red,
9914 (int) palette[i].green,
9915 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009916 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009917 (int) ping_trans_alpha[i]);
9918 else
9919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009920 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009921 (int) i,
9922 (int) palette[i].red,
9923 (int) palette[i].green,
9924 (int) palette[i].blue);
9925 }
glennrp39992b42010-11-14 00:03:43 +00009926 }
glennrp39992b42010-11-14 00:03:43 +00009927 }
9928
glennrp26f37912010-12-23 16:22:42 +00009929 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009930 {
glennrp26f37912010-12-23 16:22:42 +00009931 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +00009932 {
glennrp26f37912010-12-23 16:22:42 +00009933 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +00009934 if (logging)
9935 {
9936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9937 " Setting up bKGD chunk");
9938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9939 " background color = (%d,%d,%d)",
9940 (int) ping_background.red,
9941 (int) ping_background.green,
9942 (int) ping_background.blue);
9943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9944 " index = %d, gray=%d",
9945 (int) ping_background.index,
9946 (int) ping_background.gray);
9947 }
9948 }
glennrp26f37912010-12-23 16:22:42 +00009949 }
9950
9951 if (ping_exclude_pHYs == MagickFalse)
9952 {
9953 if (ping_have_pHYs != MagickFalse)
9954 {
9955 png_set_pHYs(ping,ping_info,
9956 ping_pHYs_x_resolution,
9957 ping_pHYs_y_resolution,
9958 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009959
9960 if (logging)
9961 {
9962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9963 " Setting up pHYs chunk");
9964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9965 " x_resolution=%lu",
9966 (unsigned long) ping_pHYs_x_resolution);
9967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9968 " y_resolution=%lu",
9969 (unsigned long) ping_pHYs_y_resolution);
9970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9971 " unit_type=%lu",
9972 (unsigned long) ping_pHYs_unit_type);
9973 }
glennrp26f37912010-12-23 16:22:42 +00009974 }
glennrpdfd70802010-11-14 01:23:35 +00009975 }
9976
9977#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +00009978 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009979 {
glennrp26f37912010-12-23 16:22:42 +00009980 if (image->page.x || image->page.y)
9981 {
9982 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
9983 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +00009984
glennrp26f37912010-12-23 16:22:42 +00009985 if (logging != MagickFalse)
9986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9987 " Setting up oFFs chunk with x=%d, y=%d, units=0",
9988 (int) image->page.x, (int) image->page.y);
9989 }
glennrpdfd70802010-11-14 01:23:35 +00009990 }
9991#endif
9992
glennrpda8f3a72011-02-27 23:54:12 +00009993 if (mng_info->need_blob != MagickFalse)
9994 {
9995 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
9996 MagickFalse)
9997 png_error(ping,"WriteBlob Failed");
9998
9999 ping_have_blob=MagickTrue;
10000 }
10001
cristy3ed852e2009-09-05 21:47:34 +000010002 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010003
glennrp39992b42010-11-14 00:03:43 +000010004 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010005 {
glennrp3b51f0e2010-11-27 18:14:08 +000010006 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010007 {
10008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10009 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10010 }
10011
10012 if (ping_color_type == 3)
10013 (void) png_set_tRNS(ping, ping_info,
10014 ping_trans_alpha,
10015 ping_num_trans,
10016 NULL);
10017
10018 else
10019 {
10020 (void) png_set_tRNS(ping, ping_info,
10021 NULL,
10022 0,
10023 &ping_trans_color);
10024
glennrp3b51f0e2010-11-27 18:14:08 +000010025 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010026 {
10027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010028 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010029 (int) ping_trans_color.red,
10030 (int) ping_trans_color.green,
10031 (int) ping_trans_color.blue);
10032 }
10033 }
glennrp991d11d2010-11-12 21:55:28 +000010034 }
10035
cristy3ed852e2009-09-05 21:47:34 +000010036 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010037 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010038
cristy3ed852e2009-09-05 21:47:34 +000010039 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010040
cristy3ed852e2009-09-05 21:47:34 +000010041 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010042 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010043
glennrp26f37912010-12-23 16:22:42 +000010044 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010045 {
glennrp4f25bd02011-01-01 18:51:28 +000010046 if ((image->page.width != 0 && image->page.width != image->columns) ||
10047 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010048 {
10049 unsigned char
10050 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010051
glennrp26f37912010-12-23 16:22:42 +000010052 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10053 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010054 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010055 PNGLong(chunk+4,(png_uint_32) image->page.width);
10056 PNGLong(chunk+8,(png_uint_32) image->page.height);
10057 chunk[12]=0; /* unit = pixels */
10058 (void) WriteBlob(image,13,chunk);
10059 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10060 }
cristy3ed852e2009-09-05 21:47:34 +000010061 }
10062
10063#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010064 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010065#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010066 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010067#undef PNG_HAVE_IDAT
10068#endif
10069
10070 png_set_packing(ping);
10071 /*
10072 Allocate memory.
10073 */
10074 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010075 if (image_depth > 8)
10076 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010077 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010078 {
glennrpb4a13412010-05-05 12:47:19 +000010079 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010080 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010081 break;
glennrp0fe50b42010-11-16 03:52:51 +000010082
glennrpb4a13412010-05-05 12:47:19 +000010083 case PNG_COLOR_TYPE_GRAY_ALPHA:
10084 rowbytes*=2;
10085 break;
glennrp0fe50b42010-11-16 03:52:51 +000010086
glennrpb4a13412010-05-05 12:47:19 +000010087 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010088 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010089 break;
glennrp0fe50b42010-11-16 03:52:51 +000010090
glennrpb4a13412010-05-05 12:47:19 +000010091 default:
10092 break;
cristy3ed852e2009-09-05 21:47:34 +000010093 }
glennrp3b51f0e2010-11-27 18:14:08 +000010094
10095 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010096 {
10097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10098 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010099
glennrpb4a13412010-05-05 12:47:19 +000010100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010101 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010102 }
glennrpcf002022011-01-30 02:38:15 +000010103 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10104 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010105
glennrpcf002022011-01-30 02:38:15 +000010106 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010107 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010108
cristy3ed852e2009-09-05 21:47:34 +000010109 /*
10110 Initialize image scanlines.
10111 */
glennrp5af765f2010-03-30 11:12:18 +000010112 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010113 {
10114 /*
10115 PNG write failed.
10116 */
10117#ifdef PNG_DEBUG
10118 if (image_info->verbose)
10119 (void) printf("PNG write has failed.\n");
10120#endif
10121 png_destroy_write_struct(&ping,&ping_info);
10122 if (quantum_info != (QuantumInfo *) NULL)
10123 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010124 if (ping_pixels != (unsigned char *) NULL)
10125 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010126#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010127 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010128#endif
glennrpda8f3a72011-02-27 23:54:12 +000010129 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010130 (void) CloseBlob(image);
10131 image_info=DestroyImageInfo(image_info);
10132 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010133 return(MagickFalse);
10134 }
cristyed552522009-10-16 14:04:35 +000010135 quantum_info=AcquireQuantumInfo(image_info,image);
10136 if (quantum_info == (QuantumInfo *) NULL)
10137 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010138 quantum_info->format=UndefinedQuantumFormat;
10139 quantum_info->depth=image_depth;
10140 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010141
cristy3ed852e2009-09-05 21:47:34 +000010142 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010143 !mng_info->write_png32) &&
10144 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010145 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010146 image_matte == MagickFalse &&
10147 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010148 {
glennrp8bb3a022010-12-13 20:40:04 +000010149 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010150 register const PixelPacket
10151 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010152
cristy3ed852e2009-09-05 21:47:34 +000010153 quantum_info->depth=8;
10154 for (pass=0; pass < num_passes; pass++)
10155 {
10156 /*
10157 Convert PseudoClass image to a PNG monochrome image.
10158 */
cristybb503372010-05-27 20:51:26 +000010159 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010160 {
glennrpd71e86a2011-02-24 01:28:37 +000010161 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10163 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010164
cristy3ed852e2009-09-05 21:47:34 +000010165 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010166
cristy3ed852e2009-09-05 21:47:34 +000010167 if (p == (const PixelPacket *) NULL)
10168 break;
glennrp0fe50b42010-11-16 03:52:51 +000010169
cristy3ed852e2009-09-05 21:47:34 +000010170 if (mng_info->IsPalette)
10171 {
10172 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010173 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010174 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10175 mng_info->write_png_depth &&
10176 mng_info->write_png_depth != old_bit_depth)
10177 {
10178 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010179 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010180 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010181 >> (8-old_bit_depth));
10182 }
10183 }
glennrp0fe50b42010-11-16 03:52:51 +000010184
cristy3ed852e2009-09-05 21:47:34 +000010185 else
10186 {
10187 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010188 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010189 }
glennrp0fe50b42010-11-16 03:52:51 +000010190
cristy3ed852e2009-09-05 21:47:34 +000010191 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010192 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010193 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010194 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010195
glennrp3b51f0e2010-11-27 18:14:08 +000010196 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10198 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010199
glennrpcf002022011-01-30 02:38:15 +000010200 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010201 }
10202 if (image->previous == (Image *) NULL)
10203 {
10204 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10205 if (status == MagickFalse)
10206 break;
10207 }
10208 }
10209 }
glennrp0fe50b42010-11-16 03:52:51 +000010210
glennrp8bb3a022010-12-13 20:40:04 +000010211 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010212 {
glennrp0fe50b42010-11-16 03:52:51 +000010213 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010214 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010215 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010216 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010217 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010218 {
glennrp8bb3a022010-12-13 20:40:04 +000010219 register const PixelPacket
10220 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010221
glennrp8bb3a022010-12-13 20:40:04 +000010222 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010223 {
glennrp8bb3a022010-12-13 20:40:04 +000010224
cristybb503372010-05-27 20:51:26 +000010225 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010226 {
10227 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010228
cristy3ed852e2009-09-05 21:47:34 +000010229 if (p == (const PixelPacket *) NULL)
10230 break;
glennrp2cc891a2010-12-24 13:44:32 +000010231
glennrp5af765f2010-03-30 11:12:18 +000010232 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010233 {
glennrp8bb3a022010-12-13 20:40:04 +000010234 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +000010235 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010236 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010237
glennrp8bb3a022010-12-13 20:40:04 +000010238 else
10239 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010240 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010241
glennrp3b51f0e2010-11-27 18:14:08 +000010242 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010244 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010245 }
glennrp2cc891a2010-12-24 13:44:32 +000010246
glennrp8bb3a022010-12-13 20:40:04 +000010247 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10248 {
10249 if (logging != MagickFalse && y == 0)
10250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10251 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010252
glennrp8bb3a022010-12-13 20:40:04 +000010253 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010254 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010255 }
glennrp2cc891a2010-12-24 13:44:32 +000010256
glennrp3b51f0e2010-11-27 18:14:08 +000010257 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010259 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010260
glennrpcf002022011-01-30 02:38:15 +000010261 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010262 }
glennrp2cc891a2010-12-24 13:44:32 +000010263
glennrp8bb3a022010-12-13 20:40:04 +000010264 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010265 {
glennrp8bb3a022010-12-13 20:40:04 +000010266 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10267 if (status == MagickFalse)
10268 break;
cristy3ed852e2009-09-05 21:47:34 +000010269 }
cristy3ed852e2009-09-05 21:47:34 +000010270 }
10271 }
glennrp8bb3a022010-12-13 20:40:04 +000010272
10273 else
10274 {
10275 register const PixelPacket
10276 *p;
10277
10278 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010279 {
glennrp8bb3a022010-12-13 20:40:04 +000010280 if ((image_depth > 8) || (mng_info->write_png24 ||
10281 mng_info->write_png32 ||
10282 (!mng_info->write_png8 && !mng_info->IsPalette)))
10283 {
10284 for (y=0; y < (ssize_t) image->rows; y++)
10285 {
10286 p=GetVirtualPixels(image,0,y,image->columns,1,
10287 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010288
glennrp8bb3a022010-12-13 20:40:04 +000010289 if (p == (const PixelPacket *) NULL)
10290 break;
glennrp2cc891a2010-12-24 13:44:32 +000010291
glennrp8bb3a022010-12-13 20:40:04 +000010292 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10293 {
10294 if (image->storage_class == DirectClass)
10295 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010296 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010297
glennrp8bb3a022010-12-13 20:40:04 +000010298 else
10299 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010300 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010301 }
glennrp2cc891a2010-12-24 13:44:32 +000010302
glennrp8bb3a022010-12-13 20:40:04 +000010303 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10304 {
10305 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010306 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +000010307 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010308
glennrp8bb3a022010-12-13 20:40:04 +000010309 if (logging != MagickFalse && y == 0)
10310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10311 " Writing GRAY_ALPHA PNG pixels (3)");
10312 }
glennrp2cc891a2010-12-24 13:44:32 +000010313
glennrp8bb3a022010-12-13 20:40:04 +000010314 else if (image_matte != MagickFalse)
10315 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010316 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010317
glennrp8bb3a022010-12-13 20:40:04 +000010318 else
10319 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010320 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010321
glennrp8bb3a022010-12-13 20:40:04 +000010322 if (logging != MagickFalse && y == 0)
10323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10324 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010325
glennrpcf002022011-01-30 02:38:15 +000010326 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010327 }
10328 }
glennrp2cc891a2010-12-24 13:44:32 +000010329
glennrp8bb3a022010-12-13 20:40:04 +000010330 else
10331 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10332 mng_info->write_png32 ||
10333 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10334 {
10335 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10336 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10337 {
10338 if (logging != MagickFalse)
10339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10340 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010341
glennrp8bb3a022010-12-13 20:40:04 +000010342 quantum_info->depth=8;
10343 image_depth=8;
10344 }
glennrp2cc891a2010-12-24 13:44:32 +000010345
glennrp8bb3a022010-12-13 20:40:04 +000010346 for (y=0; y < (ssize_t) image->rows; y++)
10347 {
10348 if (logging != MagickFalse && y == 0)
10349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10350 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010351
glennrp770d1932011-03-06 22:11:17 +000010352 p=GetVirtualPixels(image,0,y,image->columns,1,
10353 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010354
glennrp8bb3a022010-12-13 20:40:04 +000010355 if (p == (const PixelPacket *) NULL)
10356 break;
glennrp2cc891a2010-12-24 13:44:32 +000010357
glennrp8bb3a022010-12-13 20:40:04 +000010358 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010359 {
glennrp4bf89732011-03-21 13:48:28 +000010360 quantum_info->depth=image->depth;
10361
glennrp44757ab2011-03-17 12:57:03 +000010362 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010363 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +000010364 }
glennrp2cc891a2010-12-24 13:44:32 +000010365
glennrp8bb3a022010-12-13 20:40:04 +000010366 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10367 {
10368 if (logging != MagickFalse && y == 0)
10369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10370 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010371
glennrp8bb3a022010-12-13 20:40:04 +000010372 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010373 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +000010374 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010375 }
glennrp2cc891a2010-12-24 13:44:32 +000010376
glennrp8bb3a022010-12-13 20:40:04 +000010377 else
glennrp8bb3a022010-12-13 20:40:04 +000010378 {
glennrp179d0752011-03-17 13:02:10 +000010379 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +000010380 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10381
10382 if (logging != MagickFalse && y <= 2)
10383 {
10384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010385 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010386
10387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10388 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10389 (int)ping_pixels[0],(int)ping_pixels[1]);
10390 }
glennrp8bb3a022010-12-13 20:40:04 +000010391 }
glennrpcf002022011-01-30 02:38:15 +000010392 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010393 }
10394 }
glennrp2cc891a2010-12-24 13:44:32 +000010395
glennrp8bb3a022010-12-13 20:40:04 +000010396 if (image->previous == (Image *) NULL)
10397 {
10398 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10399 if (status == MagickFalse)
10400 break;
10401 }
cristy3ed852e2009-09-05 21:47:34 +000010402 }
glennrp8bb3a022010-12-13 20:40:04 +000010403 }
10404 }
10405
cristyb32b90a2009-09-07 21:45:48 +000010406 if (quantum_info != (QuantumInfo *) NULL)
10407 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010408
10409 if (logging != MagickFalse)
10410 {
10411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010412 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010413
cristy3ed852e2009-09-05 21:47:34 +000010414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010415 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010416
cristy3ed852e2009-09-05 21:47:34 +000010417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010418 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010419
cristy3ed852e2009-09-05 21:47:34 +000010420 if (mng_info->write_png_depth)
10421 {
10422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10423 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10424 }
glennrp0fe50b42010-11-16 03:52:51 +000010425
cristy3ed852e2009-09-05 21:47:34 +000010426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010427 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010428
cristy3ed852e2009-09-05 21:47:34 +000010429 if (mng_info->write_png_colortype)
10430 {
10431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10432 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10433 }
glennrp0fe50b42010-11-16 03:52:51 +000010434
cristy3ed852e2009-09-05 21:47:34 +000010435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010436 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010437
cristy3ed852e2009-09-05 21:47:34 +000010438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010439 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010440 }
10441 /*
glennrpa0ed0092011-04-18 16:36:29 +000010442 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010443 */
glennrp823b55c2011-03-14 18:46:46 +000010444 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010445 {
glennrp26f37912010-12-23 16:22:42 +000010446 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010447 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010448 while (property != (const char *) NULL)
10449 {
10450 png_textp
10451 text;
glennrp2cc891a2010-12-24 13:44:32 +000010452
glennrp26f37912010-12-23 16:22:42 +000010453 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +000010454
10455 /* Don't write any "png:" properties; those are just for "identify" */
10456 if (LocaleNCompare(property,"png:",4) != 0 &&
10457
10458 /* Suppress density and units if we wrote a pHYs chunk */
10459 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010460 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010461 LocaleCompare(property,"units") != 0) &&
10462
10463 /* Suppress the IM-generated Date:create and Date:modify */
10464 (ping_exclude_date == MagickFalse ||
10465 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010466 {
glennrpc70af4a2011-03-07 00:08:23 +000010467 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010468 {
glennrpc70af4a2011-03-07 00:08:23 +000010469 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10470 text[0].key=(char *) property;
10471 text[0].text=(char *) value;
10472 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010473
glennrpc70af4a2011-03-07 00:08:23 +000010474 if (ping_exclude_tEXt != MagickFalse)
10475 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10476
10477 else if (ping_exclude_zTXt != MagickFalse)
10478 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10479
10480 else
glennrp26f37912010-12-23 16:22:42 +000010481 {
glennrpc70af4a2011-03-07 00:08:23 +000010482 text[0].compression=image_info->compression == NoCompression ||
10483 (image_info->compression == UndefinedCompression &&
10484 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10485 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010486 }
glennrp2cc891a2010-12-24 13:44:32 +000010487
glennrpc70af4a2011-03-07 00:08:23 +000010488 if (logging != MagickFalse)
10489 {
10490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10491 " Setting up text chunk");
10492
10493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10494 " keyword: %s",text[0].key);
10495 }
10496
10497 png_set_text(ping,ping_info,text,1);
10498 png_free(ping,text);
10499 }
glennrp26f37912010-12-23 16:22:42 +000010500 }
10501 property=GetNextImageProperty(image);
10502 }
cristy3ed852e2009-09-05 21:47:34 +000010503 }
10504
10505 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010506 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010507
10508 if (logging != MagickFalse)
10509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10510 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010511
cristy3ed852e2009-09-05 21:47:34 +000010512 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010513
cristy3ed852e2009-09-05 21:47:34 +000010514 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10515 {
10516 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010517 (ping_width != mng_info->page.width) ||
10518 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010519 {
10520 unsigned char
10521 chunk[32];
10522
10523 /*
10524 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10525 */
10526 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10527 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010528 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010529 chunk[4]=4;
10530 chunk[5]=0; /* frame name separator (no name) */
10531 chunk[6]=1; /* flag for changing delay, for next frame only */
10532 chunk[7]=0; /* flag for changing frame timeout */
10533 chunk[8]=1; /* flag for changing frame clipping for next frame */
10534 chunk[9]=0; /* flag for changing frame sync_id */
10535 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10536 chunk[14]=0; /* clipping boundaries delta type */
10537 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10538 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010539 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010540 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10541 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010542 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010543 (void) WriteBlob(image,31,chunk);
10544 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10545 mng_info->old_framing_mode=4;
10546 mng_info->framing_mode=1;
10547 }
glennrp0fe50b42010-11-16 03:52:51 +000010548
cristy3ed852e2009-09-05 21:47:34 +000010549 else
10550 mng_info->framing_mode=3;
10551 }
10552 if (mng_info->write_mng && !mng_info->need_fram &&
10553 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +000010554 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010555 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010556 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010557
cristy3ed852e2009-09-05 21:47:34 +000010558 /*
10559 Free PNG resources.
10560 */
glennrp5af765f2010-03-30 11:12:18 +000010561
cristy3ed852e2009-09-05 21:47:34 +000010562 png_destroy_write_struct(&ping,&ping_info);
10563
glennrpcf002022011-01-30 02:38:15 +000010564 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010565
10566#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010567 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010568#endif
10569
glennrpda8f3a72011-02-27 23:54:12 +000010570 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010571 (void) CloseBlob(image);
10572
10573 image_info=DestroyImageInfo(image_info);
10574 image=DestroyImage(image);
10575
10576 /* Store bit depth actually written */
10577 s[0]=(char) ping_bit_depth;
10578 s[1]='\0';
10579
10580 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10581
cristy3ed852e2009-09-05 21:47:34 +000010582 if (logging != MagickFalse)
10583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10584 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010585
cristy3ed852e2009-09-05 21:47:34 +000010586 return(MagickTrue);
10587/* End write one PNG image */
10588}
10589
10590/*
10591%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10592% %
10593% %
10594% %
10595% W r i t e P N G I m a g e %
10596% %
10597% %
10598% %
10599%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10600%
10601% WritePNGImage() writes a Portable Network Graphics (PNG) or
10602% Multiple-image Network Graphics (MNG) image file.
10603%
10604% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10605%
10606% The format of the WritePNGImage method is:
10607%
10608% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10609%
10610% A description of each parameter follows:
10611%
10612% o image_info: the image info.
10613%
10614% o image: The image.
10615%
10616% Returns MagickTrue on success, MagickFalse on failure.
10617%
10618% Communicating with the PNG encoder:
10619%
10620% While the datastream written is always in PNG format and normally would
10621% be given the "png" file extension, this method also writes the following
10622% pseudo-formats which are subsets of PNG:
10623%
glennrp5a39f372011-02-25 04:52:16 +000010624% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10625% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010626% is present, the tRNS chunk must only have values 0 and 255
10627% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010628% transparent). If other values are present they will be
10629% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010630% colors are present, they will be quantized to the 4-4-4-1,
10631% 3-3-3-1, or 3-3-2-1 palette.
10632%
10633% If you want better quantization or dithering of the colors
10634% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010635% PNG encoder. The pixels contain 8-bit indices even if
10636% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010637% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010638% PNG grayscale type might be slightly more efficient. Please
10639% note that writing to the PNG8 format may result in loss
10640% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010641%
10642% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10643% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010644% one of the colors as transparent. The only loss incurred
10645% is reduction of sample depth to 8. If the image has more
10646% than one transparent color, has semitransparent pixels, or
10647% has an opaque pixel with the same RGB components as the
10648% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010649%
10650% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10651% transparency is permitted, i.e., the alpha sample for
10652% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010653% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010654% The only loss in data is the reduction of the sample depth
10655% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010656%
10657% o -define: For more precise control of the PNG output, you can use the
10658% Image options "png:bit-depth" and "png:color-type". These
10659% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010660% from the application programming interfaces. The options
10661% are case-independent and are converted to lowercase before
10662% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010663%
10664% png:color-type can be 0, 2, 3, 4, or 6.
10665%
10666% When png:color-type is 0 (Grayscale), png:bit-depth can
10667% be 1, 2, 4, 8, or 16.
10668%
10669% When png:color-type is 2 (RGB), png:bit-depth can
10670% be 8 or 16.
10671%
10672% When png:color-type is 3 (Indexed), png:bit-depth can
10673% be 1, 2, 4, or 8. This refers to the number of bits
10674% used to store the index. The color samples always have
10675% bit-depth 8 in indexed PNG files.
10676%
10677% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10678% png:bit-depth can be 8 or 16.
10679%
glennrp5a39f372011-02-25 04:52:16 +000010680% If the image cannot be written without loss with the requested bit-depth
10681% and color-type, a PNG file will not be written, and the encoder will
10682% return MagickFalse.
10683%
cristy3ed852e2009-09-05 21:47:34 +000010684% Since image encoders should not be responsible for the "heavy lifting",
10685% the user should make sure that ImageMagick has already reduced the
10686% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010687% transparency prior to attempting to write the image with depth, color,
10688% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010689%
glennrp97cefe22011-04-22 16:17:00 +000010690% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +000010691%
cristy3ed852e2009-09-05 21:47:34 +000010692% Note that another definition, "png:bit-depth-written" exists, but it
10693% is not intended for external use. It is only used internally by the
10694% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10695%
10696% It is possible to request that the PNG encoder write previously-formatted
10697% ancillary chunks in the output PNG file, using the "-profile" commandline
10698% option as shown below or by setting the profile via a programming
10699% interface:
10700%
10701% -profile PNG-chunk-x:<file>
10702%
10703% where x is a location flag and <file> is a file containing the chunk
10704% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010705% This encoder will compute the chunk length and CRC, so those must not
10706% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010707%
10708% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10709% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10710% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010711% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010712%
glennrpbb8a7332010-11-13 15:17:35 +000010713% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010714%
glennrp3241bd02010-12-12 04:36:28 +000010715% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010716%
glennrpd6afd542010-11-19 01:53:05 +000010717% o 32-bit depth is reduced to 16.
10718% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10719% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010720% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010721% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010722% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010723% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10724% this can be done without loss and a larger bit depth N was not
10725% requested via the "-define PNG:bit-depth=N" option.
10726% o If matte channel is present but only one transparent color is
10727% present, RGB+tRNS is written instead of RGBA
10728% o Opaque matte channel is removed (or added, if color-type 4 or 6
10729% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010730%
cristy3ed852e2009-09-05 21:47:34 +000010731%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10732*/
10733static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10734 Image *image)
10735{
10736 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010737 excluding,
10738 logging,
10739 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010740 status;
10741
10742 MngInfo
10743 *mng_info;
10744
10745 const char
10746 *value;
10747
10748 int
glennrp21f0e622011-01-07 16:20:57 +000010749 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010750 source;
10751
cristy3ed852e2009-09-05 21:47:34 +000010752 /*
10753 Open image file.
10754 */
10755 assert(image_info != (const ImageInfo *) NULL);
10756 assert(image_info->signature == MagickSignature);
10757 assert(image != (Image *) NULL);
10758 assert(image->signature == MagickSignature);
10759 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010760 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010761 /*
10762 Allocate a MngInfo structure.
10763 */
10764 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010765 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010766
cristy3ed852e2009-09-05 21:47:34 +000010767 if (mng_info == (MngInfo *) NULL)
10768 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010769
cristy3ed852e2009-09-05 21:47:34 +000010770 /*
10771 Initialize members of the MngInfo structure.
10772 */
10773 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10774 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010775 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010776 have_mng_structure=MagickTrue;
10777
10778 /* See if user has requested a specific PNG subformat */
10779
10780 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10781 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10782 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10783
10784 if (mng_info->write_png8)
10785 {
glennrp9c1eb072010-06-06 22:19:15 +000010786 mng_info->write_png_colortype = /* 3 */ 4;
10787 mng_info->write_png_depth = 8;
10788 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010789 }
10790
10791 if (mng_info->write_png24)
10792 {
glennrp9c1eb072010-06-06 22:19:15 +000010793 mng_info->write_png_colortype = /* 2 */ 3;
10794 mng_info->write_png_depth = 8;
10795 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010796
glennrp9c1eb072010-06-06 22:19:15 +000010797 if (image->matte == MagickTrue)
10798 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010799
glennrp9c1eb072010-06-06 22:19:15 +000010800 else
10801 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010802
glennrp9c1eb072010-06-06 22:19:15 +000010803 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010804 }
10805
10806 if (mng_info->write_png32)
10807 {
glennrp9c1eb072010-06-06 22:19:15 +000010808 mng_info->write_png_colortype = /* 6 */ 7;
10809 mng_info->write_png_depth = 8;
10810 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010811
glennrp9c1eb072010-06-06 22:19:15 +000010812 if (image->matte == MagickTrue)
10813 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010814
glennrp9c1eb072010-06-06 22:19:15 +000010815 else
10816 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010817
glennrp9c1eb072010-06-06 22:19:15 +000010818 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010819 }
10820
10821 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010822
cristy3ed852e2009-09-05 21:47:34 +000010823 if (value != (char *) NULL)
10824 {
10825 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010826 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010827
cristy3ed852e2009-09-05 21:47:34 +000010828 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010829 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010830
cristy3ed852e2009-09-05 21:47:34 +000010831 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010832 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010833
cristy3ed852e2009-09-05 21:47:34 +000010834 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010835 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010836
cristy3ed852e2009-09-05 21:47:34 +000010837 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010838 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010839
glennrpbb8a7332010-11-13 15:17:35 +000010840 else
10841 (void) ThrowMagickException(&image->exception,
10842 GetMagickModule(),CoderWarning,
10843 "ignoring invalid defined png:bit-depth",
10844 "=%s",value);
10845
cristy3ed852e2009-09-05 21:47:34 +000010846 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010848 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010849 }
glennrp0fe50b42010-11-16 03:52:51 +000010850
cristy3ed852e2009-09-05 21:47:34 +000010851 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010852
cristy3ed852e2009-09-05 21:47:34 +000010853 if (value != (char *) NULL)
10854 {
10855 /* We must store colortype+1 because 0 is a valid colortype */
10856 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010857 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010858
cristy3ed852e2009-09-05 21:47:34 +000010859 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010860 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010861
cristy3ed852e2009-09-05 21:47:34 +000010862 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010863 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010864
cristy3ed852e2009-09-05 21:47:34 +000010865 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010866 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010867
cristy3ed852e2009-09-05 21:47:34 +000010868 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010869 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010870
glennrpbb8a7332010-11-13 15:17:35 +000010871 else
10872 (void) ThrowMagickException(&image->exception,
10873 GetMagickModule(),CoderWarning,
10874 "ignoring invalid defined png:color-type",
10875 "=%s",value);
10876
cristy3ed852e2009-09-05 21:47:34 +000010877 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010879 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010880 }
10881
glennrp0e8ea192010-12-24 18:00:33 +000010882 /* Check for chunks to be excluded:
10883 *
glennrp0dff56c2011-01-29 19:10:02 +000010884 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010885 * listed in the "unused_chunks" array, above.
10886 *
10887 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10888 * define (in the image properties or in the image artifacts)
10889 * or via a mng_info member. For convenience, in addition
10890 * to or instead of a comma-separated list of chunks, the
10891 * "exclude-chunk" string can be simply "all" or "none".
10892 *
10893 * The exclude-chunk define takes priority over the mng_info.
10894 *
10895 * A "PNG:include-chunk" define takes priority over both the
10896 * mng_info and the "PNG:exclude-chunk" define. Like the
10897 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010898 * well as a comma-separated list. Chunks that are unknown to
10899 * ImageMagick are always excluded, regardless of their "copy-safe"
10900 * status according to the PNG specification, and even if they
10901 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010902 *
10903 * Finally, all chunks listed in the "unused_chunks" array are
10904 * automatically excluded, regardless of the other instructions
10905 * or lack thereof.
10906 *
10907 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10908 * will not be written and the gAMA chunk will only be written if it
10909 * is not between .45 and .46, or approximately (1.0/2.2).
10910 *
10911 * If you exclude tRNS and the image has transparency, the colortype
10912 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10913 *
10914 * The -strip option causes StripImage() to set the png:include-chunk
10915 * artifact to "none,gama".
10916 */
10917
glennrp26f37912010-12-23 16:22:42 +000010918 mng_info->ping_exclude_bKGD=MagickFalse;
10919 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010920 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010921 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10922 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010923 mng_info->ping_exclude_iCCP=MagickFalse;
10924 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10925 mng_info->ping_exclude_oFFs=MagickFalse;
10926 mng_info->ping_exclude_pHYs=MagickFalse;
10927 mng_info->ping_exclude_sRGB=MagickFalse;
10928 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010929 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010930 mng_info->ping_exclude_vpAg=MagickFalse;
10931 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10932 mng_info->ping_exclude_zTXt=MagickFalse;
10933
glennrp8d3d6e52011-04-19 04:39:51 +000010934 mng_info->ping_preserve_colormap=MagickFalse;
10935
10936 value=GetImageArtifact(image,"png:preserve-colormap");
10937 if (value == NULL)
10938 value=GetImageOption(image_info,"png:preserve-colormap");
10939 if (value != NULL)
10940 mng_info->ping_preserve_colormap=MagickTrue;
10941
glennrp03812ae2010-12-24 01:31:34 +000010942 excluding=MagickFalse;
10943
glennrp5c7cf4e2010-12-24 00:30:00 +000010944 for (source=0; source<1; source++)
10945 {
10946 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010947 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010948 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010949
10950 if (value == NULL)
10951 value=GetImageArtifact(image,"png:exclude-chunks");
10952 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010953 else
glennrpacba0042010-12-24 14:27:26 +000010954 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010955 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010956
glennrpacba0042010-12-24 14:27:26 +000010957 if (value == NULL)
10958 value=GetImageOption(image_info,"png:exclude-chunks");
10959 }
10960
glennrp03812ae2010-12-24 01:31:34 +000010961 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010962 {
glennrp03812ae2010-12-24 01:31:34 +000010963
10964 size_t
10965 last;
10966
10967 excluding=MagickTrue;
10968
10969 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000010970 {
10971 if (source == 0)
10972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10973 " png:exclude-chunk=%s found in image artifacts.\n", value);
10974 else
10975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10976 " png:exclude-chunk=%s found in image properties.\n", value);
10977 }
glennrp03812ae2010-12-24 01:31:34 +000010978
10979 last=strlen(value);
10980
10981 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000010982 {
glennrp03812ae2010-12-24 01:31:34 +000010983
10984 if (LocaleNCompare(value+i,"all",3) == 0)
10985 {
10986 mng_info->ping_exclude_bKGD=MagickTrue;
10987 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000010988 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010989 mng_info->ping_exclude_EXIF=MagickTrue;
10990 mng_info->ping_exclude_gAMA=MagickTrue;
10991 mng_info->ping_exclude_iCCP=MagickTrue;
10992 /* mng_info->ping_exclude_iTXt=MagickTrue; */
10993 mng_info->ping_exclude_oFFs=MagickTrue;
10994 mng_info->ping_exclude_pHYs=MagickTrue;
10995 mng_info->ping_exclude_sRGB=MagickTrue;
10996 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000010997 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000010998 mng_info->ping_exclude_vpAg=MagickTrue;
10999 mng_info->ping_exclude_zCCP=MagickTrue;
11000 mng_info->ping_exclude_zTXt=MagickTrue;
11001 i--;
11002 }
glennrp2cc891a2010-12-24 13:44:32 +000011003
glennrp03812ae2010-12-24 01:31:34 +000011004 if (LocaleNCompare(value+i,"none",4) == 0)
11005 {
11006 mng_info->ping_exclude_bKGD=MagickFalse;
11007 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011008 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011009 mng_info->ping_exclude_EXIF=MagickFalse;
11010 mng_info->ping_exclude_gAMA=MagickFalse;
11011 mng_info->ping_exclude_iCCP=MagickFalse;
11012 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11013 mng_info->ping_exclude_oFFs=MagickFalse;
11014 mng_info->ping_exclude_pHYs=MagickFalse;
11015 mng_info->ping_exclude_sRGB=MagickFalse;
11016 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011017 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011018 mng_info->ping_exclude_vpAg=MagickFalse;
11019 mng_info->ping_exclude_zCCP=MagickFalse;
11020 mng_info->ping_exclude_zTXt=MagickFalse;
11021 }
glennrp2cc891a2010-12-24 13:44:32 +000011022
glennrp03812ae2010-12-24 01:31:34 +000011023 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11024 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011025
glennrp03812ae2010-12-24 01:31:34 +000011026 if (LocaleNCompare(value+i,"chrm",4) == 0)
11027 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011028
glennrpa0ed0092011-04-18 16:36:29 +000011029 if (LocaleNCompare(value+i,"date",4) == 0)
11030 mng_info->ping_exclude_date=MagickTrue;
11031
glennrp03812ae2010-12-24 01:31:34 +000011032 if (LocaleNCompare(value+i,"exif",4) == 0)
11033 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011034
glennrp03812ae2010-12-24 01:31:34 +000011035 if (LocaleNCompare(value+i,"gama",4) == 0)
11036 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011037
glennrp03812ae2010-12-24 01:31:34 +000011038 if (LocaleNCompare(value+i,"iccp",4) == 0)
11039 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011040
glennrp03812ae2010-12-24 01:31:34 +000011041 /*
11042 if (LocaleNCompare(value+i,"itxt",4) == 0)
11043 mng_info->ping_exclude_iTXt=MagickTrue;
11044 */
glennrp2cc891a2010-12-24 13:44:32 +000011045
glennrp03812ae2010-12-24 01:31:34 +000011046 if (LocaleNCompare(value+i,"gama",4) == 0)
11047 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011048
glennrp03812ae2010-12-24 01:31:34 +000011049 if (LocaleNCompare(value+i,"offs",4) == 0)
11050 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011051
glennrp03812ae2010-12-24 01:31:34 +000011052 if (LocaleNCompare(value+i,"phys",4) == 0)
11053 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011054
glennrpa1e3b7b2010-12-24 16:37:33 +000011055 if (LocaleNCompare(value+i,"srgb",4) == 0)
11056 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011057
glennrp03812ae2010-12-24 01:31:34 +000011058 if (LocaleNCompare(value+i,"text",4) == 0)
11059 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011060
glennrpa1e3b7b2010-12-24 16:37:33 +000011061 if (LocaleNCompare(value+i,"trns",4) == 0)
11062 mng_info->ping_exclude_tRNS=MagickTrue;
11063
glennrp03812ae2010-12-24 01:31:34 +000011064 if (LocaleNCompare(value+i,"vpag",4) == 0)
11065 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011066
glennrp03812ae2010-12-24 01:31:34 +000011067 if (LocaleNCompare(value+i,"zccp",4) == 0)
11068 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011069
glennrp03812ae2010-12-24 01:31:34 +000011070 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11071 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011072
glennrp03812ae2010-12-24 01:31:34 +000011073 }
glennrpce91ed52010-12-23 22:37:49 +000011074 }
glennrp26f37912010-12-23 16:22:42 +000011075 }
11076
glennrp5c7cf4e2010-12-24 00:30:00 +000011077 for (source=0; source<1; source++)
11078 {
11079 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011080 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011081 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011082
11083 if (value == NULL)
11084 value=GetImageArtifact(image,"png:include-chunks");
11085 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011086 else
glennrpacba0042010-12-24 14:27:26 +000011087 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011088 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011089
glennrpacba0042010-12-24 14:27:26 +000011090 if (value == NULL)
11091 value=GetImageOption(image_info,"png:include-chunks");
11092 }
11093
glennrp03812ae2010-12-24 01:31:34 +000011094 if (value != NULL)
11095 {
11096 size_t
11097 last;
glennrp26f37912010-12-23 16:22:42 +000011098
glennrp03812ae2010-12-24 01:31:34 +000011099 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011100
glennrp03812ae2010-12-24 01:31:34 +000011101 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011102 {
11103 if (source == 0)
11104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11105 " png:include-chunk=%s found in image artifacts.\n", value);
11106 else
11107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11108 " png:include-chunk=%s found in image properties.\n", value);
11109 }
glennrp03812ae2010-12-24 01:31:34 +000011110
11111 last=strlen(value);
11112
11113 for (i=0; i<(int) last; i+=5)
11114 {
11115 if (LocaleNCompare(value+i,"all",3) == 0)
11116 {
11117 mng_info->ping_exclude_bKGD=MagickFalse;
11118 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011119 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011120 mng_info->ping_exclude_EXIF=MagickFalse;
11121 mng_info->ping_exclude_gAMA=MagickFalse;
11122 mng_info->ping_exclude_iCCP=MagickFalse;
11123 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11124 mng_info->ping_exclude_oFFs=MagickFalse;
11125 mng_info->ping_exclude_pHYs=MagickFalse;
11126 mng_info->ping_exclude_sRGB=MagickFalse;
11127 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011128 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011129 mng_info->ping_exclude_vpAg=MagickFalse;
11130 mng_info->ping_exclude_zCCP=MagickFalse;
11131 mng_info->ping_exclude_zTXt=MagickFalse;
11132 i--;
11133 }
glennrp2cc891a2010-12-24 13:44:32 +000011134
glennrp03812ae2010-12-24 01:31:34 +000011135 if (LocaleNCompare(value+i,"none",4) == 0)
11136 {
11137 mng_info->ping_exclude_bKGD=MagickTrue;
11138 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011139 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011140 mng_info->ping_exclude_EXIF=MagickTrue;
11141 mng_info->ping_exclude_gAMA=MagickTrue;
11142 mng_info->ping_exclude_iCCP=MagickTrue;
11143 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11144 mng_info->ping_exclude_oFFs=MagickTrue;
11145 mng_info->ping_exclude_pHYs=MagickTrue;
11146 mng_info->ping_exclude_sRGB=MagickTrue;
11147 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011148 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011149 mng_info->ping_exclude_vpAg=MagickTrue;
11150 mng_info->ping_exclude_zCCP=MagickTrue;
11151 mng_info->ping_exclude_zTXt=MagickTrue;
11152 }
glennrp2cc891a2010-12-24 13:44:32 +000011153
glennrp03812ae2010-12-24 01:31:34 +000011154 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11155 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011156
glennrp03812ae2010-12-24 01:31:34 +000011157 if (LocaleNCompare(value+i,"chrm",4) == 0)
11158 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011159
glennrpa0ed0092011-04-18 16:36:29 +000011160 if (LocaleNCompare(value+i,"date",4) == 0)
11161 mng_info->ping_exclude_date=MagickFalse;
11162
glennrp03812ae2010-12-24 01:31:34 +000011163 if (LocaleNCompare(value+i,"exif",4) == 0)
11164 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011165
glennrp03812ae2010-12-24 01:31:34 +000011166 if (LocaleNCompare(value+i,"gama",4) == 0)
11167 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011168
glennrp03812ae2010-12-24 01:31:34 +000011169 if (LocaleNCompare(value+i,"iccp",4) == 0)
11170 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011171
glennrp03812ae2010-12-24 01:31:34 +000011172 /*
11173 if (LocaleNCompare(value+i,"itxt",4) == 0)
11174 mng_info->ping_exclude_iTXt=MagickFalse;
11175 */
glennrp2cc891a2010-12-24 13:44:32 +000011176
glennrp03812ae2010-12-24 01:31:34 +000011177 if (LocaleNCompare(value+i,"gama",4) == 0)
11178 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011179
glennrp03812ae2010-12-24 01:31:34 +000011180 if (LocaleNCompare(value+i,"offs",4) == 0)
11181 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011182
glennrp03812ae2010-12-24 01:31:34 +000011183 if (LocaleNCompare(value+i,"phys",4) == 0)
11184 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011185
glennrpa1e3b7b2010-12-24 16:37:33 +000011186 if (LocaleNCompare(value+i,"srgb",4) == 0)
11187 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011188
glennrp03812ae2010-12-24 01:31:34 +000011189 if (LocaleNCompare(value+i,"text",4) == 0)
11190 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011191
glennrpa1e3b7b2010-12-24 16:37:33 +000011192 if (LocaleNCompare(value+i,"trns",4) == 0)
11193 mng_info->ping_exclude_tRNS=MagickFalse;
11194
glennrp03812ae2010-12-24 01:31:34 +000011195 if (LocaleNCompare(value+i,"vpag",4) == 0)
11196 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011197
glennrp03812ae2010-12-24 01:31:34 +000011198 if (LocaleNCompare(value+i,"zccp",4) == 0)
11199 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011200
glennrp03812ae2010-12-24 01:31:34 +000011201 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11202 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011203
glennrp03812ae2010-12-24 01:31:34 +000011204 }
glennrpce91ed52010-12-23 22:37:49 +000011205 }
glennrp26f37912010-12-23 16:22:42 +000011206 }
11207
glennrp03812ae2010-12-24 01:31:34 +000011208 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011209 {
11210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11211 " Chunks to be excluded from the output PNG:");
11212 if (mng_info->ping_exclude_bKGD != MagickFalse)
11213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11214 " bKGD");
11215 if (mng_info->ping_exclude_cHRM != MagickFalse)
11216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11217 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011218 if (mng_info->ping_exclude_date != MagickFalse)
11219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11220 " date");
glennrp26f37912010-12-23 16:22:42 +000011221 if (mng_info->ping_exclude_EXIF != MagickFalse)
11222 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11223 " EXIF");
11224 if (mng_info->ping_exclude_gAMA != MagickFalse)
11225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11226 " gAMA");
11227 if (mng_info->ping_exclude_iCCP != MagickFalse)
11228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11229 " iCCP");
11230/*
11231 if (mng_info->ping_exclude_iTXt != MagickFalse)
11232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11233 " iTXt");
11234*/
11235 if (mng_info->ping_exclude_oFFs != MagickFalse)
11236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11237 " oFFs");
11238 if (mng_info->ping_exclude_pHYs != MagickFalse)
11239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11240 " pHYs");
11241 if (mng_info->ping_exclude_sRGB != MagickFalse)
11242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11243 " sRGB");
11244 if (mng_info->ping_exclude_tEXt != MagickFalse)
11245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11246 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011247 if (mng_info->ping_exclude_tRNS != MagickFalse)
11248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11249 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011250 if (mng_info->ping_exclude_vpAg != MagickFalse)
11251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11252 " vpAg");
11253 if (mng_info->ping_exclude_zCCP != MagickFalse)
11254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11255 " zCCP");
11256 if (mng_info->ping_exclude_zTXt != MagickFalse)
11257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11258 " zTXt");
11259 }
11260
glennrpb9cfe272010-12-21 15:08:06 +000011261 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011262
glennrpb9cfe272010-12-21 15:08:06 +000011263 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000011264
11265 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011266
cristy3ed852e2009-09-05 21:47:34 +000011267 if (logging != MagickFalse)
11268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011269
cristy3ed852e2009-09-05 21:47:34 +000011270 return(status);
11271}
11272
11273#if defined(JNG_SUPPORTED)
11274
11275/* Write one JNG image */
11276static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11277 const ImageInfo *image_info,Image *image)
11278{
11279 Image
11280 *jpeg_image;
11281
11282 ImageInfo
11283 *jpeg_image_info;
11284
11285 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011286 logging,
cristy3ed852e2009-09-05 21:47:34 +000011287 status;
11288
11289 size_t
11290 length;
11291
11292 unsigned char
11293 *blob,
11294 chunk[80],
11295 *p;
11296
11297 unsigned int
11298 jng_alpha_compression_method,
11299 jng_alpha_sample_depth,
11300 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011301 transparent;
11302
cristybb503372010-05-27 20:51:26 +000011303 size_t
cristy3ed852e2009-09-05 21:47:34 +000011304 jng_quality;
11305
11306 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011307 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011308
11309 blob=(unsigned char *) NULL;
11310 jpeg_image=(Image *) NULL;
11311 jpeg_image_info=(ImageInfo *) NULL;
11312
11313 status=MagickTrue;
11314 transparent=image_info->type==GrayscaleMatteType ||
11315 image_info->type==TrueColorMatteType;
11316 jng_color_type=10;
11317 jng_alpha_sample_depth=0;
11318 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11319 jng_alpha_compression_method=0;
11320
11321 if (image->matte != MagickFalse)
11322 {
11323 /* if any pixels are transparent */
11324 transparent=MagickTrue;
11325 if (image_info->compression==JPEGCompression)
11326 jng_alpha_compression_method=8;
11327 }
11328
11329 if (transparent)
11330 {
11331 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011332
cristy3ed852e2009-09-05 21:47:34 +000011333 /* Create JPEG blob, image, and image_info */
11334 if (logging != MagickFalse)
11335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11336 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000011337
cristy3ed852e2009-09-05 21:47:34 +000011338 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011339
cristy3ed852e2009-09-05 21:47:34 +000011340 if (jpeg_image_info == (ImageInfo *) NULL)
11341 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011342
cristy3ed852e2009-09-05 21:47:34 +000011343 if (logging != MagickFalse)
11344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11345 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011346
cristy3ed852e2009-09-05 21:47:34 +000011347 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011348
cristy3ed852e2009-09-05 21:47:34 +000011349 if (jpeg_image == (Image *) NULL)
11350 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011351
cristy3ed852e2009-09-05 21:47:34 +000011352 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11353 status=SeparateImageChannel(jpeg_image,OpacityChannel);
11354 status=NegateImage(jpeg_image,MagickFalse);
11355 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011356
cristy3ed852e2009-09-05 21:47:34 +000011357 if (jng_quality >= 1000)
11358 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011359
cristy3ed852e2009-09-05 21:47:34 +000011360 else
11361 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011362
cristy3ed852e2009-09-05 21:47:34 +000011363 jpeg_image_info->type=GrayscaleType;
11364 (void) SetImageType(jpeg_image,GrayscaleType);
11365 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011366 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011367 "%s",jpeg_image->filename);
11368 }
11369
11370 /* To do: check bit depth of PNG alpha channel */
11371
11372 /* Check if image is grayscale. */
11373 if (image_info->type != TrueColorMatteType && image_info->type !=
11374 TrueColorType && ImageIsGray(image))
11375 jng_color_type-=2;
11376
11377 if (transparent)
11378 {
11379 if (jng_alpha_compression_method==0)
11380 {
11381 const char
11382 *value;
11383
11384 /* Encode opacity as a grayscale PNG blob */
11385 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11386 &image->exception);
11387 if (logging != MagickFalse)
11388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11389 " Creating PNG blob.");
11390 length=0;
11391
11392 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11393 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11394 jpeg_image_info->interlace=NoInterlace;
11395
11396 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11397 &image->exception);
11398
11399 /* Retrieve sample depth used */
11400 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11401 if (value != (char *) NULL)
11402 jng_alpha_sample_depth= (unsigned int) value[0];
11403 }
11404 else
11405 {
11406 /* Encode opacity as a grayscale JPEG blob */
11407
11408 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11409 &image->exception);
11410
11411 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11412 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11413 jpeg_image_info->interlace=NoInterlace;
11414 if (logging != MagickFalse)
11415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11416 " Creating blob.");
11417 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000011418 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000011419 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011420
cristy3ed852e2009-09-05 21:47:34 +000011421 if (logging != MagickFalse)
11422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011423 " Successfully read jpeg_image into a blob, length=%.20g.",
11424 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011425
11426 }
11427 /* Destroy JPEG image and image_info */
11428 jpeg_image=DestroyImage(jpeg_image);
11429 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11430 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11431 }
11432
11433 /* Write JHDR chunk */
11434 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11435 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011436 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011437 PNGLong(chunk+4,(png_uint_32) image->columns);
11438 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011439 chunk[12]=jng_color_type;
11440 chunk[13]=8; /* sample depth */
11441 chunk[14]=8; /*jng_image_compression_method */
11442 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11443 chunk[16]=jng_alpha_sample_depth;
11444 chunk[17]=jng_alpha_compression_method;
11445 chunk[18]=0; /*jng_alpha_filter_method */
11446 chunk[19]=0; /*jng_alpha_interlace_method */
11447 (void) WriteBlob(image,20,chunk);
11448 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11449 if (logging != MagickFalse)
11450 {
11451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011452 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011453
cristy3ed852e2009-09-05 21:47:34 +000011454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011455 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011456
cristy3ed852e2009-09-05 21:47:34 +000011457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11458 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011459
cristy3ed852e2009-09-05 21:47:34 +000011460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11461 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011462
cristy3ed852e2009-09-05 21:47:34 +000011463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11464 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011465
cristy3ed852e2009-09-05 21:47:34 +000011466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11467 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011468
cristy3ed852e2009-09-05 21:47:34 +000011469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11470 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011471
cristy3ed852e2009-09-05 21:47:34 +000011472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11473 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011474
cristy3ed852e2009-09-05 21:47:34 +000011475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11476 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011477
cristy3ed852e2009-09-05 21:47:34 +000011478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11479 " JNG alpha interlace:%5d",0);
11480 }
11481
glennrp0fe50b42010-11-16 03:52:51 +000011482 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011483 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011484
11485 /*
11486 Write leading ancillary chunks
11487 */
11488
11489 if (transparent)
11490 {
11491 /*
11492 Write JNG bKGD chunk
11493 */
11494
11495 unsigned char
11496 blue,
11497 green,
11498 red;
11499
cristybb503372010-05-27 20:51:26 +000011500 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011501 num_bytes;
11502
11503 if (jng_color_type == 8 || jng_color_type == 12)
11504 num_bytes=6L;
11505 else
11506 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011507 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011508 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011509 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011510 red=ScaleQuantumToChar(image->background_color.red);
11511 green=ScaleQuantumToChar(image->background_color.green);
11512 blue=ScaleQuantumToChar(image->background_color.blue);
11513 *(chunk+4)=0;
11514 *(chunk+5)=red;
11515 *(chunk+6)=0;
11516 *(chunk+7)=green;
11517 *(chunk+8)=0;
11518 *(chunk+9)=blue;
11519 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11520 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11521 }
11522
11523 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11524 {
11525 /*
11526 Write JNG sRGB chunk
11527 */
11528 (void) WriteBlobMSBULong(image,1L);
11529 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011530 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011531
cristy3ed852e2009-09-05 21:47:34 +000011532 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011533 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011534 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011535 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011536
cristy3ed852e2009-09-05 21:47:34 +000011537 else
glennrpe610a072010-08-05 17:08:46 +000011538 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011539 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011540 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011541
cristy3ed852e2009-09-05 21:47:34 +000011542 (void) WriteBlob(image,5,chunk);
11543 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11544 }
11545 else
11546 {
11547 if (image->gamma != 0.0)
11548 {
11549 /*
11550 Write JNG gAMA chunk
11551 */
11552 (void) WriteBlobMSBULong(image,4L);
11553 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011554 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011555 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011556 (void) WriteBlob(image,8,chunk);
11557 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11558 }
glennrp0fe50b42010-11-16 03:52:51 +000011559
cristy3ed852e2009-09-05 21:47:34 +000011560 if ((mng_info->equal_chrms == MagickFalse) &&
11561 (image->chromaticity.red_primary.x != 0.0))
11562 {
11563 PrimaryInfo
11564 primary;
11565
11566 /*
11567 Write JNG cHRM chunk
11568 */
11569 (void) WriteBlobMSBULong(image,32L);
11570 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011571 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011572 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011573 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11574 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011575 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011576 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11577 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011578 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011579 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11580 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011581 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011582 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11583 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011584 (void) WriteBlob(image,36,chunk);
11585 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11586 }
11587 }
glennrp0fe50b42010-11-16 03:52:51 +000011588
cristy3ed852e2009-09-05 21:47:34 +000011589 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11590 {
11591 /*
11592 Write JNG pHYs chunk
11593 */
11594 (void) WriteBlobMSBULong(image,9L);
11595 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011596 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011597 if (image->units == PixelsPerInchResolution)
11598 {
cristy35ef8242010-06-03 16:24:13 +000011599 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011600 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011601
cristy35ef8242010-06-03 16:24:13 +000011602 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011603 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011604
cristy3ed852e2009-09-05 21:47:34 +000011605 chunk[12]=1;
11606 }
glennrp0fe50b42010-11-16 03:52:51 +000011607
cristy3ed852e2009-09-05 21:47:34 +000011608 else
11609 {
11610 if (image->units == PixelsPerCentimeterResolution)
11611 {
cristy35ef8242010-06-03 16:24:13 +000011612 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011613 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011614
cristy35ef8242010-06-03 16:24:13 +000011615 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011616 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011617
cristy3ed852e2009-09-05 21:47:34 +000011618 chunk[12]=1;
11619 }
glennrp0fe50b42010-11-16 03:52:51 +000011620
cristy3ed852e2009-09-05 21:47:34 +000011621 else
11622 {
cristy35ef8242010-06-03 16:24:13 +000011623 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11624 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011625 chunk[12]=0;
11626 }
11627 }
11628 (void) WriteBlob(image,13,chunk);
11629 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11630 }
11631
11632 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11633 {
11634 /*
11635 Write JNG oFFs chunk
11636 */
11637 (void) WriteBlobMSBULong(image,9L);
11638 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011639 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011640 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11641 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011642 chunk[12]=0;
11643 (void) WriteBlob(image,13,chunk);
11644 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11645 }
11646 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11647 {
11648 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11649 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011650 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011651 PNGLong(chunk+4,(png_uint_32) image->page.width);
11652 PNGLong(chunk+8,(png_uint_32) image->page.height);
11653 chunk[12]=0; /* unit = pixels */
11654 (void) WriteBlob(image,13,chunk);
11655 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11656 }
11657
11658
11659 if (transparent)
11660 {
11661 if (jng_alpha_compression_method==0)
11662 {
cristybb503372010-05-27 20:51:26 +000011663 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011664 i;
11665
cristybb503372010-05-27 20:51:26 +000011666 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011667 len;
11668
11669 /* Write IDAT chunk header */
11670 if (logging != MagickFalse)
11671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011672 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011673 length);
cristy3ed852e2009-09-05 21:47:34 +000011674
11675 /* Copy IDAT chunks */
11676 len=0;
11677 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011678 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011679 {
11680 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11681 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011682
cristy3ed852e2009-09-05 21:47:34 +000011683 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11684 {
11685 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011686 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011687 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011688 (void) WriteBlob(image,(size_t) len+4,p);
11689 (void) WriteBlobMSBULong(image,
11690 crc32(0,p,(uInt) len+4));
11691 }
glennrp0fe50b42010-11-16 03:52:51 +000011692
cristy3ed852e2009-09-05 21:47:34 +000011693 else
11694 {
11695 if (logging != MagickFalse)
11696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011697 " Skipping %c%c%c%c chunk, length=%.20g.",
11698 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011699 }
11700 p+=(8+len);
11701 }
11702 }
11703 else
11704 {
11705 /* Write JDAA chunk header */
11706 if (logging != MagickFalse)
11707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011708 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011709 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011710 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011711 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011712 /* Write JDAT chunk(s) data */
11713 (void) WriteBlob(image,4,chunk);
11714 (void) WriteBlob(image,length,blob);
11715 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11716 (uInt) length));
11717 }
11718 blob=(unsigned char *) RelinquishMagickMemory(blob);
11719 }
11720
11721 /* Encode image as a JPEG blob */
11722 if (logging != MagickFalse)
11723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11724 " Creating jpeg_image_info.");
11725 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11726 if (jpeg_image_info == (ImageInfo *) NULL)
11727 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11728
11729 if (logging != MagickFalse)
11730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11731 " Creating jpeg_image.");
11732
11733 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11734 if (jpeg_image == (Image *) NULL)
11735 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11736 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11737
11738 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011739 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000011740 jpeg_image->filename);
11741
11742 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11743 &image->exception);
11744
11745 if (logging != MagickFalse)
11746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011747 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11748 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011749
11750 if (jng_color_type == 8 || jng_color_type == 12)
11751 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011752
cristy3ed852e2009-09-05 21:47:34 +000011753 jpeg_image_info->quality=jng_quality % 1000;
11754 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11755 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011756
cristy3ed852e2009-09-05 21:47:34 +000011757 if (logging != MagickFalse)
11758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11759 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011760
cristy3ed852e2009-09-05 21:47:34 +000011761 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011762
cristy3ed852e2009-09-05 21:47:34 +000011763 if (logging != MagickFalse)
11764 {
11765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011766 " Successfully read jpeg_image into a blob, length=%.20g.",
11767 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011768
11769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011770 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011771 }
glennrp0fe50b42010-11-16 03:52:51 +000011772
cristy3ed852e2009-09-05 21:47:34 +000011773 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011774 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011775 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011776 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011777 (void) WriteBlob(image,4,chunk);
11778 (void) WriteBlob(image,length,blob);
11779 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11780
11781 jpeg_image=DestroyImage(jpeg_image);
11782 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11783 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11784 blob=(unsigned char *) RelinquishMagickMemory(blob);
11785
11786 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011787 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011788
11789 /* Write IEND chunk */
11790 (void) WriteBlobMSBULong(image,0L);
11791 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011792 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011793 (void) WriteBlob(image,4,chunk);
11794 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11795
11796 if (logging != MagickFalse)
11797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11798 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011799
cristy3ed852e2009-09-05 21:47:34 +000011800 return(status);
11801}
11802
11803
11804/*
11805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11806% %
11807% %
11808% %
11809% W r i t e J N G I m a g e %
11810% %
11811% %
11812% %
11813%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11814%
11815% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11816%
11817% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11818%
11819% The format of the WriteJNGImage method is:
11820%
11821% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11822%
11823% A description of each parameter follows:
11824%
11825% o image_info: the image info.
11826%
11827% o image: The image.
11828%
11829%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11830*/
11831static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11832{
11833 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011834 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011835 logging,
cristy3ed852e2009-09-05 21:47:34 +000011836 status;
11837
11838 MngInfo
11839 *mng_info;
11840
cristy3ed852e2009-09-05 21:47:34 +000011841 /*
11842 Open image file.
11843 */
11844 assert(image_info != (const ImageInfo *) NULL);
11845 assert(image_info->signature == MagickSignature);
11846 assert(image != (Image *) NULL);
11847 assert(image->signature == MagickSignature);
11848 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011849 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011850 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11851 if (status == MagickFalse)
11852 return(status);
11853
11854 /*
11855 Allocate a MngInfo structure.
11856 */
11857 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011858 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011859 if (mng_info == (MngInfo *) NULL)
11860 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11861 /*
11862 Initialize members of the MngInfo structure.
11863 */
11864 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11865 mng_info->image=image;
11866 have_mng_structure=MagickTrue;
11867
11868 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11869
11870 status=WriteOneJNGImage(mng_info,image_info,image);
11871 (void) CloseBlob(image);
11872
11873 (void) CatchImageException(image);
11874 MngInfoFreeStruct(mng_info,&have_mng_structure);
11875 if (logging != MagickFalse)
11876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11877 return(status);
11878}
11879#endif
11880
11881
11882
11883static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11884{
11885 const char
11886 *option;
11887
11888 Image
11889 *next_image;
11890
11891 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011892 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011893 status;
11894
glennrp03812ae2010-12-24 01:31:34 +000011895 volatile MagickBooleanType
11896 logging;
11897
cristy3ed852e2009-09-05 21:47:34 +000011898 MngInfo
11899 *mng_info;
11900
11901 int
cristy3ed852e2009-09-05 21:47:34 +000011902 image_count,
11903 need_iterations,
11904 need_matte;
11905
11906 volatile int
11907#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11908 defined(PNG_MNG_FEATURES_SUPPORTED)
11909 need_local_plte,
11910#endif
11911 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011912 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011913 use_global_plte;
11914
cristybb503372010-05-27 20:51:26 +000011915 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011916 i;
11917
11918 unsigned char
11919 chunk[800];
11920
11921 volatile unsigned int
11922 write_jng,
11923 write_mng;
11924
cristybb503372010-05-27 20:51:26 +000011925 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011926 scene;
11927
cristybb503372010-05-27 20:51:26 +000011928 size_t
cristy3ed852e2009-09-05 21:47:34 +000011929 final_delay=0,
11930 initial_delay;
11931
glennrpd5045b42010-03-24 12:40:35 +000011932#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011933 if (image_info->verbose)
11934 printf("Your PNG library (libpng-%s) is rather old.\n",
11935 PNG_LIBPNG_VER_STRING);
11936#endif
11937
11938 /*
11939 Open image file.
11940 */
11941 assert(image_info != (const ImageInfo *) NULL);
11942 assert(image_info->signature == MagickSignature);
11943 assert(image != (Image *) NULL);
11944 assert(image->signature == MagickSignature);
11945 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011946 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011947 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11948 if (status == MagickFalse)
11949 return(status);
11950
11951 /*
11952 Allocate a MngInfo structure.
11953 */
11954 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011955 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011956 if (mng_info == (MngInfo *) NULL)
11957 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11958 /*
11959 Initialize members of the MngInfo structure.
11960 */
11961 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11962 mng_info->image=image;
11963 have_mng_structure=MagickTrue;
11964 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
11965
11966 /*
11967 * See if user has requested a specific PNG subformat to be used
11968 * for all of the PNGs in the MNG being written, e.g.,
11969 *
11970 * convert *.png png8:animation.mng
11971 *
11972 * To do: check -define png:bit_depth and png:color_type as well,
11973 * or perhaps use mng:bit_depth and mng:color_type instead for
11974 * global settings.
11975 */
11976
11977 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11978 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11979 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11980
11981 write_jng=MagickFalse;
11982 if (image_info->compression == JPEGCompression)
11983 write_jng=MagickTrue;
11984
11985 mng_info->adjoin=image_info->adjoin &&
11986 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
11987
cristy3ed852e2009-09-05 21:47:34 +000011988 if (logging != MagickFalse)
11989 {
11990 /* Log some info about the input */
11991 Image
11992 *p;
11993
11994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11995 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000011996
cristy3ed852e2009-09-05 21:47:34 +000011997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011998 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000011999
cristy3ed852e2009-09-05 21:47:34 +000012000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12001 " Type: %d",image_info->type);
12002
12003 scene=0;
12004 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12005 {
12006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012007 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012008
cristy3ed852e2009-09-05 21:47:34 +000012009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012010 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012011
cristy3ed852e2009-09-05 21:47:34 +000012012 if (p->matte)
12013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12014 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012015
cristy3ed852e2009-09-05 21:47:34 +000012016 else
12017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12018 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012019
cristy3ed852e2009-09-05 21:47:34 +000012020 if (p->storage_class == PseudoClass)
12021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12022 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012023
cristy3ed852e2009-09-05 21:47:34 +000012024 else
12025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12026 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012027
cristy3ed852e2009-09-05 21:47:34 +000012028 if (p->colors)
12029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012030 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012031
cristy3ed852e2009-09-05 21:47:34 +000012032 else
12033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12034 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012035
cristy3ed852e2009-09-05 21:47:34 +000012036 if (mng_info->adjoin == MagickFalse)
12037 break;
12038 }
12039 }
12040
cristy3ed852e2009-09-05 21:47:34 +000012041 use_global_plte=MagickFalse;
12042 all_images_are_gray=MagickFalse;
12043#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12044 need_local_plte=MagickTrue;
12045#endif
12046 need_defi=MagickFalse;
12047 need_matte=MagickFalse;
12048 mng_info->framing_mode=1;
12049 mng_info->old_framing_mode=1;
12050
12051 if (write_mng)
12052 if (image_info->page != (char *) NULL)
12053 {
12054 /*
12055 Determine image bounding box.
12056 */
12057 SetGeometry(image,&mng_info->page);
12058 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12059 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12060 }
12061 if (write_mng)
12062 {
12063 unsigned int
12064 need_geom;
12065
12066 unsigned short
12067 red,
12068 green,
12069 blue;
12070
12071 mng_info->page=image->page;
12072 need_geom=MagickTrue;
12073 if (mng_info->page.width || mng_info->page.height)
12074 need_geom=MagickFalse;
12075 /*
12076 Check all the scenes.
12077 */
12078 initial_delay=image->delay;
12079 need_iterations=MagickFalse;
12080 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12081 mng_info->equal_physs=MagickTrue,
12082 mng_info->equal_gammas=MagickTrue;
12083 mng_info->equal_srgbs=MagickTrue;
12084 mng_info->equal_backgrounds=MagickTrue;
12085 image_count=0;
12086#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12087 defined(PNG_MNG_FEATURES_SUPPORTED)
12088 all_images_are_gray=MagickTrue;
12089 mng_info->equal_palettes=MagickFalse;
12090 need_local_plte=MagickFalse;
12091#endif
12092 for (next_image=image; next_image != (Image *) NULL; )
12093 {
12094 if (need_geom)
12095 {
12096 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12097 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012098
cristy3ed852e2009-09-05 21:47:34 +000012099 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12100 mng_info->page.height=next_image->rows+next_image->page.y;
12101 }
glennrp0fe50b42010-11-16 03:52:51 +000012102
cristy3ed852e2009-09-05 21:47:34 +000012103 if (next_image->page.x || next_image->page.y)
12104 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012105
cristy3ed852e2009-09-05 21:47:34 +000012106 if (next_image->matte)
12107 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012108
cristy3ed852e2009-09-05 21:47:34 +000012109 if ((int) next_image->dispose >= BackgroundDispose)
12110 if (next_image->matte || next_image->page.x || next_image->page.y ||
12111 ((next_image->columns < mng_info->page.width) &&
12112 (next_image->rows < mng_info->page.height)))
12113 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012114
cristy3ed852e2009-09-05 21:47:34 +000012115 if (next_image->iterations)
12116 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012117
cristy3ed852e2009-09-05 21:47:34 +000012118 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012119
cristy3ed852e2009-09-05 21:47:34 +000012120 if (final_delay != initial_delay || final_delay > 1UL*
12121 next_image->ticks_per_second)
12122 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012123
cristy3ed852e2009-09-05 21:47:34 +000012124#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12125 defined(PNG_MNG_FEATURES_SUPPORTED)
12126 /*
12127 check for global palette possibility.
12128 */
12129 if (image->matte != MagickFalse)
12130 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012131
cristy3ed852e2009-09-05 21:47:34 +000012132 if (need_local_plte == 0)
12133 {
12134 if (ImageIsGray(image) == MagickFalse)
12135 all_images_are_gray=MagickFalse;
12136 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12137 if (use_global_plte == 0)
12138 use_global_plte=mng_info->equal_palettes;
12139 need_local_plte=!mng_info->equal_palettes;
12140 }
12141#endif
12142 if (GetNextImageInList(next_image) != (Image *) NULL)
12143 {
12144 if (next_image->background_color.red !=
12145 next_image->next->background_color.red ||
12146 next_image->background_color.green !=
12147 next_image->next->background_color.green ||
12148 next_image->background_color.blue !=
12149 next_image->next->background_color.blue)
12150 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012151
cristy3ed852e2009-09-05 21:47:34 +000012152 if (next_image->gamma != next_image->next->gamma)
12153 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012154
cristy3ed852e2009-09-05 21:47:34 +000012155 if (next_image->rendering_intent !=
12156 next_image->next->rendering_intent)
12157 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012158
cristy3ed852e2009-09-05 21:47:34 +000012159 if ((next_image->units != next_image->next->units) ||
12160 (next_image->x_resolution != next_image->next->x_resolution) ||
12161 (next_image->y_resolution != next_image->next->y_resolution))
12162 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012163
cristy3ed852e2009-09-05 21:47:34 +000012164 if (mng_info->equal_chrms)
12165 {
12166 if (next_image->chromaticity.red_primary.x !=
12167 next_image->next->chromaticity.red_primary.x ||
12168 next_image->chromaticity.red_primary.y !=
12169 next_image->next->chromaticity.red_primary.y ||
12170 next_image->chromaticity.green_primary.x !=
12171 next_image->next->chromaticity.green_primary.x ||
12172 next_image->chromaticity.green_primary.y !=
12173 next_image->next->chromaticity.green_primary.y ||
12174 next_image->chromaticity.blue_primary.x !=
12175 next_image->next->chromaticity.blue_primary.x ||
12176 next_image->chromaticity.blue_primary.y !=
12177 next_image->next->chromaticity.blue_primary.y ||
12178 next_image->chromaticity.white_point.x !=
12179 next_image->next->chromaticity.white_point.x ||
12180 next_image->chromaticity.white_point.y !=
12181 next_image->next->chromaticity.white_point.y)
12182 mng_info->equal_chrms=MagickFalse;
12183 }
12184 }
12185 image_count++;
12186 next_image=GetNextImageInList(next_image);
12187 }
12188 if (image_count < 2)
12189 {
12190 mng_info->equal_backgrounds=MagickFalse;
12191 mng_info->equal_chrms=MagickFalse;
12192 mng_info->equal_gammas=MagickFalse;
12193 mng_info->equal_srgbs=MagickFalse;
12194 mng_info->equal_physs=MagickFalse;
12195 use_global_plte=MagickFalse;
12196#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12197 need_local_plte=MagickTrue;
12198#endif
12199 need_iterations=MagickFalse;
12200 }
glennrp0fe50b42010-11-16 03:52:51 +000012201
cristy3ed852e2009-09-05 21:47:34 +000012202 if (mng_info->need_fram == MagickFalse)
12203 {
12204 /*
12205 Only certain framing rates 100/n are exactly representable without
12206 the FRAM chunk but we'll allow some slop in VLC files
12207 */
12208 if (final_delay == 0)
12209 {
12210 if (need_iterations != MagickFalse)
12211 {
12212 /*
12213 It's probably a GIF with loop; don't run it *too* fast.
12214 */
glennrp02617122010-07-28 13:07:35 +000012215 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012216 {
12217 final_delay=10;
12218 (void) ThrowMagickException(&image->exception,
12219 GetMagickModule(),CoderWarning,
12220 "input has zero delay between all frames; assuming",
12221 " 10 cs `%s'","");
12222 }
cristy3ed852e2009-09-05 21:47:34 +000012223 }
12224 else
12225 mng_info->ticks_per_second=0;
12226 }
12227 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012228 mng_info->ticks_per_second=(png_uint_32)
12229 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012230 if (final_delay > 50)
12231 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012232
cristy3ed852e2009-09-05 21:47:34 +000012233 if (final_delay > 75)
12234 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012235
cristy3ed852e2009-09-05 21:47:34 +000012236 if (final_delay > 125)
12237 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012238
cristy3ed852e2009-09-05 21:47:34 +000012239 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12240 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12241 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12242 1UL*image->ticks_per_second))
12243 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12244 }
glennrp0fe50b42010-11-16 03:52:51 +000012245
cristy3ed852e2009-09-05 21:47:34 +000012246 if (mng_info->need_fram != MagickFalse)
12247 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12248 /*
12249 If pseudocolor, we should also check to see if all the
12250 palettes are identical and write a global PLTE if they are.
12251 ../glennrp Feb 99.
12252 */
12253 /*
12254 Write the MNG version 1.0 signature and MHDR chunk.
12255 */
12256 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12257 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12258 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012259 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012260 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12261 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012262 PNGLong(chunk+12,mng_info->ticks_per_second);
12263 PNGLong(chunk+16,0L); /* layer count=unknown */
12264 PNGLong(chunk+20,0L); /* frame count=unknown */
12265 PNGLong(chunk+24,0L); /* play time=unknown */
12266 if (write_jng)
12267 {
12268 if (need_matte)
12269 {
12270 if (need_defi || mng_info->need_fram || use_global_plte)
12271 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012272
cristy3ed852e2009-09-05 21:47:34 +000012273 else
12274 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12275 }
glennrp0fe50b42010-11-16 03:52:51 +000012276
cristy3ed852e2009-09-05 21:47:34 +000012277 else
12278 {
12279 if (need_defi || mng_info->need_fram || use_global_plte)
12280 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012281
cristy3ed852e2009-09-05 21:47:34 +000012282 else
12283 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12284 }
12285 }
glennrp0fe50b42010-11-16 03:52:51 +000012286
cristy3ed852e2009-09-05 21:47:34 +000012287 else
12288 {
12289 if (need_matte)
12290 {
12291 if (need_defi || mng_info->need_fram || use_global_plte)
12292 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012293
cristy3ed852e2009-09-05 21:47:34 +000012294 else
12295 PNGLong(chunk+28,9L); /* simplicity=VLC */
12296 }
glennrp0fe50b42010-11-16 03:52:51 +000012297
cristy3ed852e2009-09-05 21:47:34 +000012298 else
12299 {
12300 if (need_defi || mng_info->need_fram || use_global_plte)
12301 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012302
cristy3ed852e2009-09-05 21:47:34 +000012303 else
12304 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12305 }
12306 }
12307 (void) WriteBlob(image,32,chunk);
12308 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12309 option=GetImageOption(image_info,"mng:need-cacheoff");
12310 if (option != (const char *) NULL)
12311 {
12312 size_t
12313 length;
12314
12315 /*
12316 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12317 */
12318 PNGType(chunk,mng_nEED);
12319 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012320 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012321 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012322 length+=4;
12323 (void) WriteBlob(image,length,chunk);
12324 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12325 }
12326 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12327 (GetNextImageInList(image) != (Image *) NULL) &&
12328 (image->iterations != 1))
12329 {
12330 /*
12331 Write MNG TERM chunk
12332 */
12333 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12334 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012335 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012336 chunk[4]=3; /* repeat animation */
12337 chunk[5]=0; /* show last frame when done */
12338 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12339 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012340
cristy3ed852e2009-09-05 21:47:34 +000012341 if (image->iterations == 0)
12342 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012343
cristy3ed852e2009-09-05 21:47:34 +000012344 else
12345 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012346
cristy3ed852e2009-09-05 21:47:34 +000012347 if (logging != MagickFalse)
12348 {
12349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012350 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12351 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012352
cristy3ed852e2009-09-05 21:47:34 +000012353 if (image->iterations == 0)
12354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012355 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012356
cristy3ed852e2009-09-05 21:47:34 +000012357 else
12358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012359 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012360 }
12361 (void) WriteBlob(image,14,chunk);
12362 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12363 }
12364 /*
12365 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12366 */
12367 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12368 mng_info->equal_srgbs)
12369 {
12370 /*
12371 Write MNG sRGB chunk
12372 */
12373 (void) WriteBlobMSBULong(image,1L);
12374 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012375 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012376
cristy3ed852e2009-09-05 21:47:34 +000012377 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012378 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012379 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012380 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012381
cristy3ed852e2009-09-05 21:47:34 +000012382 else
glennrpe610a072010-08-05 17:08:46 +000012383 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012384 Magick_RenderingIntent_to_PNG_RenderingIntent(
12385 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012386
cristy3ed852e2009-09-05 21:47:34 +000012387 (void) WriteBlob(image,5,chunk);
12388 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12389 mng_info->have_write_global_srgb=MagickTrue;
12390 }
glennrp0fe50b42010-11-16 03:52:51 +000012391
cristy3ed852e2009-09-05 21:47:34 +000012392 else
12393 {
12394 if (image->gamma && mng_info->equal_gammas)
12395 {
12396 /*
12397 Write MNG gAMA chunk
12398 */
12399 (void) WriteBlobMSBULong(image,4L);
12400 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012401 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012402 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012403 (void) WriteBlob(image,8,chunk);
12404 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12405 mng_info->have_write_global_gama=MagickTrue;
12406 }
12407 if (mng_info->equal_chrms)
12408 {
12409 PrimaryInfo
12410 primary;
12411
12412 /*
12413 Write MNG cHRM chunk
12414 */
12415 (void) WriteBlobMSBULong(image,32L);
12416 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012417 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012418 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012419 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12420 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012421 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012422 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12423 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012424 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012425 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12426 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012427 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012428 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12429 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012430 (void) WriteBlob(image,36,chunk);
12431 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12432 mng_info->have_write_global_chrm=MagickTrue;
12433 }
12434 }
12435 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12436 {
12437 /*
12438 Write MNG pHYs chunk
12439 */
12440 (void) WriteBlobMSBULong(image,9L);
12441 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012442 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012443
cristy3ed852e2009-09-05 21:47:34 +000012444 if (image->units == PixelsPerInchResolution)
12445 {
cristy35ef8242010-06-03 16:24:13 +000012446 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012447 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012448
cristy35ef8242010-06-03 16:24:13 +000012449 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012450 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012451
cristy3ed852e2009-09-05 21:47:34 +000012452 chunk[12]=1;
12453 }
glennrp0fe50b42010-11-16 03:52:51 +000012454
cristy3ed852e2009-09-05 21:47:34 +000012455 else
12456 {
12457 if (image->units == PixelsPerCentimeterResolution)
12458 {
cristy35ef8242010-06-03 16:24:13 +000012459 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012460 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012461
cristy35ef8242010-06-03 16:24:13 +000012462 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012463 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012464
cristy3ed852e2009-09-05 21:47:34 +000012465 chunk[12]=1;
12466 }
glennrp0fe50b42010-11-16 03:52:51 +000012467
cristy3ed852e2009-09-05 21:47:34 +000012468 else
12469 {
cristy35ef8242010-06-03 16:24:13 +000012470 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12471 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012472 chunk[12]=0;
12473 }
12474 }
12475 (void) WriteBlob(image,13,chunk);
12476 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12477 }
12478 /*
12479 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12480 or does not cover the entire frame.
12481 */
12482 if (write_mng && (image->matte || image->page.x > 0 ||
12483 image->page.y > 0 || (image->page.width &&
12484 (image->page.width+image->page.x < mng_info->page.width))
12485 || (image->page.height && (image->page.height+image->page.y
12486 < mng_info->page.height))))
12487 {
12488 (void) WriteBlobMSBULong(image,6L);
12489 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012490 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012491 red=ScaleQuantumToShort(image->background_color.red);
12492 green=ScaleQuantumToShort(image->background_color.green);
12493 blue=ScaleQuantumToShort(image->background_color.blue);
12494 PNGShort(chunk+4,red);
12495 PNGShort(chunk+6,green);
12496 PNGShort(chunk+8,blue);
12497 (void) WriteBlob(image,10,chunk);
12498 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12499 if (mng_info->equal_backgrounds)
12500 {
12501 (void) WriteBlobMSBULong(image,6L);
12502 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012503 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012504 (void) WriteBlob(image,10,chunk);
12505 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12506 }
12507 }
12508
12509#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12510 if ((need_local_plte == MagickFalse) &&
12511 (image->storage_class == PseudoClass) &&
12512 (all_images_are_gray == MagickFalse))
12513 {
cristybb503372010-05-27 20:51:26 +000012514 size_t
cristy3ed852e2009-09-05 21:47:34 +000012515 data_length;
12516
12517 /*
12518 Write MNG PLTE chunk
12519 */
12520 data_length=3*image->colors;
12521 (void) WriteBlobMSBULong(image,data_length);
12522 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012523 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012524
cristybb503372010-05-27 20:51:26 +000012525 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012526 {
12527 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
12528 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
12529 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
12530 }
glennrp0fe50b42010-11-16 03:52:51 +000012531
cristy3ed852e2009-09-05 21:47:34 +000012532 (void) WriteBlob(image,data_length+4,chunk);
12533 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12534 mng_info->have_write_global_plte=MagickTrue;
12535 }
12536#endif
12537 }
12538 scene=0;
12539 mng_info->delay=0;
12540#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12541 defined(PNG_MNG_FEATURES_SUPPORTED)
12542 mng_info->equal_palettes=MagickFalse;
12543#endif
12544 do
12545 {
12546 if (mng_info->adjoin)
12547 {
12548#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12549 defined(PNG_MNG_FEATURES_SUPPORTED)
12550 /*
12551 If we aren't using a global palette for the entire MNG, check to
12552 see if we can use one for two or more consecutive images.
12553 */
12554 if (need_local_plte && use_global_plte && !all_images_are_gray)
12555 {
12556 if (mng_info->IsPalette)
12557 {
12558 /*
12559 When equal_palettes is true, this image has the same palette
12560 as the previous PseudoClass image
12561 */
12562 mng_info->have_write_global_plte=mng_info->equal_palettes;
12563 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12564 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12565 {
12566 /*
12567 Write MNG PLTE chunk
12568 */
cristybb503372010-05-27 20:51:26 +000012569 size_t
cristy3ed852e2009-09-05 21:47:34 +000012570 data_length;
12571
12572 data_length=3*image->colors;
12573 (void) WriteBlobMSBULong(image,data_length);
12574 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012575 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012576
cristybb503372010-05-27 20:51:26 +000012577 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012578 {
12579 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12580 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12581 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12582 }
glennrp0fe50b42010-11-16 03:52:51 +000012583
cristy3ed852e2009-09-05 21:47:34 +000012584 (void) WriteBlob(image,data_length+4,chunk);
12585 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12586 (uInt) (data_length+4)));
12587 mng_info->have_write_global_plte=MagickTrue;
12588 }
12589 }
12590 else
12591 mng_info->have_write_global_plte=MagickFalse;
12592 }
12593#endif
12594 if (need_defi)
12595 {
cristybb503372010-05-27 20:51:26 +000012596 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012597 previous_x,
12598 previous_y;
12599
12600 if (scene)
12601 {
12602 previous_x=mng_info->page.x;
12603 previous_y=mng_info->page.y;
12604 }
12605 else
12606 {
12607 previous_x=0;
12608 previous_y=0;
12609 }
12610 mng_info->page=image->page;
12611 if ((mng_info->page.x != previous_x) ||
12612 (mng_info->page.y != previous_y))
12613 {
12614 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12615 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012616 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012617 chunk[4]=0; /* object 0 MSB */
12618 chunk[5]=0; /* object 0 LSB */
12619 chunk[6]=0; /* visible */
12620 chunk[7]=0; /* abstract */
12621 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12622 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12623 (void) WriteBlob(image,16,chunk);
12624 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12625 }
12626 }
12627 }
12628
12629 mng_info->write_mng=write_mng;
12630
12631 if ((int) image->dispose >= 3)
12632 mng_info->framing_mode=3;
12633
12634 if (mng_info->need_fram && mng_info->adjoin &&
12635 ((image->delay != mng_info->delay) ||
12636 (mng_info->framing_mode != mng_info->old_framing_mode)))
12637 {
12638 if (image->delay == mng_info->delay)
12639 {
12640 /*
12641 Write a MNG FRAM chunk with the new framing mode.
12642 */
12643 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12644 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012645 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012646 chunk[4]=(unsigned char) mng_info->framing_mode;
12647 (void) WriteBlob(image,5,chunk);
12648 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12649 }
12650 else
12651 {
12652 /*
12653 Write a MNG FRAM chunk with the delay.
12654 */
12655 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12656 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012657 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012658 chunk[4]=(unsigned char) mng_info->framing_mode;
12659 chunk[5]=0; /* frame name separator (no name) */
12660 chunk[6]=2; /* flag for changing default delay */
12661 chunk[7]=0; /* flag for changing frame timeout */
12662 chunk[8]=0; /* flag for changing frame clipping */
12663 chunk[9]=0; /* flag for changing frame sync_id */
12664 PNGLong(chunk+10,(png_uint_32)
12665 ((mng_info->ticks_per_second*
12666 image->delay)/MagickMax(image->ticks_per_second,1)));
12667 (void) WriteBlob(image,14,chunk);
12668 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012669 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012670 }
12671 mng_info->old_framing_mode=mng_info->framing_mode;
12672 }
12673
12674#if defined(JNG_SUPPORTED)
12675 if (image_info->compression == JPEGCompression)
12676 {
12677 ImageInfo
12678 *write_info;
12679
12680 if (logging != MagickFalse)
12681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12682 " Writing JNG object.");
12683 /* To do: specify the desired alpha compression method. */
12684 write_info=CloneImageInfo(image_info);
12685 write_info->compression=UndefinedCompression;
12686 status=WriteOneJNGImage(mng_info,write_info,image);
12687 write_info=DestroyImageInfo(write_info);
12688 }
12689 else
12690#endif
12691 {
12692 if (logging != MagickFalse)
12693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12694 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012695
glennrpb9cfe272010-12-21 15:08:06 +000012696 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012697 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012698
12699 /* We don't want any ancillary chunks written */
12700 mng_info->ping_exclude_bKGD=MagickTrue;
12701 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012702 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012703 mng_info->ping_exclude_EXIF=MagickTrue;
12704 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012705 mng_info->ping_exclude_iCCP=MagickTrue;
12706 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12707 mng_info->ping_exclude_oFFs=MagickTrue;
12708 mng_info->ping_exclude_pHYs=MagickTrue;
12709 mng_info->ping_exclude_sRGB=MagickTrue;
12710 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012711 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012712 mng_info->ping_exclude_vpAg=MagickTrue;
12713 mng_info->ping_exclude_zCCP=MagickTrue;
12714 mng_info->ping_exclude_zTXt=MagickTrue;
12715
cristy3ed852e2009-09-05 21:47:34 +000012716 status=WriteOnePNGImage(mng_info,image_info,image);
12717 }
12718
12719 if (status == MagickFalse)
12720 {
12721 MngInfoFreeStruct(mng_info,&have_mng_structure);
12722 (void) CloseBlob(image);
12723 return(MagickFalse);
12724 }
12725 (void) CatchImageException(image);
12726 if (GetNextImageInList(image) == (Image *) NULL)
12727 break;
12728 image=SyncNextImageInList(image);
12729 status=SetImageProgress(image,SaveImagesTag,scene++,
12730 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012731
cristy3ed852e2009-09-05 21:47:34 +000012732 if (status == MagickFalse)
12733 break;
glennrp0fe50b42010-11-16 03:52:51 +000012734
cristy3ed852e2009-09-05 21:47:34 +000012735 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012736
cristy3ed852e2009-09-05 21:47:34 +000012737 if (write_mng)
12738 {
12739 while (GetPreviousImageInList(image) != (Image *) NULL)
12740 image=GetPreviousImageInList(image);
12741 /*
12742 Write the MEND chunk.
12743 */
12744 (void) WriteBlobMSBULong(image,0x00000000L);
12745 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012746 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012747 (void) WriteBlob(image,4,chunk);
12748 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12749 }
12750 /*
12751 Relinquish resources.
12752 */
12753 (void) CloseBlob(image);
12754 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012755
cristy3ed852e2009-09-05 21:47:34 +000012756 if (logging != MagickFalse)
12757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012758
cristy3ed852e2009-09-05 21:47:34 +000012759 return(MagickTrue);
12760}
glennrpd5045b42010-03-24 12:40:35 +000012761#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012762
cristy3ed852e2009-09-05 21:47:34 +000012763static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12764{
12765 image=image;
12766 printf("Your PNG library is too old: You have libpng-%s\n",
12767 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012768
cristy3ed852e2009-09-05 21:47:34 +000012769 ThrowBinaryException(CoderError,"PNG library is too old",
12770 image_info->filename);
12771}
glennrp39992b42010-11-14 00:03:43 +000012772
cristy3ed852e2009-09-05 21:47:34 +000012773static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12774{
12775 return(WritePNGImage(image_info,image));
12776}
glennrpd5045b42010-03-24 12:40:35 +000012777#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012778#endif