blob: c9c604c2b3222c07f0e4b083c90c42d3a9a11bb8 [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
glennrpb5d5e6d2011-04-27 17:08:19 +0000128/* Convenience macros for copying RGB or RGB+opacity components
129 * between a pixel and a PixelPacket.
130 */
glennrp8e045c82011-04-27 16:40:27 +0000131
glennrpb5d5e6d2011-04-27 17:08:19 +0000132#define GetRGBOPixelComponents(pixel, packet) \
glennrpbb4f99d2011-05-22 11:13:17 +0000133 { \
glennrpb5d5e6d2011-04-27 17:08:19 +0000134 (packet).red = GetRedPixelComponent((pixel)); \
135 (packet).green = GetGreenPixelComponent((pixel)); \
glennrp77110c32011-05-03 05:25:16 +0000136 (packet).blue = GetBluePixelComponent((pixel)); \
glennrpb5d5e6d2011-04-27 17:08:19 +0000137 (packet).opacity = GetOpacityPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000138 }
glennrpb5d5e6d2011-04-27 17:08:19 +0000139
glennrpbb4f99d2011-05-22 11:13:17 +0000140/* Not same as SetRGBOPixelComponent in magick/pixel.h */
glennrpb5d5e6d2011-04-27 17:08:19 +0000141#define SetRGBOPixelComponents(pixel, packet) \
glennrpbb4f99d2011-05-22 11:13:17 +0000142 { \
glennrpb5d5e6d2011-04-27 17:08:19 +0000143 SetRedPixelComponent((pixel),(packet).red); \
144 SetGreenPixelComponent((pixel),(packet).green); \
145 SetBluePixelComponent((pixel),(packet).blue); \
146 SetOpacityPixelComponent((pixel),(packet).opacity); \
glennrpbb4f99d2011-05-22 11:13:17 +0000147 }
glennrp8e045c82011-04-27 16:40:27 +0000148
glennrpb5d5e6d2011-04-27 17:08:19 +0000149#define GetRGBPixelComponents(pixel, packet) \
glennrpbb4f99d2011-05-22 11:13:17 +0000150 { \
glennrpb5d5e6d2011-04-27 17:08:19 +0000151 (packet).red = GetRedPixelComponent((pixel)); \
152 (packet).green = GetGreenPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000153 (packet).blue = GetBluePixelComponent((pixel)); \
154 }
glennrp8e045c82011-04-27 16:40:27 +0000155
glennrpb5d5e6d2011-04-27 17:08:19 +0000156#define SetRGBPixelComponents(pixel, packet) \
glennrpbb4f99d2011-05-22 11:13:17 +0000157 { \
glennrpb5d5e6d2011-04-27 17:08:19 +0000158 SetRedPixelComponent((pixel),(packet).red); \
159 SetGreenPixelComponent((pixel),(packet).green); \
glennrpbb4f99d2011-05-22 11:13:17 +0000160 SetBluePixelComponent((pixel),(packet).blue); \
161 }
glennrp8e045c82011-04-27 16:40:27 +0000162
glennrp8e58efd2011-05-20 12:16:29 +0000163/* Macros for left-bit-replication to ensure that pixels
164 * and PixelPackets all have the image->depth, and for use
165 * in PNG8 quantization.
166 */
167
168
169/* LBR01: Replicate top bit */
170
171#define LBR01RedPixelPacketComponent(pixelpacket) \
172 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
173 0 : QuantumRange);
174
175#define LBR01GreenPixelPacketComponent(pixelpacket) \
176 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
177 0 : QuantumRange);
178
179#define LBR01BluePixelPacketComponent(pixelpacket) \
180 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
181 0 : QuantumRange);
182
183#define LBR01OpacityPixelPacketComponent(pixelpacket) \
184 (pixelpacket).opacity=(ScaleQuantumToChar((pixelpacket).opacity) < 0x10 ? \
185 0 : QuantumRange);
186
187#define LBR01RGBPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000188 { \
glennrp8e58efd2011-05-20 12:16:29 +0000189 LBR01RedPixelPacketComponent((pixelpacket)); \
190 LBR01GreenPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000191 LBR01BluePixelPacketComponent((pixelpacket)); \
192 }
glennrp8e58efd2011-05-20 12:16:29 +0000193
194#define LBR01RGBOPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000195 { \
glennrp8e58efd2011-05-20 12:16:29 +0000196 LBR01RGBPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000197 LBR01OpacityPixelPacketComponent((pixelpacket)); \
198 }
glennrp8e58efd2011-05-20 12:16:29 +0000199
200#define LBR01RedPixelComponent(pixel) \
201 (ScaleQuantumToChar(GetRedPixelComponent((pixel))) < 0x10 ? \
202 0 : QuantumRange);
203
204#define LBR01GreenPixelComponent(pixel) \
205 (ScaleQuantumToChar(GetGreenPixelComponent((pixel))) < 0x10 ? \
206 0 : QuantumRange);
207
208#define LBR01BluePixelComponent(pixel) \
209 (ScaleQuantumToChar(GetBluePixelComponent((pixel))) < 0x10 ? \
210 0 : QuantumRange);
211
212#define LBR01OpacityPixelComponent(pixel) \
213 (ScaleQuantumToChar(GetOpacityPixelComponent((pixel))) < 0x10 ? \
214 0 : QuantumRange);
215
216#define LBR01RGBPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000217 { \
glennrp8e58efd2011-05-20 12:16:29 +0000218 LBR01RedPixelComponent((pixel)); \
219 LBR01GreenPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000220 LBR01BluePixelComponent((pixel)); \
221 }
glennrp8e58efd2011-05-20 12:16:29 +0000222
223#define LBR01RGBOPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000224 { \
glennrp8e58efd2011-05-20 12:16:29 +0000225 LBR01RGBPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000226 LBR01OpacityPixelComponent((pixel)); \
227 }
glennrp8e58efd2011-05-20 12:16:29 +0000228
229/* LBR02: Replicate top 2 bits */
230
231#define LBR02RedPixelPacketComponent(pixelpacket) \
232 { \
233 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
234 (pixelpacket).red=ScaleCharToQuantum( \
235 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
236 }
237#define LBR02GreenPixelPacketComponent(pixelpacket) \
238 { \
239 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
240 (pixelpacket).green=ScaleCharToQuantum( \
241 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
242 }
243#define LBR02BluePixelPacketComponent(pixelpacket) \
244 { \
245 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
246 (pixelpacket).blue=ScaleCharToQuantum( \
247 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
248 }
249#define LBR02OpacityPixelPacketComponent(pixelpacket) \
250 { \
251 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).opacity) & 0xc0; \
252 (pixelpacket).opacity=ScaleCharToQuantum( \
253 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
254 }
255
256#define LBR02RGBPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000257 { \
glennrp8e58efd2011-05-20 12:16:29 +0000258 LBR02RedPixelPacketComponent((pixelpacket)); \
259 LBR02GreenPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000260 LBR02BluePixelPacketComponent((pixelpacket)); \
261 }
glennrp8e58efd2011-05-20 12:16:29 +0000262
263#define LBR02RGBOPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000264 { \
glennrp8e58efd2011-05-20 12:16:29 +0000265 LBR02RGBPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000266 LBR02OpacityPixelPacketComponent((pixelpacket)); \
267 }
glennrp8e58efd2011-05-20 12:16:29 +0000268
269#define LBR02RedPixelComponent(pixel) \
270 { \
271 unsigned char lbr_bits=ScaleQuantumToChar(GetRedPixelComponent((pixel))) \
272 & 0xc0; \
273 SetRedPixelComponent((pixel), ScaleCharToQuantum( \
274 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6)))); \
275 }
276#define LBR02GreenPixelComponent(pixel) \
277 { \
278 unsigned char lbr_bits=ScaleQuantumToChar(GetGreenPixelComponent((pixel)))\
279 & 0xc0; \
280 SetGreenPixelComponent((pixel), ScaleCharToQuantum( \
281 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6)))); \
282 }
283#define LBR02BluePixelComponent(pixel) \
284 { \
285 unsigned char lbr_bits= \
286 ScaleQuantumToChar(GetBluePixelComponent((pixel))) & 0xc0; \
287 SetBluePixelComponent((pixel), ScaleCharToQuantum( \
288 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6)))); \
289 }
290#define LBR02OpacityPixelComponent(pixel) \
291 { \
292 unsigned char lbr_bits= \
293 ScaleQuantumToChar(GetOpacityPixelComponent((pixel))) & 0xc0; \
294 SetOpacityPixelComponent((pixel), ScaleCharToQuantum( \
295 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6)))); \
296 }
297
298#define LBR02RGBPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000299 { \
glennrp8e58efd2011-05-20 12:16:29 +0000300 LBR02RedPixelComponent((pixel)); \
301 LBR02GreenPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000302 LBR02BluePixelComponent((pixel)); \
303 }
glennrp8e58efd2011-05-20 12:16:29 +0000304
305#define LBR02RGBOPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000306 { \
glennrp8e58efd2011-05-20 12:16:29 +0000307 LBR02RGBPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000308 LBR02OpacityPixelComponent((pixel)); \
309 }
glennrp8e58efd2011-05-20 12:16:29 +0000310
311/* LBR03: Replicate top 3 bits (only used with opaque pixels during
312 PNG8 quantization) */
313
314#define LBR03RedPixelPacketComponent(pixelpacket) \
315 { \
316 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
317 (pixelpacket).red=ScaleCharToQuantum( \
318 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
319 }
320#define LBR03GreenPixelPacketComponent(pixelpacket) \
321 { \
322 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
323 (pixelpacket).green=ScaleCharToQuantum( \
324 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
325 }
326#define LBR03BluePixelPacketComponent(pixelpacket) \
327 { \
328 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
329 (pixelpacket).blue=ScaleCharToQuantum( \
330 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
331 }
332
333#define LBR03RGBPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000334 { \
glennrp8e58efd2011-05-20 12:16:29 +0000335 LBR03RedPixelPacketComponent((pixelpacket)); \
336 LBR03GreenPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000337 LBR03BluePixelPacketComponent((pixelpacket)); \
338 }
glennrp8e58efd2011-05-20 12:16:29 +0000339
340#define LBR03RedPixelComponent(pixel) \
341 { \
342 unsigned char lbr_bits=ScaleQuantumToChar(GetRedPixelComponent((pixel))) \
343 & 0xe0; \
344 SetRedPixelComponent((pixel), ScaleCharToQuantum( \
345 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6)))); \
346 }
347#define LBR03GreenPixelComponent(pixel) \
348 { \
349 unsigned char lbr_bits=ScaleQuantumToChar(GetGreenPixelComponent((pixel)))\
350 & 0xe0; \
351 SetGreenPixelComponent((pixel), ScaleCharToQuantum( \
352 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6)))); \
353 }
354#define LBR03BluePixelComponent(pixel) \
355 { \
356 unsigned char lbr_bits=ScaleQuantumToChar(GetBluePixelComponent((pixel))) \
357 & 0xe0; \
358 SetBluePixelComponent((pixel), ScaleCharToQuantum( \
359 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6)))); \
360 }
361
362#define LBR03RGBPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000363 { \
glennrp8e58efd2011-05-20 12:16:29 +0000364 LBR03RedPixelComponent((pixel)); \
365 LBR03GreenPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000366 LBR03BluePixelComponent((pixel)); \
367 }
glennrp8e58efd2011-05-20 12:16:29 +0000368
369/* LBR04: Replicate top 4 bits */
370
371#define LBR04RedPixelPacketComponent(pixelpacket) \
372 { \
373 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
374 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
375 }
376#define LBR04GreenPixelPacketComponent(pixelpacket) \
377 { \
378 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
379 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
380 }
381#define LBR04BluePixelPacketComponent(pixelpacket) \
382 { \
383 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
384 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
385 }
386#define LBR04OpacityPixelPacketComponent(pixelpacket) \
387 { \
388 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).opacity) & 0xf0; \
389 (pixelpacket).opacity=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
390 }
391
392#define LBR04RGBPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000393 { \
glennrp8e58efd2011-05-20 12:16:29 +0000394 LBR04RedPixelPacketComponent((pixelpacket)); \
395 LBR04GreenPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000396 LBR04BluePixelPacketComponent((pixelpacket)); \
397 }
glennrp8e58efd2011-05-20 12:16:29 +0000398
399#define LBR04RGBOPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000400 { \
glennrp8e58efd2011-05-20 12:16:29 +0000401 LBR04RGBPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000402 LBR04OpacityPixelPacketComponent((pixelpacket)); \
403 }
glennrp8e58efd2011-05-20 12:16:29 +0000404
405#define LBR04RedPixelComponent(pixel) \
406 { \
407 unsigned char lbr_bits=ScaleQuantumToChar(GetRedPixelComponent((pixel))) \
408 & 0xf0; \
409 SetRedPixelComponent((pixel),\
410 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4)))); \
411 }
412#define LBR04GreenPixelComponent(pixel) \
413 { \
414 unsigned char lbr_bits=ScaleQuantumToChar(GetGreenPixelComponent((pixel)))\
415 & 0xf0; \
416 SetGreenPixelComponent((pixel),\
417 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4)))); \
418 }
419#define LBR04BluePixelComponent(pixel) \
420 { \
421 unsigned char lbr_bits= \
422 ScaleQuantumToChar(GetBluePixelComponent((pixel))) & 0xf0; \
423 SetBluePixelComponent((pixel),\
424 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4)))); \
425 }
426#define LBR04OpacityPixelComponent(pixel) \
427 { \
428 unsigned char lbr_bits= \
429 ScaleQuantumToChar(GetOpacityPixelComponent((pixel))) & 0xf0; \
430 SetOpacityPixelComponent((pixel),\
431 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4)))); \
432 }
433
434#define LBR04RGBPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000435 { \
glennrp8e58efd2011-05-20 12:16:29 +0000436 LBR04RedPixelComponent((pixel)); \
437 LBR04GreenPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000438 LBR04BluePixelComponent((pixel)); \
439 }
glennrp8e58efd2011-05-20 12:16:29 +0000440
441#define LBR04RGBOPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000442 { \
glennrp8e58efd2011-05-20 12:16:29 +0000443 LBR04RGBPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000444 LBR04OpacityPixelComponent((pixel)); \
445 }
glennrp8e58efd2011-05-20 12:16:29 +0000446
447
448/* LBR08: Replicate top 8 bits */
449
450#define LBR08RedPixelPacketComponent(pixelpacket) \
451 { \
452 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
453 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
454 }
455#define LBR08GreenPixelPacketComponent(pixelpacket) \
456 { \
457 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
458 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
459 }
460#define LBR08BluePixelPacketComponent(pixelpacket) \
461 { \
462 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
463 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
464 }
465#define LBR08OpacityPixelPacketComponent(pixelpacket) \
466 { \
467 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).opacity); \
468 (pixelpacket).opacity=ScaleCharToQuantum((lbr_bits)); \
469 }
470
471#define LBR08RGBPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000472 { \
glennrp8e58efd2011-05-20 12:16:29 +0000473 LBR08RedPixelPacketComponent((pixelpacket)); \
474 LBR08GreenPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000475 LBR08BluePixelPacketComponent((pixelpacket)); \
476 }
glennrp8e58efd2011-05-20 12:16:29 +0000477
478#define LBR08RGBOPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000479 { \
glennrp8e58efd2011-05-20 12:16:29 +0000480 LBR08RGBPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000481 LBR08OpacityPixelPacketComponent((pixelpacket)); \
482 }
glennrp8e58efd2011-05-20 12:16:29 +0000483
484#define LBR08RedPixelComponent(pixel) \
485 { \
486 unsigned char lbr_bits= \
487 ScaleQuantumToChar(GetRedPixelComponent((pixel))); \
488 SetRedPixelComponent((pixel),\
489 ScaleCharToQuantum((lbr_bits))); \
490 }
491#define LBR08GreenPixelComponent(pixel) \
492 { \
493 unsigned char lbr_bits= \
494 ScaleQuantumToChar(GetGreenPixelComponent((pixel))); \
495 SetGreenPixelComponent((pixel),\
496 ScaleCharToQuantum((lbr_bits))); \
497 }
498#define LBR08BluePixelComponent(pixel) \
499 { \
500 unsigned char lbr_bits= \
501 ScaleQuantumToChar(GetBluePixelComponent((pixel))); \
502 SetBluePixelComponent((pixel),\
503 ScaleCharToQuantum((lbr_bits))); \
504 }
505#define LBR08OpacityPixelComponent(pixel) \
506 { \
507 unsigned char lbr_bits= \
508 ScaleQuantumToChar(GetOpacityPixelComponent((pixel))); \
509 SetOpacityPixelComponent((pixel),\
510 ScaleCharToQuantum((lbr_bits))); \
511 }
512
513#define LBR08RGBPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000514 { \
glennrp8e58efd2011-05-20 12:16:29 +0000515 LBR08RedPixelComponent((pixel)); \
516 LBR08GreenPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000517 LBR08BluePixelComponent((pixel)); \
518 }
glennrp8e58efd2011-05-20 12:16:29 +0000519
520#define LBR08RGBOPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000521 { \
glennrp8e58efd2011-05-20 12:16:29 +0000522 LBR08RGBPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000523 LBR08OpacityPixelComponent((pixel)); \
524 }
glennrp8e58efd2011-05-20 12:16:29 +0000525
526
527/* LBR16: Replicate top 16 bits */
528
529#define LBR16RedPixelPacketComponent(pixelpacket) \
530 { \
531 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
532 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
533 }
534#define LBR16GreenPixelPacketComponent(pixelpacket) \
535 { \
536 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
537 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
538 }
539#define LBR16BluePixelPacketComponent(pixelpacket) \
540 { \
541 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
542 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
543 }
544#define LBR16OpacityPixelPacketComponent(pixelpacket) \
545 { \
546 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).opacity); \
547 (pixelpacket).opacity=ScaleShortToQuantum((lbr_bits)); \
548 }
549
550#define LBR16RGBPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000551 { \
glennrp8e58efd2011-05-20 12:16:29 +0000552 LBR16RedPixelPacketComponent((pixelpacket)); \
553 LBR16GreenPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000554 LBR16BluePixelPacketComponent((pixelpacket)); \
555 }
glennrp8e58efd2011-05-20 12:16:29 +0000556
557#define LBR16RGBOPixelPacketComponent(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000558 { \
glennrp8e58efd2011-05-20 12:16:29 +0000559 LBR16RGBPixelPacketComponent((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000560 LBR16OpacityPixelPacketComponent((pixelpacket)); \
561 }
glennrp8e58efd2011-05-20 12:16:29 +0000562
563#define LBR16RedPixelComponent(pixel) \
564 { \
565 unsigned short lbr_bits= \
566 ScaleQuantumToShort(GetRedPixelComponent((pixel))); \
567 SetRedPixelComponent((pixel),\
568 ScaleShortToQuantum((lbr_bits))); \
569 }
570#define LBR16GreenPixelComponent(pixel) \
571 { \
572 unsigned short lbr_bits= \
573 ScaleQuantumToShort(GetGreenPixelComponent((pixel))); \
574 SetGreenPixelComponent((pixel),\
575 ScaleShortToQuantum((lbr_bits))); \
576 }
577#define LBR16BluePixelComponent(pixel) \
578 { \
579 unsigned short lbr_bits= \
580 ScaleQuantumToShort(GetBluePixelComponent((pixel))); \
581 SetBluePixelComponent((pixel),\
582 ScaleShortToQuantum((lbr_bits))); \
583 }
584#define LBR16OpacityPixelComponent(pixel) \
585 { \
586 unsigned short lbr_bits= \
587 ScaleQuantumToShort(GetOpacityPixelComponent((pixel))); \
588 SetOpacityPixelComponent((pixel),\
589 ScaleShortToQuantum((lbr_bits))); \
590 }
591
592#define LBR16RGBPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000593 { \
glennrp8e58efd2011-05-20 12:16:29 +0000594 LBR16RedPixelComponent((pixel)); \
595 LBR16GreenPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000596 LBR16BluePixelComponent((pixel)); \
597 }
glennrp8e58efd2011-05-20 12:16:29 +0000598
599#define LBR16RGBOPixelComponent(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000600 { \
glennrp8e58efd2011-05-20 12:16:29 +0000601 LBR16RGBPixelComponent((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000602 LBR16OpacityPixelComponent((pixel)); \
603 }
glennrp8e58efd2011-05-20 12:16:29 +0000604
cristy3ed852e2009-09-05 21:47:34 +0000605/*
606 Establish thread safety.
607 setjmp/longjmp is claimed to be safe on these platforms:
608 setjmp/longjmp is alleged to be unsafe on these platforms:
609*/
610#ifndef SETJMP_IS_THREAD_SAFE
611#define PNG_SETJMP_NOT_THREAD_SAFE
612#endif
613
614#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
615static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000616 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000617#endif
618
619/*
620 This temporary until I set up malloc'ed object attributes array.
621 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
622 waste more memory.
623*/
624#define MNG_MAX_OBJECTS 256
625
626/*
627 If this not defined, spec is interpreted strictly. If it is
628 defined, an attempt will be made to recover from some errors,
629 including
630 o global PLTE too short
631*/
632#undef MNG_LOOSE
633
634/*
635 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
636 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
637 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
638 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
639 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
640 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
641 will be enabled by default in libpng-1.2.0.
642*/
cristy3ed852e2009-09-05 21:47:34 +0000643#ifdef PNG_MNG_FEATURES_SUPPORTED
644# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
645# define PNG_READ_EMPTY_PLTE_SUPPORTED
646# endif
647# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
648# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
649# endif
650#endif
651
652/*
cristybb503372010-05-27 20:51:26 +0000653 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000654 This macro is only defined in libpng-1.0.3 and later.
655 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
656*/
657#ifndef PNG_UINT_31_MAX
658#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
659#endif
660
661/*
662 Constant strings for known chunk types. If you need to add a chunk,
663 add a string holding the name here. To make the code more
664 portable, we use ASCII numbers like this, not characters.
665*/
666
667static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
668static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
669static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
670static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
671static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
672static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
673static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
674static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
675static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
676static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
677static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
678static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
679static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
680static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
681static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
682static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
683static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
684static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
685static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
686static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
687static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
688static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
689static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
690static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
691static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
692static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
693static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
694static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
695static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
696static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
697static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
698static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
699static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
700static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
701
702#if defined(JNG_SUPPORTED)
703static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
704static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
705static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
706static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
707static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
708static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
709#endif
710
711/*
712Other known chunks that are not yet supported by ImageMagick:
713static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
714static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
715static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
716static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
717static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
718static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
719static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
720static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
721*/
722
723typedef struct _MngBox
724{
cristy8182b072010-05-30 20:10:53 +0000725 long
cristy3ed852e2009-09-05 21:47:34 +0000726 left,
727 right,
728 top,
729 bottom;
730} MngBox;
731
732typedef struct _MngPair
733{
cristy8182b072010-05-30 20:10:53 +0000734 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000735 a,
736 b;
737} MngPair;
738
739#ifdef MNG_OBJECT_BUFFERS
740typedef struct _MngBuffer
741{
742
cristybb503372010-05-27 20:51:26 +0000743 size_t
cristy3ed852e2009-09-05 21:47:34 +0000744 height,
745 width;
746
747 Image
748 *image;
749
750 png_color
751 plte[256];
752
753 int
754 reference_count;
755
756 unsigned char
757 alpha_sample_depth,
758 compression_method,
759 color_type,
760 concrete,
761 filter_method,
762 frozen,
763 image_type,
764 interlace_method,
765 pixel_sample_depth,
766 plte_length,
767 sample_depth,
768 viewable;
769} MngBuffer;
770#endif
771
772typedef struct _MngInfo
773{
774
775#ifdef MNG_OBJECT_BUFFERS
776 MngBuffer
777 *ob[MNG_MAX_OBJECTS];
778#endif
779
780 Image *
781 image;
782
783 RectangleInfo
784 page;
785
786 int
787 adjoin,
788#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
789 bytes_in_read_buffer,
790 found_empty_plte,
791#endif
792 equal_backgrounds,
793 equal_chrms,
794 equal_gammas,
795#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
796 defined(PNG_MNG_FEATURES_SUPPORTED)
797 equal_palettes,
798#endif
799 equal_physs,
800 equal_srgbs,
801 framing_mode,
802 have_global_bkgd,
803 have_global_chrm,
804 have_global_gama,
805 have_global_phys,
806 have_global_sbit,
807 have_global_srgb,
808 have_saved_bkgd_index,
809 have_write_global_chrm,
810 have_write_global_gama,
811 have_write_global_plte,
812 have_write_global_srgb,
813 need_fram,
814 object_id,
815 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000816 saved_bkgd_index;
817
818 int
819 new_number_colors;
820
cristybb503372010-05-27 20:51:26 +0000821 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000822 image_found,
823 loop_count[256],
824 loop_iteration[256],
825 scenes_found,
826 x_off[MNG_MAX_OBJECTS],
827 y_off[MNG_MAX_OBJECTS];
828
829 MngBox
830 clip,
831 frame,
832 image_box,
833 object_clip[MNG_MAX_OBJECTS];
834
835 unsigned char
836 /* These flags could be combined into one byte */
837 exists[MNG_MAX_OBJECTS],
838 frozen[MNG_MAX_OBJECTS],
839 loop_active[256],
840 invisible[MNG_MAX_OBJECTS],
841 viewable[MNG_MAX_OBJECTS];
842
843 MagickOffsetType
844 loop_jump[256];
845
846 png_colorp
847 global_plte;
848
849 png_color_8
850 global_sbit;
851
852 png_byte
853#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
854 read_buffer[8],
855#endif
856 global_trns[256];
857
858 float
859 global_gamma;
860
861 ChromaticityInfo
862 global_chrm;
863
864 RenderingIntent
865 global_srgb_intent;
866
cristy35ef8242010-06-03 16:24:13 +0000867 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000868 delay,
869 global_plte_length,
870 global_trns_length,
871 global_x_pixels_per_unit,
872 global_y_pixels_per_unit,
873 mng_width,
874 mng_height,
875 ticks_per_second;
876
glennrpb9cfe272010-12-21 15:08:06 +0000877 MagickBooleanType
878 need_blob;
879
cristy3ed852e2009-09-05 21:47:34 +0000880 unsigned int
881 IsPalette,
882 global_phys_unit_type,
883 basi_warning,
884 clon_warning,
885 dhdr_warning,
886 jhdr_warning,
887 magn_warning,
888 past_warning,
889 phyg_warning,
890 phys_warning,
891 sbit_warning,
892 show_warning,
893 mng_type,
894 write_mng,
895 write_png_colortype,
896 write_png_depth,
897 write_png8,
898 write_png24,
899 write_png32;
900
901#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000902 size_t
cristy3ed852e2009-09-05 21:47:34 +0000903 basi_width,
904 basi_height;
905
906 unsigned int
907 basi_depth,
908 basi_color_type,
909 basi_compression_method,
910 basi_filter_type,
911 basi_interlace_method,
912 basi_red,
913 basi_green,
914 basi_blue,
915 basi_alpha,
916 basi_viewable;
917#endif
918
919 png_uint_16
920 magn_first,
921 magn_last,
922 magn_mb,
923 magn_ml,
924 magn_mr,
925 magn_mt,
926 magn_mx,
927 magn_my,
928 magn_methx,
929 magn_methy;
930
931 PixelPacket
932 mng_global_bkgd;
933
glennrp26f37912010-12-23 16:22:42 +0000934 /* Added at version 6.6.6-7 */
935 MagickBooleanType
936 ping_exclude_bKGD,
937 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000938 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000939 ping_exclude_EXIF,
940 ping_exclude_gAMA,
941 ping_exclude_iCCP,
942 /* ping_exclude_iTXt, */
943 ping_exclude_oFFs,
944 ping_exclude_pHYs,
945 ping_exclude_sRGB,
946 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000947 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000948 ping_exclude_vpAg,
949 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000950 ping_exclude_zTXt,
951 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000952
cristy3ed852e2009-09-05 21:47:34 +0000953} MngInfo;
954#endif /* VER */
955
956/*
957 Forward declarations.
958*/
959static MagickBooleanType
960 WritePNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000961
cristy3ed852e2009-09-05 21:47:34 +0000962static MagickBooleanType
963 WriteMNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000964
cristy3ed852e2009-09-05 21:47:34 +0000965#if defined(JNG_SUPPORTED)
966static MagickBooleanType
967 WriteJNGImage(const ImageInfo *,Image *);
968#endif
969
glennrp0c3e06b2010-11-19 13:45:02 +0000970#if PNG_LIBPNG_VER > 10011
971
glennrpfd05d622011-02-25 04:10:33 +0000972
glennrp0c3e06b2010-11-19 13:45:02 +0000973#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
974static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000975LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000976{
glennrp67b9c1a2011-04-22 18:47:36 +0000977 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
978 *
979 * This is true if the high byte and the next highest byte of
980 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000981 * are equal to each other. We check this by seeing if the samples
982 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000983 *
984 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000985 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000986 */
987
glennrp3faa9a32011-04-23 14:00:25 +0000988#define QuantumToCharToQuantumEqQuantum(quantum) \
989 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
990
glennrp0c3e06b2010-11-19 13:45:02 +0000991 MagickBooleanType
992 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000993
glennrp03e11f62011-04-22 13:30:16 +0000994 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000995 {
996
997 const PixelPacket
998 *p;
999
1000 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +00001001 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
1002 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
1003 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
1004 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001005
1006 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
1007 {
1008 int indx;
1009
1010 for (indx=0; indx < (ssize_t) image->colors; indx++)
1011 {
glennrp3faa9a32011-04-23 14:00:25 +00001012 ok_to_reduce=(
1013 QuantumToCharToQuantumEqQuantum(
1014 image->colormap[indx].red) &&
1015 QuantumToCharToQuantumEqQuantum(
1016 image->colormap[indx].green) &&
1017 QuantumToCharToQuantumEqQuantum(
1018 image->colormap[indx].blue)) ?
1019 MagickTrue : MagickFalse;
1020
glennrp0c3e06b2010-11-19 13:45:02 +00001021 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00001022 break;
glennrp0c3e06b2010-11-19 13:45:02 +00001023 }
1024 }
1025
1026 if ((ok_to_reduce != MagickFalse) &&
1027 (image->storage_class != PseudoClass))
1028 {
1029 ssize_t
1030 y;
1031
1032 register ssize_t
1033 x;
1034
1035 for (y=0; y < (ssize_t) image->rows; y++)
1036 {
1037 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1038
1039 if (p == (const PixelPacket *) NULL)
1040 {
1041 ok_to_reduce = MagickFalse;
1042 break;
1043 }
1044
1045 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1046 {
glennrp3faa9a32011-04-23 14:00:25 +00001047 ok_to_reduce=
1048 QuantumToCharToQuantumEqQuantum(GetRedPixelComponent(p)) &&
1049 QuantumToCharToQuantumEqQuantum(GetGreenPixelComponent(p)) &&
1050 QuantumToCharToQuantumEqQuantum(GetBluePixelComponent(p)) ?
1051 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001052
1053 if (ok_to_reduce == MagickFalse)
1054 break;
1055
1056 p++;
1057 }
glennrp8640fb52010-11-23 15:48:26 +00001058 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001059 break;
1060 }
1061 }
1062
1063 if (ok_to_reduce != MagickFalse)
1064 {
glennrp0c3e06b2010-11-19 13:45:02 +00001065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001066 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001067 }
glennrpa6a06632011-01-19 15:15:34 +00001068 else
1069 {
1070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001071 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001072 }
glennrp0c3e06b2010-11-19 13:45:02 +00001073 }
1074
1075 return ok_to_reduce;
1076}
1077#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1078
glennrpe610a072010-08-05 17:08:46 +00001079static int
glennrpcf002022011-01-30 02:38:15 +00001080Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001081{
glennrpe610a072010-08-05 17:08:46 +00001082 switch (intent)
1083 {
1084 case PerceptualIntent:
1085 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001086
glennrpe610a072010-08-05 17:08:46 +00001087 case RelativeIntent:
1088 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001089
glennrpe610a072010-08-05 17:08:46 +00001090 case SaturationIntent:
1091 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001092
glennrpe610a072010-08-05 17:08:46 +00001093 case AbsoluteIntent:
1094 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001095
glennrpe610a072010-08-05 17:08:46 +00001096 default:
1097 return -1;
1098 }
1099}
1100
1101static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001102Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001103{
glennrpcf002022011-01-30 02:38:15 +00001104 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001105 {
1106 case 0:
1107 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001108
glennrpe610a072010-08-05 17:08:46 +00001109 case 1:
1110 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001111
glennrpe610a072010-08-05 17:08:46 +00001112 case 2:
1113 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001114
glennrpe610a072010-08-05 17:08:46 +00001115 case 3:
1116 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001117
glennrpe610a072010-08-05 17:08:46 +00001118 default:
1119 return UndefinedIntent;
1120 }
1121}
1122
cristybb503372010-05-27 20:51:26 +00001123static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001124{
1125 if (x > y)
1126 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001127
cristy3ed852e2009-09-05 21:47:34 +00001128 return(y);
1129}
glennrp0c3e06b2010-11-19 13:45:02 +00001130
cristybb503372010-05-27 20:51:26 +00001131static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001132{
1133 if (x < y)
1134 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001135
cristy3ed852e2009-09-05 21:47:34 +00001136 return(y);
1137}
glennrp0c3e06b2010-11-19 13:45:02 +00001138
cristy3ed852e2009-09-05 21:47:34 +00001139
1140/*
1141%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1142% %
1143% %
1144% %
1145% I m a g e I s G r a y %
1146% %
1147% %
1148% %
1149%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1150% %
1151% Like IsGrayImage except does not change DirectClass to PseudoClass %
1152% %
1153%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1154*/
1155static MagickBooleanType ImageIsGray(Image *image)
1156{
1157 register const PixelPacket
1158 *p;
1159
cristybb503372010-05-27 20:51:26 +00001160 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001161 i,
1162 x,
1163 y;
1164
1165 assert(image != (Image *) NULL);
1166 assert(image->signature == MagickSignature);
1167 if (image->debug != MagickFalse)
1168 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1169
1170 if (image->storage_class == PseudoClass)
1171 {
cristybb503372010-05-27 20:51:26 +00001172 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001173 if (IsGray(image->colormap+i) == MagickFalse)
1174 return(MagickFalse);
1175 return(MagickTrue);
1176 }
cristybb503372010-05-27 20:51:26 +00001177 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001178 {
1179 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1180 if (p == (const PixelPacket *) NULL)
1181 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001182 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001183 {
1184 if (IsGray(p) == MagickFalse)
1185 return(MagickFalse);
1186 p++;
1187 }
1188 }
1189 return(MagickTrue);
1190}
glennrpd5045b42010-03-24 12:40:35 +00001191#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001192#endif /* MAGICKCORE_PNG_DELEGATE */
1193
1194/*
1195%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1196% %
1197% %
1198% %
1199% I s M N G %
1200% %
1201% %
1202% %
1203%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1204%
1205% IsMNG() returns MagickTrue if the image format type, identified by the
1206% magick string, is MNG.
1207%
1208% The format of the IsMNG method is:
1209%
1210% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1211%
1212% A description of each parameter follows:
1213%
1214% o magick: compare image format pattern against these bytes.
1215%
1216% o length: Specifies the length of the magick string.
1217%
1218%
1219*/
1220static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1221{
1222 if (length < 8)
1223 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001224
cristy3ed852e2009-09-05 21:47:34 +00001225 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1226 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001227
cristy3ed852e2009-09-05 21:47:34 +00001228 return(MagickFalse);
1229}
1230
1231/*
1232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1233% %
1234% %
1235% %
1236% I s J N G %
1237% %
1238% %
1239% %
1240%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1241%
1242% IsJNG() returns MagickTrue if the image format type, identified by the
1243% magick string, is JNG.
1244%
1245% The format of the IsJNG method is:
1246%
1247% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1248%
1249% A description of each parameter follows:
1250%
1251% o magick: compare image format pattern against these bytes.
1252%
1253% o length: Specifies the length of the magick string.
1254%
1255%
1256*/
1257static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1258{
1259 if (length < 8)
1260 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001261
cristy3ed852e2009-09-05 21:47:34 +00001262 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1263 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001264
cristy3ed852e2009-09-05 21:47:34 +00001265 return(MagickFalse);
1266}
1267
1268/*
1269%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1270% %
1271% %
1272% %
1273% I s P N G %
1274% %
1275% %
1276% %
1277%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1278%
1279% IsPNG() returns MagickTrue if the image format type, identified by the
1280% magick string, is PNG.
1281%
1282% The format of the IsPNG method is:
1283%
1284% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1285%
1286% A description of each parameter follows:
1287%
1288% o magick: compare image format pattern against these bytes.
1289%
1290% o length: Specifies the length of the magick string.
1291%
1292*/
1293static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1294{
1295 if (length < 8)
1296 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001297
cristy3ed852e2009-09-05 21:47:34 +00001298 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1299 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001300
cristy3ed852e2009-09-05 21:47:34 +00001301 return(MagickFalse);
1302}
1303
1304#if defined(MAGICKCORE_PNG_DELEGATE)
1305#if defined(__cplusplus) || defined(c_plusplus)
1306extern "C" {
1307#endif
1308
glennrpd5045b42010-03-24 12:40:35 +00001309#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001310static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001311{
1312 unsigned char
1313 buffer[4];
1314
1315 assert(image != (Image *) NULL);
1316 assert(image->signature == MagickSignature);
1317 buffer[0]=(unsigned char) (value >> 24);
1318 buffer[1]=(unsigned char) (value >> 16);
1319 buffer[2]=(unsigned char) (value >> 8);
1320 buffer[3]=(unsigned char) value;
1321 return((size_t) WriteBlob(image,4,buffer));
1322}
1323
1324static void PNGLong(png_bytep p,png_uint_32 value)
1325{
1326 *p++=(png_byte) ((value >> 24) & 0xff);
1327 *p++=(png_byte) ((value >> 16) & 0xff);
1328 *p++=(png_byte) ((value >> 8) & 0xff);
1329 *p++=(png_byte) (value & 0xff);
1330}
1331
glennrpa521b2f2010-10-29 04:11:03 +00001332#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001333static void PNGsLong(png_bytep p,png_int_32 value)
1334{
1335 *p++=(png_byte) ((value >> 24) & 0xff);
1336 *p++=(png_byte) ((value >> 16) & 0xff);
1337 *p++=(png_byte) ((value >> 8) & 0xff);
1338 *p++=(png_byte) (value & 0xff);
1339}
glennrpa521b2f2010-10-29 04:11:03 +00001340#endif
cristy3ed852e2009-09-05 21:47:34 +00001341
1342static void PNGShort(png_bytep p,png_uint_16 value)
1343{
1344 *p++=(png_byte) ((value >> 8) & 0xff);
1345 *p++=(png_byte) (value & 0xff);
1346}
1347
1348static void PNGType(png_bytep p,png_bytep type)
1349{
1350 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1351}
1352
glennrp03812ae2010-12-24 01:31:34 +00001353static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1354 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001355{
1356 if (logging != MagickFalse)
1357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001358 " Writing %c%c%c%c chunk, length: %.20g",
1359 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001360}
glennrpd5045b42010-03-24 12:40:35 +00001361#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001362
1363#if defined(__cplusplus) || defined(c_plusplus)
1364}
1365#endif
1366
glennrpd5045b42010-03-24 12:40:35 +00001367#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001368/*
1369%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1370% %
1371% %
1372% %
1373% R e a d P N G I m a g e %
1374% %
1375% %
1376% %
1377%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1378%
1379% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1380% Multiple-image Network Graphics (MNG) image file and returns it. It
1381% allocates the memory necessary for the new Image structure and returns a
1382% pointer to the new image or set of images.
1383%
1384% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1385%
1386% The format of the ReadPNGImage method is:
1387%
1388% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1389%
1390% A description of each parameter follows:
1391%
1392% o image_info: the image info.
1393%
1394% o exception: return any errors or warnings in this structure.
1395%
1396% To do, more or less in chronological order (as of version 5.5.2,
1397% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1398%
1399% Get 16-bit cheap transparency working.
1400%
1401% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1402%
1403% Preserve all unknown and not-yet-handled known chunks found in input
1404% PNG file and copy them into output PNG files according to the PNG
1405% copying rules.
1406%
1407% (At this point, PNG encoding should be in full MNG compliance)
1408%
1409% Provide options for choice of background to use when the MNG BACK
1410% chunk is not present or is not mandatory (i.e., leave transparent,
1411% user specified, MNG BACK, PNG bKGD)
1412%
1413% Implement LOOP/ENDL [done, but could do discretionary loops more
1414% efficiently by linking in the duplicate frames.].
1415%
1416% Decode and act on the MHDR simplicity profile (offer option to reject
1417% files or attempt to process them anyway when the profile isn't LC or VLC).
1418%
1419% Upgrade to full MNG without Delta-PNG.
1420%
1421% o BACK [done a while ago except for background image ID]
1422% o MOVE [done 15 May 1999]
1423% o CLIP [done 15 May 1999]
1424% o DISC [done 19 May 1999]
1425% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1426% o SEEK [partially done 19 May 1999 (discard function only)]
1427% o SHOW
1428% o PAST
1429% o BASI
1430% o MNG-level tEXt/iTXt/zTXt
1431% o pHYg
1432% o pHYs
1433% o sBIT
1434% o bKGD
1435% o iTXt (wait for libpng implementation).
1436%
1437% Use the scene signature to discover when an identical scene is
1438% being reused, and just point to the original image->exception instead
1439% of storing another set of pixels. This not specific to MNG
1440% but could be applied generally.
1441%
1442% Upgrade to full MNG with Delta-PNG.
1443%
1444% JNG tEXt/iTXt/zTXt
1445%
1446% We will not attempt to read files containing the CgBI chunk.
1447% They are really Xcode files meant for display on the iPhone.
1448% These are not valid PNG files and it is impossible to recover
1449% the orginal PNG from files that have been converted to Xcode-PNG,
1450% since irretrievable loss of color data has occurred due to the
1451% use of premultiplied alpha.
1452*/
1453
1454#if defined(__cplusplus) || defined(c_plusplus)
1455extern "C" {
1456#endif
1457
1458/*
1459 This the function that does the actual reading of data. It is
1460 the same as the one supplied in libpng, except that it receives the
1461 datastream from the ReadBlob() function instead of standard input.
1462*/
1463static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1464{
1465 Image
1466 *image;
1467
1468 image=(Image *) png_get_io_ptr(png_ptr);
1469 if (length)
1470 {
1471 png_size_t
1472 check;
1473
1474 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1475 if (check != length)
1476 {
1477 char
1478 msg[MaxTextExtent];
1479
cristy3b6fd2e2011-05-20 12:53:50 +00001480 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001481 "Expected %.20g bytes; found %.20g bytes",(double) length,
1482 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001483 png_warning(png_ptr,msg);
1484 png_error(png_ptr,"Read Exception");
1485 }
1486 }
1487}
1488
1489#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1490 !defined(PNG_MNG_FEATURES_SUPPORTED)
1491/* We use mng_get_data() instead of png_get_data() if we have a libpng
1492 * older than libpng-1.0.3a, which was the first to allow the empty
1493 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1494 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1495 * encountered after an empty PLTE, so we have to look ahead for bKGD
1496 * chunks and remove them from the datastream that is passed to libpng,
1497 * and store their contents for later use.
1498 */
1499static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1500{
1501 MngInfo
1502 *mng_info;
1503
1504 Image
1505 *image;
1506
1507 png_size_t
1508 check;
1509
cristybb503372010-05-27 20:51:26 +00001510 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001511 i;
1512
1513 i=0;
1514 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1515 image=(Image *) mng_info->image;
1516 while (mng_info->bytes_in_read_buffer && length)
1517 {
1518 data[i]=mng_info->read_buffer[i];
1519 mng_info->bytes_in_read_buffer--;
1520 length--;
1521 i++;
1522 }
1523 if (length)
1524 {
1525 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001526
cristy3ed852e2009-09-05 21:47:34 +00001527 if (check != length)
1528 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001529
cristy3ed852e2009-09-05 21:47:34 +00001530 if (length == 4)
1531 {
1532 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1533 (data[3] == 0))
1534 {
1535 check=(png_size_t) ReadBlob(image,(size_t) length,
1536 (char *) mng_info->read_buffer);
1537 mng_info->read_buffer[4]=0;
1538 mng_info->bytes_in_read_buffer=4;
1539 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1540 mng_info->found_empty_plte=MagickTrue;
1541 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1542 {
1543 mng_info->found_empty_plte=MagickFalse;
1544 mng_info->have_saved_bkgd_index=MagickFalse;
1545 }
1546 }
glennrp0fe50b42010-11-16 03:52:51 +00001547
cristy3ed852e2009-09-05 21:47:34 +00001548 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1549 (data[3] == 1))
1550 {
1551 check=(png_size_t) ReadBlob(image,(size_t) length,
1552 (char *) mng_info->read_buffer);
1553 mng_info->read_buffer[4]=0;
1554 mng_info->bytes_in_read_buffer=4;
1555 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1556 if (mng_info->found_empty_plte)
1557 {
1558 /*
1559 Skip the bKGD data byte and CRC.
1560 */
1561 check=(png_size_t)
1562 ReadBlob(image,5,(char *) mng_info->read_buffer);
1563 check=(png_size_t) ReadBlob(image,(size_t) length,
1564 (char *) mng_info->read_buffer);
1565 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1566 mng_info->have_saved_bkgd_index=MagickTrue;
1567 mng_info->bytes_in_read_buffer=0;
1568 }
1569 }
1570 }
1571 }
1572}
1573#endif
1574
1575static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1576{
1577 Image
1578 *image;
1579
1580 image=(Image *) png_get_io_ptr(png_ptr);
1581 if (length)
1582 {
1583 png_size_t
1584 check;
1585
cristybb503372010-05-27 20:51:26 +00001586 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001587
cristy3ed852e2009-09-05 21:47:34 +00001588 if (check != length)
1589 png_error(png_ptr,"WriteBlob Failed");
1590 }
1591}
1592
1593static void png_flush_data(png_structp png_ptr)
1594{
1595 (void) png_ptr;
1596}
1597
1598#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1599static int PalettesAreEqual(Image *a,Image *b)
1600{
cristybb503372010-05-27 20:51:26 +00001601 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001602 i;
1603
1604 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1605 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001606
cristy3ed852e2009-09-05 21:47:34 +00001607 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1608 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001609
cristy3ed852e2009-09-05 21:47:34 +00001610 if (a->colors != b->colors)
1611 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001612
cristybb503372010-05-27 20:51:26 +00001613 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001614 {
1615 if ((a->colormap[i].red != b->colormap[i].red) ||
1616 (a->colormap[i].green != b->colormap[i].green) ||
1617 (a->colormap[i].blue != b->colormap[i].blue))
1618 return((int) MagickFalse);
1619 }
glennrp0fe50b42010-11-16 03:52:51 +00001620
cristy3ed852e2009-09-05 21:47:34 +00001621 return((int) MagickTrue);
1622}
1623#endif
1624
1625static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1626{
1627 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1628 mng_info->exists[i] && !mng_info->frozen[i])
1629 {
1630#ifdef MNG_OBJECT_BUFFERS
1631 if (mng_info->ob[i] != (MngBuffer *) NULL)
1632 {
1633 if (mng_info->ob[i]->reference_count > 0)
1634 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001635
cristy3ed852e2009-09-05 21:47:34 +00001636 if (mng_info->ob[i]->reference_count == 0)
1637 {
1638 if (mng_info->ob[i]->image != (Image *) NULL)
1639 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001640
cristy3ed852e2009-09-05 21:47:34 +00001641 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1642 }
1643 }
1644 mng_info->ob[i]=(MngBuffer *) NULL;
1645#endif
1646 mng_info->exists[i]=MagickFalse;
1647 mng_info->invisible[i]=MagickFalse;
1648 mng_info->viewable[i]=MagickFalse;
1649 mng_info->frozen[i]=MagickFalse;
1650 mng_info->x_off[i]=0;
1651 mng_info->y_off[i]=0;
1652 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001653 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001654 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001655 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001656 }
1657}
1658
glennrp21f0e622011-01-07 16:20:57 +00001659static void MngInfoFreeStruct(MngInfo *mng_info,
1660 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001661{
glennrp21f0e622011-01-07 16:20:57 +00001662 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001663 {
cristybb503372010-05-27 20:51:26 +00001664 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001665 i;
1666
1667 for (i=1; i < MNG_MAX_OBJECTS; i++)
1668 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001669
cristy3ed852e2009-09-05 21:47:34 +00001670 if (mng_info->global_plte != (png_colorp) NULL)
1671 mng_info->global_plte=(png_colorp)
1672 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001673
cristy3ed852e2009-09-05 21:47:34 +00001674 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1675 *have_mng_structure=MagickFalse;
1676 }
1677}
1678
1679static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1680{
1681 MngBox
1682 box;
1683
1684 box=box1;
1685 if (box.left < box2.left)
1686 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001687
cristy3ed852e2009-09-05 21:47:34 +00001688 if (box.top < box2.top)
1689 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001690
cristy3ed852e2009-09-05 21:47:34 +00001691 if (box.right > box2.right)
1692 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001693
cristy3ed852e2009-09-05 21:47:34 +00001694 if (box.bottom > box2.bottom)
1695 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001696
cristy3ed852e2009-09-05 21:47:34 +00001697 return box;
1698}
1699
1700static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1701{
1702 MngBox
1703 box;
1704
1705 /*
1706 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1707 */
cristybb503372010-05-27 20:51:26 +00001708 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1709 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1710 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1711 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001712 if (delta_type != 0)
1713 {
1714 box.left+=previous_box.left;
1715 box.right+=previous_box.right;
1716 box.top+=previous_box.top;
1717 box.bottom+=previous_box.bottom;
1718 }
glennrp0fe50b42010-11-16 03:52:51 +00001719
cristy3ed852e2009-09-05 21:47:34 +00001720 return(box);
1721}
1722
1723static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1724 unsigned char *p)
1725{
1726 MngPair
1727 pair;
1728 /*
cristybb503372010-05-27 20:51:26 +00001729 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001730 */
cristy8182b072010-05-30 20:10:53 +00001731 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1732 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001733
cristy3ed852e2009-09-05 21:47:34 +00001734 if (delta_type != 0)
1735 {
1736 pair.a+=previous_pair.a;
1737 pair.b+=previous_pair.b;
1738 }
glennrp0fe50b42010-11-16 03:52:51 +00001739
cristy3ed852e2009-09-05 21:47:34 +00001740 return(pair);
1741}
1742
cristy8182b072010-05-30 20:10:53 +00001743static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001744{
cristy8182b072010-05-30 20:10:53 +00001745 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001746}
1747
glennrpcf002022011-01-30 02:38:15 +00001748static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001749{
1750 Image
1751 *image;
1752
1753 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001754
cristy3ed852e2009-09-05 21:47:34 +00001755 if (image->debug != MagickFalse)
1756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1757 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001758
cristy3ed852e2009-09-05 21:47:34 +00001759 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1760 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001761
glennrpe4017e32011-01-08 17:16:09 +00001762#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001763 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1764 * are building with libpng-1.4.x and can be ignored.
1765 */
cristy3ed852e2009-09-05 21:47:34 +00001766 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001767#else
1768 png_longjmp(ping,1);
1769#endif
cristy3ed852e2009-09-05 21:47:34 +00001770}
1771
glennrpcf002022011-01-30 02:38:15 +00001772static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001773{
1774 Image
1775 *image;
1776
1777 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1778 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001779
cristy3ed852e2009-09-05 21:47:34 +00001780 image=(Image *) png_get_error_ptr(ping);
1781 if (image->debug != MagickFalse)
1782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001783 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001784
cristy3ed852e2009-09-05 21:47:34 +00001785 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1786 message,"`%s'",image->filename);
1787}
1788
1789#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001790static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001791{
1792#if (PNG_LIBPNG_VER < 10011)
1793 png_voidp
1794 ret;
1795
1796 png_ptr=png_ptr;
1797 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001798
cristy3ed852e2009-09-05 21:47:34 +00001799 if (ret == NULL)
1800 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001801
cristy3ed852e2009-09-05 21:47:34 +00001802 return(ret);
1803#else
1804 png_ptr=png_ptr;
1805 return((png_voidp) AcquireMagickMemory((size_t) size));
1806#endif
1807}
1808
1809/*
1810 Free a pointer. It is removed from the list at the same time.
1811*/
glennrpcf002022011-01-30 02:38:15 +00001812static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001813{
1814 png_ptr=png_ptr;
1815 ptr=RelinquishMagickMemory(ptr);
1816 return((png_free_ptr) NULL);
1817}
1818#endif
1819
1820#if defined(__cplusplus) || defined(c_plusplus)
1821}
1822#endif
1823
1824static int
glennrpcf002022011-01-30 02:38:15 +00001825Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001826 png_textp text,int ii)
1827{
cristybb503372010-05-27 20:51:26 +00001828 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001829 i;
1830
1831 register unsigned char
1832 *dp;
1833
1834 register png_charp
1835 sp;
1836
1837 png_uint_32
1838 length,
1839 nibbles;
1840
1841 StringInfo
1842 *profile;
1843
glennrp0c3e06b2010-11-19 13:45:02 +00001844 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001845 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1846 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1847 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1848 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1849 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1850 13,14,15};
1851
1852 sp=text[ii].text+1;
1853 /* look for newline */
1854 while (*sp != '\n')
1855 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001856
cristy3ed852e2009-09-05 21:47:34 +00001857 /* look for length */
1858 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1859 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001860
cristyf2f27272009-12-17 14:48:46 +00001861 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001862
glennrp97f90e22011-02-22 05:47:58 +00001863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1864 " length: %lu",(unsigned long) length);
1865
cristy3ed852e2009-09-05 21:47:34 +00001866 while (*sp != ' ' && *sp != '\n')
1867 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001868
cristy3ed852e2009-09-05 21:47:34 +00001869 /* allocate space */
1870 if (length == 0)
1871 {
1872 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1873 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1874 return(MagickFalse);
1875 }
glennrp0fe50b42010-11-16 03:52:51 +00001876
cristy3ed852e2009-09-05 21:47:34 +00001877 profile=AcquireStringInfo(length);
glennrp0fe50b42010-11-16 03:52:51 +00001878
cristy3ed852e2009-09-05 21:47:34 +00001879 if (profile == (StringInfo *) NULL)
1880 {
1881 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1882 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1883 "unable to copy profile");
1884 return(MagickFalse);
1885 }
glennrp0fe50b42010-11-16 03:52:51 +00001886
cristy3ed852e2009-09-05 21:47:34 +00001887 /* copy profile, skipping white space and column 1 "=" signs */
1888 dp=GetStringInfoDatum(profile);
1889 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001890
cristybb503372010-05-27 20:51:26 +00001891 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001892 {
1893 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1894 {
1895 if (*sp == '\0')
1896 {
1897 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1898 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1899 profile=DestroyStringInfo(profile);
1900 return(MagickFalse);
1901 }
1902 sp++;
1903 }
glennrp0fe50b42010-11-16 03:52:51 +00001904
cristy3ed852e2009-09-05 21:47:34 +00001905 if (i%2 == 0)
1906 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001907
cristy3ed852e2009-09-05 21:47:34 +00001908 else
1909 (*dp++)+=unhex[(int) *sp++];
1910 }
1911 /*
1912 We have already read "Raw profile type.
1913 */
1914 (void) SetImageProfile(image,&text[ii].key[17],profile);
1915 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001916
cristy3ed852e2009-09-05 21:47:34 +00001917 if (image_info->verbose)
1918 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001919
cristy3ed852e2009-09-05 21:47:34 +00001920 return MagickTrue;
1921}
1922
1923#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1924static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1925{
1926 Image
1927 *image;
1928
1929
1930 /* The unknown chunk structure contains the chunk data:
1931 png_byte name[5];
1932 png_byte *data;
1933 png_size_t size;
1934
1935 Note that libpng has already taken care of the CRC handling.
1936 */
1937
1938
1939 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1940 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1941 return(0); /* Did not recognize */
1942
1943 /* recognized vpAg */
1944
1945 if (chunk->size != 9)
1946 return(-1); /* Error return */
1947
1948 if (chunk->data[8] != 0)
1949 return(0); /* ImageMagick requires pixel units */
1950
1951 image=(Image *) png_get_user_chunk_ptr(ping);
1952
cristybb503372010-05-27 20:51:26 +00001953 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001954 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001955
cristybb503372010-05-27 20:51:26 +00001956 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001957 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1958
1959 /* Return one of the following: */
1960 /* return(-n); chunk had an error */
1961 /* return(0); did not recognize */
1962 /* return(n); success */
1963
1964 return(1);
1965
1966}
1967#endif
1968
1969/*
1970%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1971% %
1972% %
1973% %
1974% R e a d O n e P N G I m a g e %
1975% %
1976% %
1977% %
1978%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1979%
1980% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1981% (minus the 8-byte signature) and returns it. It allocates the memory
1982% necessary for the new Image structure and returns a pointer to the new
1983% image.
1984%
1985% The format of the ReadOnePNGImage method is:
1986%
1987% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1988% ExceptionInfo *exception)
1989%
1990% A description of each parameter follows:
1991%
1992% o mng_info: Specifies a pointer to a MngInfo structure.
1993%
1994% o image_info: the image info.
1995%
1996% o exception: return any errors or warnings in this structure.
1997%
1998*/
1999static Image *ReadOnePNGImage(MngInfo *mng_info,
2000 const ImageInfo *image_info, ExceptionInfo *exception)
2001{
2002 /* Read one PNG image */
2003
glennrpcc95c3f2011-04-18 16:46:48 +00002004 /* To do: Read the tIME chunk into the date:modify property */
2005 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2006
cristy3ed852e2009-09-05 21:47:34 +00002007 Image
2008 *image;
2009
2010 int
glennrp4eb39312011-03-30 21:34:55 +00002011 intent,
glennrpcb395ac2011-03-30 19:50:23 +00002012 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002013 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002014 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002015 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00002016 pass,
2017 ping_bit_depth,
2018 ping_color_type,
2019 ping_interlace_method,
2020 ping_compression_method,
2021 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002022 ping_num_trans,
2023 unit_type;
2024
2025 double
2026 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002027
glennrpa6a06632011-01-19 15:15:34 +00002028 LongPixelPacket
2029 transparent_color;
2030
cristy3ed852e2009-09-05 21:47:34 +00002031 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002032 logging,
cristy3ed852e2009-09-05 21:47:34 +00002033 status;
2034
glennrpfaa852b2010-03-30 12:17:00 +00002035 png_bytep
2036 ping_trans_alpha;
2037
2038 png_color_16p
2039 ping_background,
2040 ping_trans_color;
2041
cristy3ed852e2009-09-05 21:47:34 +00002042 png_info
2043 *end_info,
2044 *ping_info;
2045
2046 png_struct
2047 *ping;
2048
2049 png_textp
2050 text;
2051
glennrpfaa852b2010-03-30 12:17:00 +00002052 png_uint_32
2053 ping_height,
2054 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002055 ping_rowbytes,
2056 x_resolution,
2057 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002058
cristy3ed852e2009-09-05 21:47:34 +00002059 QuantumInfo
2060 *quantum_info;
2061
2062 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002063 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002064
cristybb503372010-05-27 20:51:26 +00002065 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002066 y;
2067
2068 register unsigned char
2069 *p;
2070
2071 register IndexPacket
cristy5c6f7892010-05-05 22:53:29 +00002072 *indexes;
cristy3ed852e2009-09-05 21:47:34 +00002073
cristybb503372010-05-27 20:51:26 +00002074 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002075 i,
2076 x;
2077
2078 register PixelPacket
2079 *q;
2080
2081 size_t
glennrp39992b42010-11-14 00:03:43 +00002082 length,
cristy3ed852e2009-09-05 21:47:34 +00002083 row_offset;
2084
cristyeb3b22a2011-03-31 20:16:11 +00002085 ssize_t
2086 j;
2087
cristy3ed852e2009-09-05 21:47:34 +00002088#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2089 png_byte unused_chunks[]=
2090 {
2091 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2092 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2093 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2094 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2095 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2096 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2097 };
2098#endif
2099
2100 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002101 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002102
2103#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002104 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002105#endif
2106
glennrp25c1e2b2010-03-25 01:39:56 +00002107#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002108 if (image_info->verbose)
2109 printf("Your PNG library (libpng-%s) is rather old.\n",
2110 PNG_LIBPNG_VER_STRING);
2111#endif
2112
glennrp61b4c952009-11-10 20:40:41 +00002113#if (PNG_LIBPNG_VER >= 10400)
2114# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2115 if (image_info->verbose)
2116 {
2117 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2118 PNG_LIBPNG_VER_STRING);
2119 printf("Please update it.\n");
2120 }
2121# endif
2122#endif
2123
2124
cristyed552522009-10-16 14:04:35 +00002125 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002126 image=mng_info->image;
2127
glennrpa6a06632011-01-19 15:15:34 +00002128 if (logging != MagickFalse)
2129 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2130 " image->matte=%d",(int) image->matte);
2131
glennrp0e319732011-01-25 21:53:13 +00002132 /* Set to an out-of-range color unless tRNS chunk is present */
2133 transparent_color.red=65537;
2134 transparent_color.green=65537;
2135 transparent_color.blue=65537;
2136 transparent_color.opacity=65537;
2137
glennrpcb395ac2011-03-30 19:50:23 +00002138 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002139 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002140 num_raw_profiles = 0;
2141
cristy3ed852e2009-09-05 21:47:34 +00002142 /*
2143 Allocate the PNG structures
2144 */
2145#ifdef PNG_USER_MEM_SUPPORTED
2146 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00002147 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2148 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002149#else
2150 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00002151 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002152#endif
2153 if (ping == (png_struct *) NULL)
2154 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002155
cristy3ed852e2009-09-05 21:47:34 +00002156 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002157
cristy3ed852e2009-09-05 21:47:34 +00002158 if (ping_info == (png_info *) NULL)
2159 {
2160 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2161 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2162 }
glennrp0fe50b42010-11-16 03:52:51 +00002163
cristy3ed852e2009-09-05 21:47:34 +00002164 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002165
cristy3ed852e2009-09-05 21:47:34 +00002166 if (end_info == (png_info *) NULL)
2167 {
2168 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2169 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2170 }
glennrp0fe50b42010-11-16 03:52:51 +00002171
glennrpcf002022011-01-30 02:38:15 +00002172 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002173
glennrpfaa852b2010-03-30 12:17:00 +00002174 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002175 {
2176 /*
2177 PNG image is corrupt.
2178 */
2179 png_destroy_read_struct(&ping,&ping_info,&end_info);
2180#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002181 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002182#endif
2183 if (logging != MagickFalse)
2184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2185 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002186
cristy3ed852e2009-09-05 21:47:34 +00002187 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002188 {
2189 InheritException(exception,&image->exception);
2190 image->columns=0;
2191 }
glennrp0fe50b42010-11-16 03:52:51 +00002192
cristy3ed852e2009-09-05 21:47:34 +00002193 return(GetFirstImageInList(image));
2194 }
2195 /*
2196 Prepare PNG for reading.
2197 */
glennrpfaa852b2010-03-30 12:17:00 +00002198
cristy3ed852e2009-09-05 21:47:34 +00002199 mng_info->image_found++;
2200 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002201
cristy3ed852e2009-09-05 21:47:34 +00002202 if (LocaleCompare(image_info->magick,"MNG") == 0)
2203 {
2204#if defined(PNG_MNG_FEATURES_SUPPORTED)
2205 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2206 png_set_read_fn(ping,image,png_get_data);
2207#else
2208#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2209 png_permit_empty_plte(ping,MagickTrue);
2210 png_set_read_fn(ping,image,png_get_data);
2211#else
2212 mng_info->image=image;
2213 mng_info->bytes_in_read_buffer=0;
2214 mng_info->found_empty_plte=MagickFalse;
2215 mng_info->have_saved_bkgd_index=MagickFalse;
2216 png_set_read_fn(ping,mng_info,mng_get_data);
2217#endif
2218#endif
2219 }
glennrp0fe50b42010-11-16 03:52:51 +00002220
cristy3ed852e2009-09-05 21:47:34 +00002221 else
2222 png_set_read_fn(ping,image,png_get_data);
2223
2224#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2225 /* Ignore unused chunks and all unknown chunks except for vpAg */
2226 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2227 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2228 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2229 (int)sizeof(unused_chunks)/5);
2230 /* Callback for other unknown chunks */
2231 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2232#endif
2233
glennrp991e92a2010-01-28 03:09:00 +00002234#if (PNG_LIBPNG_VER < 10400)
2235# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2236 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002237 /* Disable thread-unsafe features of pnggccrd */
2238 if (png_access_version_number() >= 10200)
2239 {
2240 png_uint_32 mmx_disable_mask=0;
2241 png_uint_32 asm_flags;
2242
2243 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2244 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2245 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2246 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2247 asm_flags=png_get_asm_flags(ping);
2248 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2249 }
glennrp991e92a2010-01-28 03:09:00 +00002250# endif
cristy3ed852e2009-09-05 21:47:34 +00002251#endif
2252
2253 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002254
2255 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2256 &ping_bit_depth,&ping_color_type,
2257 &ping_interlace_method,&ping_compression_method,
2258 &ping_filter_method);
2259
2260 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2261 &ping_trans_color);
2262
2263 (void) png_get_bKGD(ping, ping_info, &ping_background);
2264
2265 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002266 {
glennrpfaa852b2010-03-30 12:17:00 +00002267 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2268 {
2269 png_set_packing(ping);
2270 ping_bit_depth = 8;
2271 }
cristy3ed852e2009-09-05 21:47:34 +00002272 }
glennrpfaa852b2010-03-30 12:17:00 +00002273
2274 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002275 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002276 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002277 if (logging != MagickFalse)
2278 {
2279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002280 " PNG width: %.20g, height: %.20g",
2281 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002282
cristy3ed852e2009-09-05 21:47:34 +00002283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2284 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002285 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002286
cristy3ed852e2009-09-05 21:47:34 +00002287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2288 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002289 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002290
cristy3ed852e2009-09-05 21:47:34 +00002291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2292 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002293 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002294 }
2295
glennrpfaa852b2010-03-30 12:17:00 +00002296#ifdef PNG_READ_iCCP_SUPPORTED
2297 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002298 {
2299 int
2300 compression;
2301
glennrpe4017e32011-01-08 17:16:09 +00002302#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002303 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002304 info;
2305#else
2306 png_bytep
2307 info;
2308#endif
2309
2310 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002311 name;
2312
2313 png_uint_32
2314 profile_length;
2315
2316 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2317 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002318
cristy3ed852e2009-09-05 21:47:34 +00002319 if (profile_length != 0)
2320 {
2321 StringInfo
2322 *profile;
2323
2324 if (logging != MagickFalse)
2325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2326 " Reading PNG iCCP chunk.");
2327 profile=AcquireStringInfo(profile_length);
2328 SetStringInfoDatum(profile,(const unsigned char *) info);
2329 (void) SetImageProfile(image,"icc",profile);
2330 profile=DestroyStringInfo(profile);
2331 }
2332 }
2333#endif
2334#if defined(PNG_READ_sRGB_SUPPORTED)
2335 {
cristy3ed852e2009-09-05 21:47:34 +00002336 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002337 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2338 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002339
cristy3ed852e2009-09-05 21:47:34 +00002340 if (png_get_sRGB(ping,ping_info,&intent))
2341 {
glennrpcf002022011-01-30 02:38:15 +00002342 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2343 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002344
cristy3ed852e2009-09-05 21:47:34 +00002345 if (logging != MagickFalse)
2346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002347 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002348 }
2349 }
2350#endif
2351 {
glennrpfaa852b2010-03-30 12:17:00 +00002352 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2353 if (mng_info->have_global_gama)
2354 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002355
cristy3ed852e2009-09-05 21:47:34 +00002356 if (png_get_gAMA(ping,ping_info,&file_gamma))
2357 {
2358 image->gamma=(float) file_gamma;
2359 if (logging != MagickFalse)
2360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2361 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2362 }
2363 }
glennrpfaa852b2010-03-30 12:17:00 +00002364 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2365 {
2366 if (mng_info->have_global_chrm != MagickFalse)
2367 {
2368 (void) png_set_cHRM(ping,ping_info,
2369 mng_info->global_chrm.white_point.x,
2370 mng_info->global_chrm.white_point.y,
2371 mng_info->global_chrm.red_primary.x,
2372 mng_info->global_chrm.red_primary.y,
2373 mng_info->global_chrm.green_primary.x,
2374 mng_info->global_chrm.green_primary.y,
2375 mng_info->global_chrm.blue_primary.x,
2376 mng_info->global_chrm.blue_primary.y);
2377 }
2378 }
glennrp0fe50b42010-11-16 03:52:51 +00002379
glennrpfaa852b2010-03-30 12:17:00 +00002380 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002381 {
2382 (void) png_get_cHRM(ping,ping_info,
2383 &image->chromaticity.white_point.x,
2384 &image->chromaticity.white_point.y,
2385 &image->chromaticity.red_primary.x,
2386 &image->chromaticity.red_primary.y,
2387 &image->chromaticity.green_primary.x,
2388 &image->chromaticity.green_primary.y,
2389 &image->chromaticity.blue_primary.x,
2390 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002391
cristy3ed852e2009-09-05 21:47:34 +00002392 if (logging != MagickFalse)
2393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2394 " Reading PNG cHRM chunk.");
2395 }
glennrp0fe50b42010-11-16 03:52:51 +00002396
glennrpe610a072010-08-05 17:08:46 +00002397 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002398 {
glennrpe610a072010-08-05 17:08:46 +00002399 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002400 Magick_RenderingIntent_to_PNG_RenderingIntent
2401 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002402 png_set_gAMA(ping,ping_info,0.45455f);
2403 png_set_cHRM(ping,ping_info,
2404 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2405 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002406 }
cristy3ed852e2009-09-05 21:47:34 +00002407#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002408 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002409 {
cristy905ef802011-02-23 00:29:18 +00002410 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2411 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002412
cristy3ed852e2009-09-05 21:47:34 +00002413 if (logging != MagickFalse)
2414 if (image->page.x || image->page.y)
2415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002416 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2417 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002418 }
2419#endif
2420#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002421 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2422 {
2423 if (mng_info->have_global_phys)
2424 {
2425 png_set_pHYs(ping,ping_info,
2426 mng_info->global_x_pixels_per_unit,
2427 mng_info->global_y_pixels_per_unit,
2428 mng_info->global_phys_unit_type);
2429 }
2430 }
2431
2432 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002433 {
cristy3ed852e2009-09-05 21:47:34 +00002434 /*
2435 Set image resolution.
2436 */
2437 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002438 &unit_type);
2439 image->x_resolution=(double) x_resolution;
2440 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002441
cristy3ed852e2009-09-05 21:47:34 +00002442 if (unit_type == PNG_RESOLUTION_METER)
2443 {
2444 image->units=PixelsPerCentimeterResolution;
2445 image->x_resolution=(double) x_resolution/100.0;
2446 image->y_resolution=(double) y_resolution/100.0;
2447 }
glennrp0fe50b42010-11-16 03:52:51 +00002448
cristy3ed852e2009-09-05 21:47:34 +00002449 if (logging != MagickFalse)
2450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002451 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2452 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002453 }
cristy3ed852e2009-09-05 21:47:34 +00002454#endif
glennrp823b55c2011-03-14 18:46:46 +00002455
glennrpfaa852b2010-03-30 12:17:00 +00002456 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002457 {
2458 int
2459 number_colors;
2460
2461 png_colorp
2462 palette;
2463
2464 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002465
cristy3ed852e2009-09-05 21:47:34 +00002466 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002467 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002468 {
2469 if (mng_info->global_plte_length)
2470 {
2471 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2472 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002473
glennrpfaa852b2010-03-30 12:17:00 +00002474 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002475 if (mng_info->global_trns_length)
2476 {
2477 if (mng_info->global_trns_length >
2478 mng_info->global_plte_length)
2479 (void) ThrowMagickException(&image->exception,
2480 GetMagickModule(),CoderError,
2481 "global tRNS has more entries than global PLTE",
2482 "`%s'",image_info->filename);
2483 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2484 (int) mng_info->global_trns_length,NULL);
2485 }
glennrpbfd9e612011-04-22 14:02:20 +00002486#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002487 if (
2488#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2489 mng_info->have_saved_bkgd_index ||
2490#endif
glennrpfaa852b2010-03-30 12:17:00 +00002491 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002492 {
2493 png_color_16
2494 background;
2495
2496#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2497 if (mng_info->have_saved_bkgd_index)
2498 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002499#endif
glennrpfaa852b2010-03-30 12:17:00 +00002500 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2501 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002502
cristy3ed852e2009-09-05 21:47:34 +00002503 background.red=(png_uint_16)
2504 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002505
cristy3ed852e2009-09-05 21:47:34 +00002506 background.green=(png_uint_16)
2507 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002508
cristy3ed852e2009-09-05 21:47:34 +00002509 background.blue=(png_uint_16)
2510 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002511
glennrpc6c391a2011-04-27 02:23:56 +00002512 background.gray=(png_uint_16)
2513 mng_info->global_plte[background.index].green;
2514
cristy3ed852e2009-09-05 21:47:34 +00002515 png_set_bKGD(ping,ping_info,&background);
2516 }
2517#endif
2518 }
2519 else
2520 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2521 CoderError,"No global PLTE in file","`%s'",
2522 image_info->filename);
2523 }
2524 }
2525
glennrpbfd9e612011-04-22 14:02:20 +00002526#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002527 if (mng_info->have_global_bkgd &&
2528 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002529 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002530
glennrpfaa852b2010-03-30 12:17:00 +00002531 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002532 {
glennrpbfd9e612011-04-22 14:02:20 +00002533 unsigned int
2534 bkgd_scale;
2535
cristy3ed852e2009-09-05 21:47:34 +00002536 /*
2537 Set image background color.
2538 */
2539 if (logging != MagickFalse)
2540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2541 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002542
glennrpbfd9e612011-04-22 14:02:20 +00002543 /* Scale background components to 16-bit, then scale
2544 * to quantum depth
2545 */
2546 if (logging != MagickFalse)
2547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2548 " raw ping_background=(%d,%d,%d).",ping_background->red,
2549 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002550
glennrpbfd9e612011-04-22 14:02:20 +00002551 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002552
glennrpbfd9e612011-04-22 14:02:20 +00002553 if (ping_bit_depth == 1)
2554 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002555
glennrpbfd9e612011-04-22 14:02:20 +00002556 else if (ping_bit_depth == 2)
2557 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002558
glennrpbfd9e612011-04-22 14:02:20 +00002559 else if (ping_bit_depth == 4)
2560 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002561
glennrpbfd9e612011-04-22 14:02:20 +00002562 if (ping_bit_depth <= 8)
2563 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002564
glennrpbfd9e612011-04-22 14:02:20 +00002565 ping_background->red *= bkgd_scale;
2566 ping_background->green *= bkgd_scale;
2567 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002568
glennrpbfd9e612011-04-22 14:02:20 +00002569 if (logging != MagickFalse)
2570 {
glennrp2cbb4482010-06-02 04:37:24 +00002571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2572 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002573
glennrp2cbb4482010-06-02 04:37:24 +00002574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2575 " ping_background=(%d,%d,%d).",ping_background->red,
2576 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002577 }
glennrp2cbb4482010-06-02 04:37:24 +00002578
glennrpbfd9e612011-04-22 14:02:20 +00002579 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002580 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002581
glennrpbfd9e612011-04-22 14:02:20 +00002582 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002583 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002584
glennrpbfd9e612011-04-22 14:02:20 +00002585 image->background_color.blue=
2586 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002587
glennrpbfd9e612011-04-22 14:02:20 +00002588 image->background_color.opacity=OpaqueOpacity;
glennrp2cbb4482010-06-02 04:37:24 +00002589
glennrpbfd9e612011-04-22 14:02:20 +00002590 if (logging != MagickFalse)
2591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2592 " image->background_color=(%.20g,%.20g,%.20g).",
2593 (double) image->background_color.red,
2594 (double) image->background_color.green,
2595 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002596 }
glennrpbfd9e612011-04-22 14:02:20 +00002597#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002598
glennrpfaa852b2010-03-30 12:17:00 +00002599 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002600 {
2601 /*
glennrpa6a06632011-01-19 15:15:34 +00002602 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002603 */
2604 int
2605 max_sample;
2606
cristy35ef8242010-06-03 16:24:13 +00002607 size_t
2608 one=1;
2609
cristy3ed852e2009-09-05 21:47:34 +00002610 if (logging != MagickFalse)
2611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2612 " Reading PNG tRNS chunk.");
2613
cristyf9cca6a2010-06-04 23:49:28 +00002614 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002615
glennrpfaa852b2010-03-30 12:17:00 +00002616 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2617 (int)ping_trans_color->gray > max_sample) ||
2618 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2619 ((int)ping_trans_color->red > max_sample ||
2620 (int)ping_trans_color->green > max_sample ||
2621 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002622 {
2623 if (logging != MagickFalse)
2624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2625 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002626 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002627 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002628 image->matte=MagickFalse;
2629 }
2630 else
2631 {
glennrpa6a06632011-01-19 15:15:34 +00002632 int
2633 scale_to_short;
2634
2635 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2636
2637 /* Scale transparent_color to short */
2638 transparent_color.red= scale_to_short*ping_trans_color->red;
2639 transparent_color.green= scale_to_short*ping_trans_color->green;
2640 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2641 transparent_color.opacity= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002642
glennrpfaa852b2010-03-30 12:17:00 +00002643 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002644 {
glennrp0f111982010-07-07 20:18:33 +00002645 if (logging != MagickFalse)
2646 {
2647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2648 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002649
glennrp0f111982010-07-07 20:18:33 +00002650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2651 " scaled graylevel is %d.",transparent_color.opacity);
2652 }
cristy3ed852e2009-09-05 21:47:34 +00002653 transparent_color.red=transparent_color.opacity;
2654 transparent_color.green=transparent_color.opacity;
2655 transparent_color.blue=transparent_color.opacity;
2656 }
2657 }
2658 }
2659#if defined(PNG_READ_sBIT_SUPPORTED)
2660 if (mng_info->have_global_sbit)
2661 {
glennrpfaa852b2010-03-30 12:17:00 +00002662 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002663 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2664 }
2665#endif
2666 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002667
cristy3ed852e2009-09-05 21:47:34 +00002668 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002669
2670 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2671
cristy3ed852e2009-09-05 21:47:34 +00002672 /*
2673 Initialize image structure.
2674 */
2675 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002676 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002677 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002678 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002679 if (mng_info->mng_type == 0)
2680 {
glennrpfaa852b2010-03-30 12:17:00 +00002681 mng_info->mng_width=ping_width;
2682 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002683 mng_info->frame=mng_info->image_box;
2684 mng_info->clip=mng_info->image_box;
2685 }
glennrp0fe50b42010-11-16 03:52:51 +00002686
cristy3ed852e2009-09-05 21:47:34 +00002687 else
2688 {
2689 image->page.y=mng_info->y_off[mng_info->object_id];
2690 }
glennrp0fe50b42010-11-16 03:52:51 +00002691
cristy3ed852e2009-09-05 21:47:34 +00002692 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002693 image->columns=ping_width;
2694 image->rows=ping_height;
2695 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002696 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002697 {
cristybefe4d22010-06-07 01:18:58 +00002698 size_t
2699 one;
2700
cristy3ed852e2009-09-05 21:47:34 +00002701 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002702 one=1;
2703 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002704#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2705 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002706 image->colors=256;
2707#else
2708 if (image->colors > 65536L)
2709 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002710#endif
glennrpfaa852b2010-03-30 12:17:00 +00002711 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002712 {
2713 int
2714 number_colors;
2715
2716 png_colorp
2717 palette;
2718
2719 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002720 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002721
cristy3ed852e2009-09-05 21:47:34 +00002722 if (logging != MagickFalse)
2723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2724 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2725 }
2726 }
2727
2728 if (image->storage_class == PseudoClass)
2729 {
2730 /*
2731 Initialize image colormap.
2732 */
2733 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2734 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002735
glennrpfaa852b2010-03-30 12:17:00 +00002736 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002737 {
2738 int
2739 number_colors;
2740
2741 png_colorp
2742 palette;
2743
2744 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002745
glennrp6af6cf12011-04-22 13:05:16 +00002746 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002747 {
2748 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2749 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2750 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2751 }
glennrp6af6cf12011-04-22 13:05:16 +00002752
glennrp67b9c1a2011-04-22 18:47:36 +00002753 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002754 {
2755 image->colormap[i].red=0;
2756 image->colormap[i].green=0;
2757 image->colormap[i].blue=0;
2758 }
cristy3ed852e2009-09-05 21:47:34 +00002759 }
glennrp0fe50b42010-11-16 03:52:51 +00002760
cristy3ed852e2009-09-05 21:47:34 +00002761 else
2762 {
cristybb503372010-05-27 20:51:26 +00002763 size_t
cristy3ed852e2009-09-05 21:47:34 +00002764 scale;
2765
glennrpfaa852b2010-03-30 12:17:00 +00002766 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002767
cristy3ed852e2009-09-05 21:47:34 +00002768 if (scale < 1)
2769 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002770
cristybb503372010-05-27 20:51:26 +00002771 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002772 {
2773 image->colormap[i].red=(Quantum) (i*scale);
2774 image->colormap[i].green=(Quantum) (i*scale);
2775 image->colormap[i].blue=(Quantum) (i*scale);
2776 }
2777 }
2778 }
glennrp147bc912011-03-30 18:47:21 +00002779
glennrpcb395ac2011-03-30 19:50:23 +00002780 /* Set some properties for reporting by "identify" */
2781 {
glennrp147bc912011-03-30 18:47:21 +00002782 char
2783 msg[MaxTextExtent];
2784
2785 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2786 ping_interlace_method in value */
2787
cristy3b6fd2e2011-05-20 12:53:50 +00002788 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002789 "%d, %d",(int) ping_width, (int) ping_height);
2790 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002791
cristy3b6fd2e2011-05-20 12:53:50 +00002792 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
glennrp147bc912011-03-30 18:47:21 +00002793 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2794
cristy3b6fd2e2011-05-20 12:53:50 +00002795 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
glennrp147bc912011-03-30 18:47:21 +00002796 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2797
cristy3b6fd2e2011-05-20 12:53:50 +00002798 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002799 (int) ping_interlace_method);
2800 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002801 }
glennrp147bc912011-03-30 18:47:21 +00002802
cristy3ed852e2009-09-05 21:47:34 +00002803 /*
2804 Read image scanlines.
2805 */
2806 if (image->delay != 0)
2807 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002808
glennrp0ca69b12010-07-26 01:57:52 +00002809 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002810 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2811 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002812 {
2813 if (logging != MagickFalse)
2814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002815 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002816 mng_info->scenes_found-1);
2817 png_destroy_read_struct(&ping,&ping_info,&end_info);
2818#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002819 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002820#endif
2821 if (logging != MagickFalse)
2822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2823 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002824
cristy3ed852e2009-09-05 21:47:34 +00002825 return(image);
2826 }
glennrp0fe50b42010-11-16 03:52:51 +00002827
cristy3ed852e2009-09-05 21:47:34 +00002828 if (logging != MagickFalse)
2829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2830 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002831
cristy3ed852e2009-09-05 21:47:34 +00002832 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002833 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2834 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002835
cristy3ed852e2009-09-05 21:47:34 +00002836 else
glennrpcf002022011-01-30 02:38:15 +00002837 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2838 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002839
glennrpcf002022011-01-30 02:38:15 +00002840 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002841 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002842
cristy3ed852e2009-09-05 21:47:34 +00002843 if (logging != MagickFalse)
2844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2845 " Converting PNG pixels to pixel packets");
2846 /*
2847 Convert PNG pixels to pixel packets.
2848 */
glennrpfaa852b2010-03-30 12:17:00 +00002849 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002850 {
2851 /*
2852 PNG image is corrupt.
2853 */
2854 png_destroy_read_struct(&ping,&ping_info,&end_info);
2855#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002856 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002857#endif
2858 if (quantum_info != (QuantumInfo *) NULL)
2859 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002860
glennrpcf002022011-01-30 02:38:15 +00002861 if (ping_pixels != (unsigned char *) NULL)
2862 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002863
cristy3ed852e2009-09-05 21:47:34 +00002864 if (logging != MagickFalse)
2865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2866 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002867
cristy3ed852e2009-09-05 21:47:34 +00002868 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002869 {
2870 InheritException(exception,&image->exception);
2871 image->columns=0;
2872 }
glennrp0fe50b42010-11-16 03:52:51 +00002873
cristy3ed852e2009-09-05 21:47:34 +00002874 return(GetFirstImageInList(image));
2875 }
glennrp0fe50b42010-11-16 03:52:51 +00002876
cristyed552522009-10-16 14:04:35 +00002877 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002878
cristyed552522009-10-16 14:04:35 +00002879 if (quantum_info == (QuantumInfo *) NULL)
2880 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002881
glennrpc8cbc5d2011-01-01 00:12:34 +00002882 {
2883
2884 MagickBooleanType
2885 found_transparent_pixel;
2886
2887 found_transparent_pixel=MagickFalse;
2888
cristy3ed852e2009-09-05 21:47:34 +00002889 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002890 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002891 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002892 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002893 /*
2894 Convert image to DirectClass pixel packets.
2895 */
glennrp67b9c1a2011-04-22 18:47:36 +00002896#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2897 int
2898 depth;
2899
2900 depth=(ssize_t) ping_bit_depth;
2901#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002902 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2903 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2904 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2905 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002906
glennrpc8cbc5d2011-01-01 00:12:34 +00002907 for (y=0; y < (ssize_t) image->rows; y++)
2908 {
2909 if (num_passes > 1)
2910 row_offset=ping_rowbytes*y;
2911
2912 else
2913 row_offset=0;
2914
glennrpcf002022011-01-30 02:38:15 +00002915 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002916 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2917
2918 if (q == (PixelPacket *) NULL)
2919 break;
2920
glennrpc8cbc5d2011-01-01 00:12:34 +00002921 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2922 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002923 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002924
2925 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2926 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002927 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002928
2929 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2930 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002931 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002932
2933 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2934 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002935 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002936
2937 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2938 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002939 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002940
glennrpc8cbc5d2011-01-01 00:12:34 +00002941 if (found_transparent_pixel == MagickFalse)
2942 {
2943 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002944 if (y== 0 && logging != MagickFalse)
2945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2946 " Looking for cheap transparent pixel");
2947
glennrpc8cbc5d2011-01-01 00:12:34 +00002948 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2949 {
glennrp5aa37f62011-01-02 03:07:57 +00002950 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2951 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
glennrp8b698592011-04-26 03:38:21 +00002952 (GetOpacityPixelComponent(q) != OpaqueOpacity))
glennrpc8cbc5d2011-01-01 00:12:34 +00002953 {
glennrpa6a06632011-01-19 15:15:34 +00002954 if (logging != MagickFalse)
2955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2956 " ...got one.");
2957
glennrpc8cbc5d2011-01-01 00:12:34 +00002958 found_transparent_pixel = MagickTrue;
2959 break;
2960 }
glennrp4f25bd02011-01-01 18:51:28 +00002961 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2962 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
glennrp8b698592011-04-26 03:38:21 +00002963 (ScaleQuantumToShort(GetRedPixelComponent(q))
2964 == transparent_color.red &&
2965 ScaleQuantumToShort(GetGreenPixelComponent(q))
2966 == transparent_color.green &&
2967 ScaleQuantumToShort(GetBluePixelComponent(q))
2968 == transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002969 {
glennrpa6a06632011-01-19 15:15:34 +00002970 if (logging != MagickFalse)
2971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2972 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002973 found_transparent_pixel = MagickTrue;
2974 break;
2975 }
glennrpc8cbc5d2011-01-01 00:12:34 +00002976 q++;
2977 }
2978 }
2979
2980 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2981 {
2982 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2983 image->rows);
2984
2985 if (status == MagickFalse)
2986 break;
2987 }
2988 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2989 break;
2990 }
2991
2992 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2993 {
2994 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002995 if (status == MagickFalse)
2996 break;
2997 }
cristy3ed852e2009-09-05 21:47:34 +00002998 }
cristy3ed852e2009-09-05 21:47:34 +00002999 }
glennrp0fe50b42010-11-16 03:52:51 +00003000
cristy3ed852e2009-09-05 21:47:34 +00003001 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003002
cristy3ed852e2009-09-05 21:47:34 +00003003 for (pass=0; pass < num_passes; pass++)
3004 {
3005 Quantum
3006 *quantum_scanline;
3007
3008 register Quantum
3009 *r;
3010
3011 /*
3012 Convert grayscale image to PseudoClass pixel packets.
3013 */
glennrpfaa852b2010-03-30 12:17:00 +00003014 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00003015 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003016
cristy3ed852e2009-09-05 21:47:34 +00003017 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3018 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003019
cristy3ed852e2009-09-05 21:47:34 +00003020 if (quantum_scanline == (Quantum *) NULL)
3021 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003022
cristybb503372010-05-27 20:51:26 +00003023 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003024 {
3025 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003026 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003027
cristy3ed852e2009-09-05 21:47:34 +00003028 else
3029 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003030
glennrpcf002022011-01-30 02:38:15 +00003031 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003032 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003033
cristy3ed852e2009-09-05 21:47:34 +00003034 if (q == (PixelPacket *) NULL)
3035 break;
glennrp0fe50b42010-11-16 03:52:51 +00003036
cristy5c6f7892010-05-05 22:53:29 +00003037 indexes=GetAuthenticIndexQueue(image);
glennrpcf002022011-01-30 02:38:15 +00003038 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003039 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003040
glennrpfaa852b2010-03-30 12:17:00 +00003041 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003042 {
3043 case 1:
3044 {
cristybb503372010-05-27 20:51:26 +00003045 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003046 bit;
3047
cristybb503372010-05-27 20:51:26 +00003048 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003049 {
3050 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003051 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003052 p++;
3053 }
glennrp0fe50b42010-11-16 03:52:51 +00003054
cristy3ed852e2009-09-05 21:47:34 +00003055 if ((image->columns % 8) != 0)
3056 {
cristybb503372010-05-27 20:51:26 +00003057 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003058 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003059 }
glennrp0fe50b42010-11-16 03:52:51 +00003060
cristy3ed852e2009-09-05 21:47:34 +00003061 break;
3062 }
glennrp47b9dd52010-11-24 18:12:06 +00003063
cristy3ed852e2009-09-05 21:47:34 +00003064 case 2:
3065 {
cristybb503372010-05-27 20:51:26 +00003066 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003067 {
glennrpa18d5bc2011-04-23 14:51:34 +00003068 *r++=(*p >> 6) & 0x03;
3069 *r++=(*p >> 4) & 0x03;
3070 *r++=(*p >> 2) & 0x03;
3071 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003072 }
glennrp0fe50b42010-11-16 03:52:51 +00003073
cristy3ed852e2009-09-05 21:47:34 +00003074 if ((image->columns % 4) != 0)
3075 {
cristybb503372010-05-27 20:51:26 +00003076 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003077 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003078 }
glennrp0fe50b42010-11-16 03:52:51 +00003079
cristy3ed852e2009-09-05 21:47:34 +00003080 break;
3081 }
glennrp47b9dd52010-11-24 18:12:06 +00003082
cristy3ed852e2009-09-05 21:47:34 +00003083 case 4:
3084 {
cristybb503372010-05-27 20:51:26 +00003085 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003086 {
glennrpa18d5bc2011-04-23 14:51:34 +00003087 *r++=(*p >> 4) & 0x0f;
3088 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003089 }
glennrp0fe50b42010-11-16 03:52:51 +00003090
cristy3ed852e2009-09-05 21:47:34 +00003091 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003092 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003093
cristy3ed852e2009-09-05 21:47:34 +00003094 break;
3095 }
glennrp47b9dd52010-11-24 18:12:06 +00003096
cristy3ed852e2009-09-05 21:47:34 +00003097 case 8:
3098 {
glennrpfaa852b2010-03-30 12:17:00 +00003099 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003100 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003101 {
glennrpa18d5bc2011-04-23 14:51:34 +00003102 *r++=*p++;
cristy3ed852e2009-09-05 21:47:34 +00003103 /* In image.h, OpaqueOpacity is 0
3104 * TransparentOpacity is QuantumRange
3105 * In a PNG datastream, Opaque is QuantumRange
3106 * and Transparent is 0.
3107 */
glennrp8b698592011-04-26 03:38:21 +00003108 SetOpacityPixelComponent(q,
3109 ScaleCharToQuantum((unsigned char) (255-(*p++))));
3110 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp0b206f52011-01-07 04:55:32 +00003111 found_transparent_pixel = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00003112 q++;
3113 }
glennrp0fe50b42010-11-16 03:52:51 +00003114
cristy3ed852e2009-09-05 21:47:34 +00003115 else
cristybb503372010-05-27 20:51:26 +00003116 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003117 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003118
cristy3ed852e2009-09-05 21:47:34 +00003119 break;
3120 }
glennrp47b9dd52010-11-24 18:12:06 +00003121
cristy3ed852e2009-09-05 21:47:34 +00003122 case 16:
3123 {
cristybb503372010-05-27 20:51:26 +00003124 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003125 {
glennrp58f77c72011-04-23 14:09:09 +00003126#if (MAGICKCORE_QUANTUM_DEPTH == 16)
3127 size_t
3128 quantum;
3129
3130 if (image->colors > 256)
3131 *r=((*p++) << 8);
3132
3133 else
3134 *r=0;
3135
3136 quantum=(*r);
3137 quantum|=(*p++);
3138 *r=(Quantum) quantum;
3139 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003140
3141 if (ping_color_type == 4)
3142 {
glennrp58f77c72011-04-23 14:09:09 +00003143 quantum=((*p++) << 8);
3144 quantum|=(*p++);
glennrp8b698592011-04-26 03:38:21 +00003145 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-quantum));
3146 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00003147 found_transparent_pixel = MagickTrue;
3148 q++;
3149 }
3150#else
3151#if (MAGICKCORE_QUANTUM_DEPTH == 32)
3152 size_t
3153 quantum;
3154
3155 if (image->colors > 256)
3156 *r=((*p++) << 8);
3157
3158 else
3159 *r=0;
3160
3161 quantum=(*r);
3162 quantum|=(*p++);
3163 *r=quantum;
3164 r++;
3165
3166 if (ping_color_type == 4)
3167 {
glennrp8b698592011-04-26 03:38:21 +00003168 quantum=(*p << 8) | *(p+1);
3169 quantum*=65537L;
3170 SetOpacityPixelComponent(q,
3171 (Quantum) GetAlphaPixelComponent(q));
3172 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp58f77c72011-04-23 14:09:09 +00003173 found_transparent_pixel = MagickTrue;
3174 p+=2;
3175 q++;
3176 }
3177
3178#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3179 *r++=(*p++);
3180 p++; /* strip low byte */
3181
3182 if (ping_color_type == 4)
3183 {
glennrp8b698592011-04-26 03:38:21 +00003184 SetOpacityPixelComponent(q,(Quantum) (QuantumRange-(*p++)));
3185 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
glennrp9d0ea4d2011-04-22 18:35:57 +00003186 found_transparent_pixel = MagickTrue;
3187 p++;
3188 q++;
3189 }
cristy3ed852e2009-09-05 21:47:34 +00003190#endif
glennrp58f77c72011-04-23 14:09:09 +00003191#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003192 }
glennrp47b9dd52010-11-24 18:12:06 +00003193
cristy3ed852e2009-09-05 21:47:34 +00003194 break;
3195 }
glennrp47b9dd52010-11-24 18:12:06 +00003196
cristy3ed852e2009-09-05 21:47:34 +00003197 default:
3198 break;
3199 }
glennrp3faa9a32011-04-23 14:00:25 +00003200
cristy3ed852e2009-09-05 21:47:34 +00003201 /*
3202 Transfer image scanline.
3203 */
3204 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003205
cristybb503372010-05-27 20:51:26 +00003206 for (x=0; x < (ssize_t) image->columns; x++)
cristy9fff7b42011-04-29 01:09:31 +00003207 SetIndexPixelComponent(indexes+x,*r++);
glennrp0fe50b42010-11-16 03:52:51 +00003208
cristy3ed852e2009-09-05 21:47:34 +00003209 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3210 break;
glennrp0fe50b42010-11-16 03:52:51 +00003211
cristy7a287bf2010-02-14 02:18:09 +00003212 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3213 {
cristycee97112010-05-28 00:44:52 +00003214 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003215 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003216
cristy7a287bf2010-02-14 02:18:09 +00003217 if (status == MagickFalse)
3218 break;
3219 }
cristy3ed852e2009-09-05 21:47:34 +00003220 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003221
cristy7a287bf2010-02-14 02:18:09 +00003222 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003223 {
3224 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003225
cristy3ed852e2009-09-05 21:47:34 +00003226 if (status == MagickFalse)
3227 break;
3228 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003229
cristy3ed852e2009-09-05 21:47:34 +00003230 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3231 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003232
3233 image->matte=found_transparent_pixel;
3234
3235 if (logging != MagickFalse)
3236 {
3237 if (found_transparent_pixel != MagickFalse)
3238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3239 " Found transparent pixel");
3240 else
glennrp5aa37f62011-01-02 03:07:57 +00003241 {
3242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3243 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003244
glennrp5aa37f62011-01-02 03:07:57 +00003245 ping_color_type&=0x03;
3246 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003247 }
3248 }
3249
cristyb32b90a2009-09-07 21:45:48 +00003250 if (quantum_info != (QuantumInfo *) NULL)
3251 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003252
cristy5c6f7892010-05-05 22:53:29 +00003253 if (image->storage_class == PseudoClass)
3254 {
cristyaeb2cbc2010-05-07 13:28:58 +00003255 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003256 matte;
3257
3258 matte=image->matte;
3259 image->matte=MagickFalse;
3260 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00003261 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003262 }
glennrp47b9dd52010-11-24 18:12:06 +00003263
glennrp4eb39312011-03-30 21:34:55 +00003264 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003265
3266 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003267 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003268 {
3269 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003270 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003271 image->colors=2;
3272 (void) SetImageBackgroundColor(image);
3273#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003274 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003275#endif
3276 if (logging != MagickFalse)
3277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3278 " exit ReadOnePNGImage() early.");
3279 return(image);
3280 }
glennrp47b9dd52010-11-24 18:12:06 +00003281
glennrpfaa852b2010-03-30 12:17:00 +00003282 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003283 {
3284 ClassType
3285 storage_class;
3286
3287 /*
3288 Image has a transparent background.
3289 */
3290 storage_class=image->storage_class;
3291 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003292
glennrp3c218112010-11-27 15:31:26 +00003293/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003294
glennrp0fe50b42010-11-16 03:52:51 +00003295 if (storage_class == PseudoClass)
3296 {
3297 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003298 {
glennrp0fe50b42010-11-16 03:52:51 +00003299 for (x=0; x < ping_num_trans; x++)
3300 {
3301 image->colormap[x].opacity =
3302 ScaleCharToQuantum((unsigned char)(255-ping_trans_alpha[x]));
3303 }
glennrpc11cf6a2010-03-20 16:46:19 +00003304 }
glennrp47b9dd52010-11-24 18:12:06 +00003305
glennrp0fe50b42010-11-16 03:52:51 +00003306 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3307 {
3308 for (x=0; x < (int) image->colors; x++)
3309 {
3310 if (ScaleQuantumToShort(image->colormap[x].red) ==
3311 transparent_color.opacity)
3312 {
3313 image->colormap[x].opacity = (Quantum) TransparentOpacity;
3314 }
3315 }
3316 }
3317 (void) SyncImage(image);
3318 }
glennrp47b9dd52010-11-24 18:12:06 +00003319
glennrpa6a06632011-01-19 15:15:34 +00003320#if 1 /* Should have already been done above, but glennrp problem P10
3321 * needs this.
3322 */
glennrp0fe50b42010-11-16 03:52:51 +00003323 else
3324 {
3325 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003326 {
glennrp0fe50b42010-11-16 03:52:51 +00003327 image->storage_class=storage_class;
3328 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3329
3330 if (q == (PixelPacket *) NULL)
3331 break;
3332
3333 indexes=GetAuthenticIndexQueue(image);
3334
glennrpa6a06632011-01-19 15:15:34 +00003335 /* Caution: on a Q8 build, this does not distinguish between
3336 * 16-bit colors that differ only in the low byte
3337 */
glennrp0fe50b42010-11-16 03:52:51 +00003338 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3339 {
glennrp8b698592011-04-26 03:38:21 +00003340 if (ScaleQuantumToShort(GetRedPixelComponent(q))
3341 == transparent_color.red &&
3342 ScaleQuantumToShort(GetGreenPixelComponent(q))
3343 == transparent_color.green &&
3344 ScaleQuantumToShort(GetBluePixelComponent(q))
3345 == transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003346 {
glennrp8b698592011-04-26 03:38:21 +00003347 SetOpacityPixelComponent(q,TransparentOpacity);
glennrp4f25bd02011-01-01 18:51:28 +00003348 }
glennrp0fe50b42010-11-16 03:52:51 +00003349
glennrp67b9c1a2011-04-22 18:47:36 +00003350#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003351 else
glennrp4f25bd02011-01-01 18:51:28 +00003352 {
glennrp4737d522011-04-29 03:33:42 +00003353 SetOpacityPixelComponent(q)=(Quantum) OpaqueOpacity;
glennrp4f25bd02011-01-01 18:51:28 +00003354 }
glennrpa6a06632011-01-19 15:15:34 +00003355#endif
glennrp0fe50b42010-11-16 03:52:51 +00003356
3357 q++;
3358 }
3359
3360 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3361 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003362 }
glennrp0fe50b42010-11-16 03:52:51 +00003363 }
glennrpa6a06632011-01-19 15:15:34 +00003364#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003365
cristy3ed852e2009-09-05 21:47:34 +00003366 image->storage_class=DirectClass;
3367 }
glennrp3c218112010-11-27 15:31:26 +00003368
cristyb40fc462010-08-08 00:49:49 +00003369 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3370 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3371 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003372
cristyeb3b22a2011-03-31 20:16:11 +00003373 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003374 {
3375 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003376 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3377 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003378 else
glennrpa0ed0092011-04-18 16:36:29 +00003379 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3380 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003381
glennrp4eb39312011-03-30 21:34:55 +00003382 if (status != MagickFalse)
3383 for (i=0; i < (ssize_t) num_text; i++)
3384 {
3385 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003386
glennrp4eb39312011-03-30 21:34:55 +00003387 if (logging != MagickFalse)
3388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3389 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003390
glennrp4eb39312011-03-30 21:34:55 +00003391 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003392 {
glennrp4eb39312011-03-30 21:34:55 +00003393 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
3394 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003395 }
glennrp0fe50b42010-11-16 03:52:51 +00003396
glennrp4eb39312011-03-30 21:34:55 +00003397 else
3398 {
3399 char
3400 *value;
3401
3402 length=text[i].text_length;
3403 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3404 sizeof(*value));
3405 if (value == (char *) NULL)
3406 {
3407 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3408 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3409 image->filename);
3410 break;
3411 }
3412 *value='\0';
3413 (void) ConcatenateMagickString(value,text[i].text,length+2);
3414
3415 /* Don't save "density" or "units" property if we have a pHYs
3416 * chunk
3417 */
3418 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3419 (LocaleCompare(text[i].key,"density") != 0 &&
3420 LocaleCompare(text[i].key,"units") != 0))
3421 (void) SetImageProperty(image,text[i].key,value);
3422
3423 if (logging != MagickFalse)
3424 {
3425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3426 " length: %lu",(unsigned long) length);
3427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3428 " Keyword: %s",text[i].key);
3429 }
3430
3431 value=DestroyString(value);
3432 }
3433 }
3434 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003435 }
glennrp3c218112010-11-27 15:31:26 +00003436
cristy3ed852e2009-09-05 21:47:34 +00003437#ifdef MNG_OBJECT_BUFFERS
3438 /*
3439 Store the object if necessary.
3440 */
3441 if (object_id && !mng_info->frozen[object_id])
3442 {
3443 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3444 {
3445 /*
3446 create a new object buffer.
3447 */
3448 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003449 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003450
cristy3ed852e2009-09-05 21:47:34 +00003451 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3452 {
3453 mng_info->ob[object_id]->image=(Image *) NULL;
3454 mng_info->ob[object_id]->reference_count=1;
3455 }
3456 }
glennrp47b9dd52010-11-24 18:12:06 +00003457
cristy3ed852e2009-09-05 21:47:34 +00003458 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3459 mng_info->ob[object_id]->frozen)
3460 {
3461 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3462 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3463 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3464 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003465
cristy3ed852e2009-09-05 21:47:34 +00003466 if (mng_info->ob[object_id]->frozen)
3467 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3468 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3469 "`%s'",image->filename);
3470 }
glennrp0fe50b42010-11-16 03:52:51 +00003471
cristy3ed852e2009-09-05 21:47:34 +00003472 else
3473 {
cristy3ed852e2009-09-05 21:47:34 +00003474
3475 if (mng_info->ob[object_id]->image != (Image *) NULL)
3476 mng_info->ob[object_id]->image=DestroyImage
3477 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003478
cristy3ed852e2009-09-05 21:47:34 +00003479 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3480 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003481
cristy3ed852e2009-09-05 21:47:34 +00003482 if (mng_info->ob[object_id]->image != (Image *) NULL)
3483 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003484
cristy3ed852e2009-09-05 21:47:34 +00003485 else
3486 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3487 ResourceLimitError,"Cloning image for object buffer failed",
3488 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003489
glennrpfaa852b2010-03-30 12:17:00 +00003490 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003491 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003492
glennrpfaa852b2010-03-30 12:17:00 +00003493 mng_info->ob[object_id]->width=ping_width;
3494 mng_info->ob[object_id]->height=ping_height;
3495 mng_info->ob[object_id]->color_type=ping_color_type;
3496 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3497 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3498 mng_info->ob[object_id]->compression_method=
3499 ping_compression_method;
3500 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003501
glennrpfaa852b2010-03-30 12:17:00 +00003502 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003503 {
3504 int
3505 number_colors;
3506
3507 png_colorp
3508 plte;
3509
3510 /*
3511 Copy the PLTE to the object buffer.
3512 */
3513 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3514 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003515
cristy3ed852e2009-09-05 21:47:34 +00003516 for (i=0; i < number_colors; i++)
3517 {
3518 mng_info->ob[object_id]->plte[i]=plte[i];
3519 }
3520 }
glennrp47b9dd52010-11-24 18:12:06 +00003521
cristy3ed852e2009-09-05 21:47:34 +00003522 else
3523 mng_info->ob[object_id]->plte_length=0;
3524 }
3525 }
3526#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003527
3528 /* Set image->matte to MagickTrue if the input colortype supports
3529 * alpha or if a valid tRNS chunk is present, no matter whether there
3530 * is actual transparency present.
3531 */
3532 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3533 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3534 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3535 MagickTrue : MagickFalse;
3536
glennrpcb395ac2011-03-30 19:50:23 +00003537 /* Set more properties for identify to retrieve */
3538 {
3539 char
3540 msg[MaxTextExtent];
3541
glennrp4eb39312011-03-30 21:34:55 +00003542 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003543 {
3544 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003545 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003546 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003547 (void) SetImageProperty(image,"PNG:text ",msg);
3548 }
3549
3550 if (num_raw_profiles != 0)
3551 {
cristy3b6fd2e2011-05-20 12:53:50 +00003552 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003553 "%d were found", num_raw_profiles);
3554 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3555 }
3556
glennrpcb395ac2011-03-30 19:50:23 +00003557 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003558 {
cristy3b6fd2e2011-05-20 12:53:50 +00003559 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003560 "chunk was found (see Chromaticity, above)");
3561 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3562 }
glennrpcb395ac2011-03-30 19:50:23 +00003563
3564 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003565 {
cristy3b6fd2e2011-05-20 12:53:50 +00003566 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003567 "chunk was found (see Background color, above)");
3568 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3569 }
3570
cristy3b6fd2e2011-05-20 12:53:50 +00003571 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003572 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003573
3574 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3575 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3576
glennrpcb395ac2011-03-30 19:50:23 +00003577 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3578 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003579
3580#if defined(PNG_sRGB_SUPPORTED)
3581 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3582 {
cristy3b6fd2e2011-05-20 12:53:50 +00003583 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003584 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003585 (int) intent);
3586 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3587 }
3588#endif
3589
3590 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3591 {
cristy3b6fd2e2011-05-20 12:53:50 +00003592 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003593 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003594 file_gamma);
3595 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3596 }
3597
3598#if defined(PNG_pHYs_SUPPORTED)
3599 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3600 {
cristy3b6fd2e2011-05-20 12:53:50 +00003601 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003602 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003603 (double) x_resolution,(double) y_resolution, unit_type);
3604 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3605 }
3606#endif
3607
3608#if defined(PNG_oFFs_SUPPORTED)
3609 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3610 {
cristy3b6fd2e2011-05-20 12:53:50 +00003611 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003612 (double) image->page.x,(double) image->page.y);
3613 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3614 }
3615#endif
3616
glennrp07523c72011-03-31 18:12:10 +00003617 if ((image->page.width != 0 && image->page.width != image->columns) ||
3618 (image->page.height != 0 && image->page.height != image->rows))
3619 {
cristy3b6fd2e2011-05-20 12:53:50 +00003620 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003621 "width=%.20g, height=%.20g",
3622 (double) image->page.width,(double) image->page.height);
3623 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3624 }
glennrpcb395ac2011-03-30 19:50:23 +00003625 }
3626
cristy3ed852e2009-09-05 21:47:34 +00003627 /*
3628 Relinquish resources.
3629 */
3630 png_destroy_read_struct(&ping,&ping_info,&end_info);
3631
glennrpcf002022011-01-30 02:38:15 +00003632 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003633#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003634 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003635#endif
3636
3637 if (logging != MagickFalse)
3638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3639 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003640
cristy3ed852e2009-09-05 21:47:34 +00003641 return(image);
3642
3643/* end of reading one PNG image */
3644}
3645
3646static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3647{
3648 Image
3649 *image,
3650 *previous;
3651
3652 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003653 have_mng_structure,
3654 logging,
cristy3ed852e2009-09-05 21:47:34 +00003655 status;
3656
3657 MngInfo
3658 *mng_info;
3659
3660 char
3661 magic_number[MaxTextExtent];
3662
cristy3ed852e2009-09-05 21:47:34 +00003663 ssize_t
3664 count;
3665
3666 /*
3667 Open image file.
3668 */
3669 assert(image_info != (const ImageInfo *) NULL);
3670 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003671
cristy3ed852e2009-09-05 21:47:34 +00003672 if (image_info->debug != MagickFalse)
3673 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3674 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003675
cristy3ed852e2009-09-05 21:47:34 +00003676 assert(exception != (ExceptionInfo *) NULL);
3677 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003678 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003679 image=AcquireImage(image_info);
3680 mng_info=(MngInfo *) NULL;
3681 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003682
cristy3ed852e2009-09-05 21:47:34 +00003683 if (status == MagickFalse)
3684 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003685
cristy3ed852e2009-09-05 21:47:34 +00003686 /*
3687 Verify PNG signature.
3688 */
3689 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003690
glennrpdde35db2011-02-21 12:06:32 +00003691 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003692 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003693
cristy3ed852e2009-09-05 21:47:34 +00003694 /*
3695 Allocate a MngInfo structure.
3696 */
3697 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003698 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003699
cristy3ed852e2009-09-05 21:47:34 +00003700 if (mng_info == (MngInfo *) NULL)
3701 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003702
cristy3ed852e2009-09-05 21:47:34 +00003703 /*
3704 Initialize members of the MngInfo structure.
3705 */
3706 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3707 mng_info->image=image;
3708 have_mng_structure=MagickTrue;
3709
3710 previous=image;
3711 image=ReadOnePNGImage(mng_info,image_info,exception);
3712 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003713
cristy3ed852e2009-09-05 21:47:34 +00003714 if (image == (Image *) NULL)
3715 {
3716 if (previous != (Image *) NULL)
3717 {
3718 if (previous->signature != MagickSignature)
3719 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003720
cristy3ed852e2009-09-05 21:47:34 +00003721 (void) CloseBlob(previous);
3722 (void) DestroyImageList(previous);
3723 }
glennrp0fe50b42010-11-16 03:52:51 +00003724
cristy3ed852e2009-09-05 21:47:34 +00003725 if (logging != MagickFalse)
3726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3727 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003728
cristy3ed852e2009-09-05 21:47:34 +00003729 return((Image *) NULL);
3730 }
glennrp47b9dd52010-11-24 18:12:06 +00003731
cristy3ed852e2009-09-05 21:47:34 +00003732 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003733
cristy3ed852e2009-09-05 21:47:34 +00003734 if ((image->columns == 0) || (image->rows == 0))
3735 {
3736 if (logging != MagickFalse)
3737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3738 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003739
cristy3ed852e2009-09-05 21:47:34 +00003740 ThrowReaderException(CorruptImageError,"CorruptImage");
3741 }
glennrp47b9dd52010-11-24 18:12:06 +00003742
cristy3ed852e2009-09-05 21:47:34 +00003743 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3744 {
3745 (void) SetImageType(image,TrueColorType);
3746 image->matte=MagickFalse;
3747 }
glennrp0fe50b42010-11-16 03:52:51 +00003748
cristy3ed852e2009-09-05 21:47:34 +00003749 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3750 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003751
cristy3ed852e2009-09-05 21:47:34 +00003752 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3754 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3755 (double) image->page.width,(double) image->page.height,
3756 (double) image->page.x,(double) image->page.y);
3757
3758 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003760
cristy3ed852e2009-09-05 21:47:34 +00003761 return(image);
3762}
3763
3764
3765
3766#if defined(JNG_SUPPORTED)
3767/*
3768%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3769% %
3770% %
3771% %
3772% R e a d O n e J N G I m a g e %
3773% %
3774% %
3775% %
3776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3777%
3778% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3779% (minus the 8-byte signature) and returns it. It allocates the memory
3780% necessary for the new Image structure and returns a pointer to the new
3781% image.
3782%
3783% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3784%
3785% The format of the ReadOneJNGImage method is:
3786%
3787% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3788% ExceptionInfo *exception)
3789%
3790% A description of each parameter follows:
3791%
3792% o mng_info: Specifies a pointer to a MngInfo structure.
3793%
3794% o image_info: the image info.
3795%
3796% o exception: return any errors or warnings in this structure.
3797%
3798*/
3799static Image *ReadOneJNGImage(MngInfo *mng_info,
3800 const ImageInfo *image_info, ExceptionInfo *exception)
3801{
3802 Image
3803 *alpha_image,
3804 *color_image,
3805 *image,
3806 *jng_image;
3807
3808 ImageInfo
3809 *alpha_image_info,
3810 *color_image_info;
3811
cristy4383ec82011-01-05 15:42:32 +00003812 MagickBooleanType
3813 logging;
3814
cristybb503372010-05-27 20:51:26 +00003815 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003816 y;
3817
3818 MagickBooleanType
3819 status;
3820
3821 png_uint_32
3822 jng_height,
3823 jng_width;
3824
3825 png_byte
3826 jng_color_type,
3827 jng_image_sample_depth,
3828 jng_image_compression_method,
3829 jng_image_interlace_method,
3830 jng_alpha_sample_depth,
3831 jng_alpha_compression_method,
3832 jng_alpha_filter_method,
3833 jng_alpha_interlace_method;
3834
3835 register const PixelPacket
3836 *s;
3837
cristybb503372010-05-27 20:51:26 +00003838 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003839 i,
3840 x;
3841
3842 register PixelPacket
3843 *q;
3844
3845 register unsigned char
3846 *p;
3847
3848 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003849 read_JSEP,
3850 reading_idat,
3851 skip_to_iend;
3852
cristybb503372010-05-27 20:51:26 +00003853 size_t
cristy3ed852e2009-09-05 21:47:34 +00003854 length;
3855
3856 jng_alpha_compression_method=0;
3857 jng_alpha_sample_depth=8;
3858 jng_color_type=0;
3859 jng_height=0;
3860 jng_width=0;
3861 alpha_image=(Image *) NULL;
3862 color_image=(Image *) NULL;
3863 alpha_image_info=(ImageInfo *) NULL;
3864 color_image_info=(ImageInfo *) NULL;
3865
3866 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003867 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003868
3869 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003870
cristy3ed852e2009-09-05 21:47:34 +00003871 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
3872 {
3873 /*
3874 Allocate next image structure.
3875 */
3876 if (logging != MagickFalse)
3877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3878 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003879
cristy3ed852e2009-09-05 21:47:34 +00003880 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003881
cristy3ed852e2009-09-05 21:47:34 +00003882 if (GetNextImageInList(image) == (Image *) NULL)
3883 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003884
cristy3ed852e2009-09-05 21:47:34 +00003885 image=SyncNextImageInList(image);
3886 }
3887 mng_info->image=image;
3888
3889 /*
3890 Signature bytes have already been read.
3891 */
3892
3893 read_JSEP=MagickFalse;
3894 reading_idat=MagickFalse;
3895 skip_to_iend=MagickFalse;
3896 for (;;)
3897 {
3898 char
3899 type[MaxTextExtent];
3900
3901 unsigned char
3902 *chunk;
3903
3904 unsigned int
3905 count;
3906
3907 /*
3908 Read a new JNG chunk.
3909 */
3910 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3911 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003912
cristy3ed852e2009-09-05 21:47:34 +00003913 if (status == MagickFalse)
3914 break;
glennrp0fe50b42010-11-16 03:52:51 +00003915
cristy3ed852e2009-09-05 21:47:34 +00003916 type[0]='\0';
3917 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3918 length=ReadBlobMSBLong(image);
3919 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3920
3921 if (logging != MagickFalse)
3922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003923 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3924 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003925
3926 if (length > PNG_UINT_31_MAX || count == 0)
3927 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003928
cristy3ed852e2009-09-05 21:47:34 +00003929 p=NULL;
3930 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003931
cristy3ed852e2009-09-05 21:47:34 +00003932 if (length)
3933 {
3934 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003935
cristy3ed852e2009-09-05 21:47:34 +00003936 if (chunk == (unsigned char *) NULL)
3937 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003938
cristybb503372010-05-27 20:51:26 +00003939 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003940 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003941
cristy3ed852e2009-09-05 21:47:34 +00003942 p=chunk;
3943 }
glennrp47b9dd52010-11-24 18:12:06 +00003944
cristy3ed852e2009-09-05 21:47:34 +00003945 (void) ReadBlobMSBLong(image); /* read crc word */
3946
3947 if (skip_to_iend)
3948 {
3949 if (length)
3950 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003951
cristy3ed852e2009-09-05 21:47:34 +00003952 continue;
3953 }
3954
3955 if (memcmp(type,mng_JHDR,4) == 0)
3956 {
3957 if (length == 16)
3958 {
cristybb503372010-05-27 20:51:26 +00003959 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003960 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003961 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003962 (p[6] << 8) | p[7]);
3963 jng_color_type=p[8];
3964 jng_image_sample_depth=p[9];
3965 jng_image_compression_method=p[10];
3966 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3969 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003970
cristy3ed852e2009-09-05 21:47:34 +00003971 jng_alpha_sample_depth=p[12];
3972 jng_alpha_compression_method=p[13];
3973 jng_alpha_filter_method=p[14];
3974 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003975
cristy3ed852e2009-09-05 21:47:34 +00003976 if (logging != MagickFalse)
3977 {
3978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003979 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003980
cristy3ed852e2009-09-05 21:47:34 +00003981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003982 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003983
cristy3ed852e2009-09-05 21:47:34 +00003984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3985 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003986
cristy3ed852e2009-09-05 21:47:34 +00003987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3988 " jng_image_sample_depth: %3d",
3989 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003990
cristy3ed852e2009-09-05 21:47:34 +00003991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3992 " jng_image_compression_method:%3d",
3993 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003994
cristy3ed852e2009-09-05 21:47:34 +00003995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3996 " jng_image_interlace_method: %3d",
3997 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003998
cristy3ed852e2009-09-05 21:47:34 +00003999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4000 " jng_alpha_sample_depth: %3d",
4001 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004002
cristy3ed852e2009-09-05 21:47:34 +00004003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4004 " jng_alpha_compression_method:%3d",
4005 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004006
cristy3ed852e2009-09-05 21:47:34 +00004007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4008 " jng_alpha_filter_method: %3d",
4009 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004010
cristy3ed852e2009-09-05 21:47:34 +00004011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4012 " jng_alpha_interlace_method: %3d",
4013 jng_alpha_interlace_method);
4014 }
4015 }
glennrp47b9dd52010-11-24 18:12:06 +00004016
cristy3ed852e2009-09-05 21:47:34 +00004017 if (length)
4018 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004019
cristy3ed852e2009-09-05 21:47:34 +00004020 continue;
4021 }
4022
4023
4024 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4025 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4026 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4027 {
4028 /*
4029 o create color_image
4030 o open color_blob, attached to color_image
4031 o if (color type has alpha)
4032 open alpha_blob, attached to alpha_image
4033 */
4034
cristy73bd4a52010-10-05 11:24:23 +00004035 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004036
cristy3ed852e2009-09-05 21:47:34 +00004037 if (color_image_info == (ImageInfo *) NULL)
4038 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004039
cristy3ed852e2009-09-05 21:47:34 +00004040 GetImageInfo(color_image_info);
4041 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00004042
cristy3ed852e2009-09-05 21:47:34 +00004043 if (color_image == (Image *) NULL)
4044 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4045
4046 if (logging != MagickFalse)
4047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4048 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004049
cristy3ed852e2009-09-05 21:47:34 +00004050 (void) AcquireUniqueFilename(color_image->filename);
4051 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4052 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004053
cristy3ed852e2009-09-05 21:47:34 +00004054 if (status == MagickFalse)
4055 return((Image *) NULL);
4056
4057 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4058 {
4059 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004060 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004061
cristy3ed852e2009-09-05 21:47:34 +00004062 if (alpha_image_info == (ImageInfo *) NULL)
4063 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004064
cristy3ed852e2009-09-05 21:47:34 +00004065 GetImageInfo(alpha_image_info);
4066 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00004067
cristy3ed852e2009-09-05 21:47:34 +00004068 if (alpha_image == (Image *) NULL)
4069 {
4070 alpha_image=DestroyImage(alpha_image);
4071 ThrowReaderException(ResourceLimitError,
4072 "MemoryAllocationFailed");
4073 }
glennrp0fe50b42010-11-16 03:52:51 +00004074
cristy3ed852e2009-09-05 21:47:34 +00004075 if (logging != MagickFalse)
4076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4077 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004078
cristy3ed852e2009-09-05 21:47:34 +00004079 (void) AcquireUniqueFilename(alpha_image->filename);
4080 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4081 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004082
cristy3ed852e2009-09-05 21:47:34 +00004083 if (status == MagickFalse)
4084 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004085
cristy3ed852e2009-09-05 21:47:34 +00004086 if (jng_alpha_compression_method == 0)
4087 {
4088 unsigned char
4089 data[18];
4090
4091 if (logging != MagickFalse)
4092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4093 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004094
cristy3ed852e2009-09-05 21:47:34 +00004095 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4096 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004097
cristy3ed852e2009-09-05 21:47:34 +00004098 (void) WriteBlobMSBULong(alpha_image,13L);
4099 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004100 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004101 PNGLong(data+4,jng_width);
4102 PNGLong(data+8,jng_height);
4103 data[12]=jng_alpha_sample_depth;
4104 data[13]=0; /* color_type gray */
4105 data[14]=0; /* compression method 0 */
4106 data[15]=0; /* filter_method 0 */
4107 data[16]=0; /* interlace_method 0 */
4108 (void) WriteBlob(alpha_image,17,data);
4109 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4110 }
4111 }
4112 reading_idat=MagickTrue;
4113 }
4114
4115 if (memcmp(type,mng_JDAT,4) == 0)
4116 {
glennrp47b9dd52010-11-24 18:12:06 +00004117 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004118
4119 if (logging != MagickFalse)
4120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4121 " Copying JDAT chunk data to color_blob.");
4122
4123 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004124
cristy3ed852e2009-09-05 21:47:34 +00004125 if (length)
4126 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004127
cristy3ed852e2009-09-05 21:47:34 +00004128 continue;
4129 }
4130
4131 if (memcmp(type,mng_IDAT,4) == 0)
4132 {
4133 png_byte
4134 data[5];
4135
glennrp47b9dd52010-11-24 18:12:06 +00004136 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004137
4138 if (image_info->ping == MagickFalse)
4139 {
4140 if (logging != MagickFalse)
4141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4142 " Copying IDAT chunk data to alpha_blob.");
4143
cristybb503372010-05-27 20:51:26 +00004144 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004145 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004146 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004147 (void) WriteBlob(alpha_image,4,data);
4148 (void) WriteBlob(alpha_image,length,chunk);
4149 (void) WriteBlobMSBULong(alpha_image,
4150 crc32(crc32(0,data,4),chunk,(uInt) length));
4151 }
glennrp0fe50b42010-11-16 03:52:51 +00004152
cristy3ed852e2009-09-05 21:47:34 +00004153 if (length)
4154 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004155
cristy3ed852e2009-09-05 21:47:34 +00004156 continue;
4157 }
4158
4159 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4160 {
glennrp47b9dd52010-11-24 18:12:06 +00004161 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004162
4163 if (image_info->ping == MagickFalse)
4164 {
4165 if (logging != MagickFalse)
4166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4167 " Copying JDAA chunk data to alpha_blob.");
4168
4169 (void) WriteBlob(alpha_image,length,chunk);
4170 }
glennrp0fe50b42010-11-16 03:52:51 +00004171
cristy3ed852e2009-09-05 21:47:34 +00004172 if (length)
4173 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004174
cristy3ed852e2009-09-05 21:47:34 +00004175 continue;
4176 }
4177
4178 if (memcmp(type,mng_JSEP,4) == 0)
4179 {
4180 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004181
cristy3ed852e2009-09-05 21:47:34 +00004182 if (length)
4183 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004184
cristy3ed852e2009-09-05 21:47:34 +00004185 continue;
4186 }
4187
4188 if (memcmp(type,mng_bKGD,4) == 0)
4189 {
4190 if (length == 2)
4191 {
4192 image->background_color.red=ScaleCharToQuantum(p[1]);
4193 image->background_color.green=image->background_color.red;
4194 image->background_color.blue=image->background_color.red;
4195 }
glennrp0fe50b42010-11-16 03:52:51 +00004196
cristy3ed852e2009-09-05 21:47:34 +00004197 if (length == 6)
4198 {
4199 image->background_color.red=ScaleCharToQuantum(p[1]);
4200 image->background_color.green=ScaleCharToQuantum(p[3]);
4201 image->background_color.blue=ScaleCharToQuantum(p[5]);
4202 }
glennrp0fe50b42010-11-16 03:52:51 +00004203
cristy3ed852e2009-09-05 21:47:34 +00004204 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4205 continue;
4206 }
4207
4208 if (memcmp(type,mng_gAMA,4) == 0)
4209 {
4210 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004211 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004212
cristy3ed852e2009-09-05 21:47:34 +00004213 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4214 continue;
4215 }
4216
4217 if (memcmp(type,mng_cHRM,4) == 0)
4218 {
4219 if (length == 32)
4220 {
cristy8182b072010-05-30 20:10:53 +00004221 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4222 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4223 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4224 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4225 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4226 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4227 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4228 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004229 }
glennrp47b9dd52010-11-24 18:12:06 +00004230
cristy3ed852e2009-09-05 21:47:34 +00004231 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4232 continue;
4233 }
4234
4235 if (memcmp(type,mng_sRGB,4) == 0)
4236 {
4237 if (length == 1)
4238 {
glennrpe610a072010-08-05 17:08:46 +00004239 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004240 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004241 image->gamma=0.45455f;
4242 image->chromaticity.red_primary.x=0.6400f;
4243 image->chromaticity.red_primary.y=0.3300f;
4244 image->chromaticity.green_primary.x=0.3000f;
4245 image->chromaticity.green_primary.y=0.6000f;
4246 image->chromaticity.blue_primary.x=0.1500f;
4247 image->chromaticity.blue_primary.y=0.0600f;
4248 image->chromaticity.white_point.x=0.3127f;
4249 image->chromaticity.white_point.y=0.3290f;
4250 }
glennrp47b9dd52010-11-24 18:12:06 +00004251
cristy3ed852e2009-09-05 21:47:34 +00004252 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4253 continue;
4254 }
4255
4256 if (memcmp(type,mng_oFFs,4) == 0)
4257 {
4258 if (length > 8)
4259 {
glennrp5eae7602011-02-22 15:21:32 +00004260 image->page.x=(ssize_t) mng_get_long(p);
4261 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004262
cristy3ed852e2009-09-05 21:47:34 +00004263 if ((int) p[8] != 0)
4264 {
4265 image->page.x/=10000;
4266 image->page.y/=10000;
4267 }
4268 }
glennrp47b9dd52010-11-24 18:12:06 +00004269
cristy3ed852e2009-09-05 21:47:34 +00004270 if (length)
4271 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004272
cristy3ed852e2009-09-05 21:47:34 +00004273 continue;
4274 }
4275
4276 if (memcmp(type,mng_pHYs,4) == 0)
4277 {
4278 if (length > 8)
4279 {
cristy8182b072010-05-30 20:10:53 +00004280 image->x_resolution=(double) mng_get_long(p);
4281 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004282 if ((int) p[8] == PNG_RESOLUTION_METER)
4283 {
4284 image->units=PixelsPerCentimeterResolution;
4285 image->x_resolution=image->x_resolution/100.0f;
4286 image->y_resolution=image->y_resolution/100.0f;
4287 }
4288 }
glennrp0fe50b42010-11-16 03:52:51 +00004289
cristy3ed852e2009-09-05 21:47:34 +00004290 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4291 continue;
4292 }
4293
4294#if 0
4295 if (memcmp(type,mng_iCCP,4) == 0)
4296 {
glennrpfd05d622011-02-25 04:10:33 +00004297 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004298 if (length)
4299 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004300
cristy3ed852e2009-09-05 21:47:34 +00004301 continue;
4302 }
4303#endif
4304
4305 if (length)
4306 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4307
4308 if (memcmp(type,mng_IEND,4))
4309 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004310
cristy3ed852e2009-09-05 21:47:34 +00004311 break;
4312 }
4313
4314
4315 /* IEND found */
4316
4317 /*
4318 Finish up reading image data:
4319
4320 o read main image from color_blob.
4321
4322 o close color_blob.
4323
4324 o if (color_type has alpha)
4325 if alpha_encoding is PNG
4326 read secondary image from alpha_blob via ReadPNG
4327 if alpha_encoding is JPEG
4328 read secondary image from alpha_blob via ReadJPEG
4329
4330 o close alpha_blob.
4331
4332 o copy intensity of secondary image into
4333 opacity samples of main image.
4334
4335 o destroy the secondary image.
4336 */
4337
4338 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004339
cristy3ed852e2009-09-05 21:47:34 +00004340 if (logging != MagickFalse)
4341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4342 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004343
cristy3b6fd2e2011-05-20 12:53:50 +00004344 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004345 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004346
cristy3ed852e2009-09-05 21:47:34 +00004347 color_image_info->ping=MagickFalse; /* To do: avoid this */
4348 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004349
cristy3ed852e2009-09-05 21:47:34 +00004350 if (jng_image == (Image *) NULL)
4351 return((Image *) NULL);
4352
4353 (void) RelinquishUniqueFileResource(color_image->filename);
4354 color_image=DestroyImage(color_image);
4355 color_image_info=DestroyImageInfo(color_image_info);
4356
4357 if (jng_image == (Image *) NULL)
4358 return((Image *) NULL);
4359
4360 if (logging != MagickFalse)
4361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4362 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004363
cristy3ed852e2009-09-05 21:47:34 +00004364 image->rows=jng_height;
4365 image->columns=jng_width;
4366 length=image->columns*sizeof(PixelPacket);
glennrp0fe50b42010-11-16 03:52:51 +00004367
cristybb503372010-05-27 20:51:26 +00004368 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004369 {
4370 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4371 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4372 (void) CopyMagickMemory(q,s,length);
glennrp47b9dd52010-11-24 18:12:06 +00004373
cristy3ed852e2009-09-05 21:47:34 +00004374 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4375 break;
4376 }
glennrp0fe50b42010-11-16 03:52:51 +00004377
cristy3ed852e2009-09-05 21:47:34 +00004378 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004379
cristy3ed852e2009-09-05 21:47:34 +00004380 if (image_info->ping == MagickFalse)
4381 {
4382 if (jng_color_type >= 12)
4383 {
4384 if (jng_alpha_compression_method == 0)
4385 {
4386 png_byte
4387 data[5];
4388 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4389 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004390 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004391 (void) WriteBlob(alpha_image,4,data);
4392 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4393 }
glennrp0fe50b42010-11-16 03:52:51 +00004394
cristy3ed852e2009-09-05 21:47:34 +00004395 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004396
cristy3ed852e2009-09-05 21:47:34 +00004397 if (logging != MagickFalse)
4398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4399 " Reading opacity from alpha_blob.");
4400
cristy3b6fd2e2011-05-20 12:53:50 +00004401 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004402 "%s",alpha_image->filename);
4403
4404 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004405
cristy3ed852e2009-09-05 21:47:34 +00004406 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004407 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004408 {
4409 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4410 &image->exception);
4411 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004412
cristy3ed852e2009-09-05 21:47:34 +00004413 if (image->matte != MagickFalse)
cristybb503372010-05-27 20:51:26 +00004414 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
glennrp4737d522011-04-29 03:33:42 +00004415 SetOpacityPixelComponent(q,(Quantum) QuantumRange-
4416 GetRedPixelComponent(s));
glennrp0fe50b42010-11-16 03:52:51 +00004417
cristy3ed852e2009-09-05 21:47:34 +00004418 else
cristybb503372010-05-27 20:51:26 +00004419 for (x=(ssize_t) image->columns; x != 0; x--,q++,s++)
cristy3ed852e2009-09-05 21:47:34 +00004420 {
glennrp7c7b3152011-04-26 04:01:27 +00004421 SetOpacityPixelComponent(q,(Quantum) QuantumRange-
4422 GetRedPixelComponent(s));
4423 if (GetOpacityPixelComponent(q) != OpaqueOpacity)
cristy3ed852e2009-09-05 21:47:34 +00004424 image->matte=MagickTrue;
4425 }
glennrp0fe50b42010-11-16 03:52:51 +00004426
cristy3ed852e2009-09-05 21:47:34 +00004427 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4428 break;
4429 }
4430 (void) RelinquishUniqueFileResource(alpha_image->filename);
4431 alpha_image=DestroyImage(alpha_image);
4432 alpha_image_info=DestroyImageInfo(alpha_image_info);
4433 if (jng_image != (Image *) NULL)
4434 jng_image=DestroyImage(jng_image);
4435 }
4436 }
4437
glennrp47b9dd52010-11-24 18:12:06 +00004438 /* Read the JNG image. */
4439
cristy3ed852e2009-09-05 21:47:34 +00004440 if (mng_info->mng_type == 0)
4441 {
4442 mng_info->mng_width=jng_width;
4443 mng_info->mng_height=jng_height;
4444 }
glennrp0fe50b42010-11-16 03:52:51 +00004445
cristy3ed852e2009-09-05 21:47:34 +00004446 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004447 {
4448 image->page.width=jng_width;
4449 image->page.height=jng_height;
4450 }
4451
cristy3ed852e2009-09-05 21:47:34 +00004452 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004453 {
4454 image->page.x=mng_info->x_off[mng_info->object_id];
4455 image->page.y=mng_info->y_off[mng_info->object_id];
4456 }
4457
cristy3ed852e2009-09-05 21:47:34 +00004458 else
glennrp0fe50b42010-11-16 03:52:51 +00004459 {
4460 image->page.y=mng_info->y_off[mng_info->object_id];
4461 }
4462
cristy3ed852e2009-09-05 21:47:34 +00004463 mng_info->image_found++;
4464 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4465 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004466
cristy3ed852e2009-09-05 21:47:34 +00004467 if (logging != MagickFalse)
4468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4469 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004470
cristy3ed852e2009-09-05 21:47:34 +00004471 return(image);
4472}
4473
4474/*
4475%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4476% %
4477% %
4478% %
4479% R e a d J N G I m a g e %
4480% %
4481% %
4482% %
4483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4484%
4485% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4486% (including the 8-byte signature) and returns it. It allocates the memory
4487% necessary for the new Image structure and returns a pointer to the new
4488% image.
4489%
4490% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4491%
4492% The format of the ReadJNGImage method is:
4493%
4494% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4495% *exception)
4496%
4497% A description of each parameter follows:
4498%
4499% o image_info: the image info.
4500%
4501% o exception: return any errors or warnings in this structure.
4502%
4503*/
4504
4505static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4506{
4507 Image
4508 *image,
4509 *previous;
4510
4511 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004512 have_mng_structure,
4513 logging,
cristy3ed852e2009-09-05 21:47:34 +00004514 status;
4515
4516 MngInfo
4517 *mng_info;
4518
4519 char
4520 magic_number[MaxTextExtent];
4521
cristy3ed852e2009-09-05 21:47:34 +00004522 size_t
4523 count;
4524
4525 /*
4526 Open image file.
4527 */
4528 assert(image_info != (const ImageInfo *) NULL);
4529 assert(image_info->signature == MagickSignature);
4530 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4531 assert(exception != (ExceptionInfo *) NULL);
4532 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004533 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004534 image=AcquireImage(image_info);
4535 mng_info=(MngInfo *) NULL;
4536 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004537
cristy3ed852e2009-09-05 21:47:34 +00004538 if (status == MagickFalse)
4539 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004540
cristy3ed852e2009-09-05 21:47:34 +00004541 if (LocaleCompare(image_info->magick,"JNG") != 0)
4542 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004543
glennrp47b9dd52010-11-24 18:12:06 +00004544 /* Verify JNG signature. */
4545
cristy3ed852e2009-09-05 21:47:34 +00004546 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004547
glennrp3b8763e2011-02-21 12:08:18 +00004548 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004549 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004550
glennrp47b9dd52010-11-24 18:12:06 +00004551 /* Allocate a MngInfo structure. */
4552
cristy3ed852e2009-09-05 21:47:34 +00004553 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004554 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004555
cristy3ed852e2009-09-05 21:47:34 +00004556 if (mng_info == (MngInfo *) NULL)
4557 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004558
glennrp47b9dd52010-11-24 18:12:06 +00004559 /* Initialize members of the MngInfo structure. */
4560
cristy3ed852e2009-09-05 21:47:34 +00004561 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4562 have_mng_structure=MagickTrue;
4563
4564 mng_info->image=image;
4565 previous=image;
4566 image=ReadOneJNGImage(mng_info,image_info,exception);
4567 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004568
cristy3ed852e2009-09-05 21:47:34 +00004569 if (image == (Image *) NULL)
4570 {
4571 if (IsImageObject(previous) != MagickFalse)
4572 {
4573 (void) CloseBlob(previous);
4574 (void) DestroyImageList(previous);
4575 }
glennrp0fe50b42010-11-16 03:52:51 +00004576
cristy3ed852e2009-09-05 21:47:34 +00004577 if (logging != MagickFalse)
4578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4579 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004580
cristy3ed852e2009-09-05 21:47:34 +00004581 return((Image *) NULL);
4582 }
4583 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004584
cristy3ed852e2009-09-05 21:47:34 +00004585 if (image->columns == 0 || image->rows == 0)
4586 {
4587 if (logging != MagickFalse)
4588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4589 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004590
cristy3ed852e2009-09-05 21:47:34 +00004591 ThrowReaderException(CorruptImageError,"CorruptImage");
4592 }
glennrp0fe50b42010-11-16 03:52:51 +00004593
cristy3ed852e2009-09-05 21:47:34 +00004594 if (logging != MagickFalse)
4595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004596
cristy3ed852e2009-09-05 21:47:34 +00004597 return(image);
4598}
4599#endif
4600
4601static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4602{
4603 char
4604 page_geometry[MaxTextExtent];
4605
4606 Image
4607 *image,
4608 *previous;
4609
cristy4383ec82011-01-05 15:42:32 +00004610 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004611 logging,
4612 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004613
cristy3ed852e2009-09-05 21:47:34 +00004614 volatile int
4615 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004616 object_id,
4617 term_chunk_found,
4618 skip_to_iend;
4619
cristybb503372010-05-27 20:51:26 +00004620 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004621 image_count=0;
4622
4623 MagickBooleanType
4624 status;
4625
4626 MagickOffsetType
4627 offset;
4628
4629 MngInfo
4630 *mng_info;
4631
4632 MngBox
4633 default_fb,
4634 fb,
4635 previous_fb;
4636
4637#if defined(MNG_INSERT_LAYERS)
4638 PixelPacket
4639 mng_background_color;
4640#endif
4641
4642 register unsigned char
4643 *p;
4644
cristybb503372010-05-27 20:51:26 +00004645 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004646 i;
4647
4648 size_t
4649 count;
4650
cristybb503372010-05-27 20:51:26 +00004651 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004652 loop_level;
4653
4654 volatile short
4655 skipping_loop;
4656
4657#if defined(MNG_INSERT_LAYERS)
4658 unsigned int
4659 mandatory_back=0;
4660#endif
4661
4662 volatile unsigned int
4663#ifdef MNG_OBJECT_BUFFERS
4664 mng_background_object=0,
4665#endif
4666 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4667
cristybb503372010-05-27 20:51:26 +00004668 size_t
cristy3ed852e2009-09-05 21:47:34 +00004669 default_frame_timeout,
4670 frame_timeout,
4671#if defined(MNG_INSERT_LAYERS)
4672 image_height,
4673 image_width,
4674#endif
4675 length;
4676
glennrp38ea0832010-06-02 18:50:28 +00004677 /* These delays are all measured in image ticks_per_second,
4678 * not in MNG ticks_per_second
4679 */
cristybb503372010-05-27 20:51:26 +00004680 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004681 default_frame_delay,
4682 final_delay,
4683 final_image_delay,
4684 frame_delay,
4685#if defined(MNG_INSERT_LAYERS)
4686 insert_layers,
4687#endif
4688 mng_iterations=1,
4689 simplicity=0,
4690 subframe_height=0,
4691 subframe_width=0;
4692
4693 previous_fb.top=0;
4694 previous_fb.bottom=0;
4695 previous_fb.left=0;
4696 previous_fb.right=0;
4697 default_fb.top=0;
4698 default_fb.bottom=0;
4699 default_fb.left=0;
4700 default_fb.right=0;
4701
glennrp47b9dd52010-11-24 18:12:06 +00004702 /* Open image file. */
4703
cristy3ed852e2009-09-05 21:47:34 +00004704 assert(image_info != (const ImageInfo *) NULL);
4705 assert(image_info->signature == MagickSignature);
4706 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4707 assert(exception != (ExceptionInfo *) NULL);
4708 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004709 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004710 image=AcquireImage(image_info);
4711 mng_info=(MngInfo *) NULL;
4712 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004713
cristy3ed852e2009-09-05 21:47:34 +00004714 if (status == MagickFalse)
4715 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004716
cristy3ed852e2009-09-05 21:47:34 +00004717 first_mng_object=MagickFalse;
4718 skipping_loop=(-1);
4719 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004720
4721 /* Allocate a MngInfo structure. */
4722
cristy73bd4a52010-10-05 11:24:23 +00004723 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004724
cristy3ed852e2009-09-05 21:47:34 +00004725 if (mng_info == (MngInfo *) NULL)
4726 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004727
glennrp47b9dd52010-11-24 18:12:06 +00004728 /* Initialize members of the MngInfo structure. */
4729
cristy3ed852e2009-09-05 21:47:34 +00004730 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4731 mng_info->image=image;
4732 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004733
4734 if (LocaleCompare(image_info->magick,"MNG") == 0)
4735 {
4736 char
4737 magic_number[MaxTextExtent];
4738
glennrp47b9dd52010-11-24 18:12:06 +00004739 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004740 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4741 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4742 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004743
4744 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004745 for (i=0; i < MNG_MAX_OBJECTS; i++)
4746 {
cristybb503372010-05-27 20:51:26 +00004747 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4748 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004749 }
4750 mng_info->exists[0]=MagickTrue;
4751 }
glennrp47b9dd52010-11-24 18:12:06 +00004752
cristy3ed852e2009-09-05 21:47:34 +00004753 first_mng_object=MagickTrue;
4754 mng_type=0;
4755#if defined(MNG_INSERT_LAYERS)
4756 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4757#endif
4758 default_frame_delay=0;
4759 default_frame_timeout=0;
4760 frame_delay=0;
4761 final_delay=1;
4762 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4763 object_id=0;
4764 skip_to_iend=MagickFalse;
4765 term_chunk_found=MagickFalse;
4766 mng_info->framing_mode=1;
4767#if defined(MNG_INSERT_LAYERS)
4768 mandatory_back=MagickFalse;
4769#endif
4770#if defined(MNG_INSERT_LAYERS)
4771 mng_background_color=image->background_color;
4772#endif
4773 default_fb=mng_info->frame;
4774 previous_fb=mng_info->frame;
4775 do
4776 {
4777 char
4778 type[MaxTextExtent];
4779
4780 if (LocaleCompare(image_info->magick,"MNG") == 0)
4781 {
4782 unsigned char
4783 *chunk;
4784
4785 /*
4786 Read a new chunk.
4787 */
4788 type[0]='\0';
4789 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4790 length=ReadBlobMSBLong(image);
4791 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4792
4793 if (logging != MagickFalse)
4794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004795 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4796 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004797
4798 if (length > PNG_UINT_31_MAX)
4799 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004800
cristy3ed852e2009-09-05 21:47:34 +00004801 if (count == 0)
4802 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004803
cristy3ed852e2009-09-05 21:47:34 +00004804 p=NULL;
4805 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004806
cristy3ed852e2009-09-05 21:47:34 +00004807 if (length)
4808 {
4809 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004810
cristy3ed852e2009-09-05 21:47:34 +00004811 if (chunk == (unsigned char *) NULL)
4812 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004813
cristybb503372010-05-27 20:51:26 +00004814 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004815 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004816
cristy3ed852e2009-09-05 21:47:34 +00004817 p=chunk;
4818 }
glennrp0fe50b42010-11-16 03:52:51 +00004819
cristy3ed852e2009-09-05 21:47:34 +00004820 (void) ReadBlobMSBLong(image); /* read crc word */
4821
4822#if !defined(JNG_SUPPORTED)
4823 if (memcmp(type,mng_JHDR,4) == 0)
4824 {
4825 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004826
cristy3ed852e2009-09-05 21:47:34 +00004827 if (mng_info->jhdr_warning == 0)
4828 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4829 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004830
cristy3ed852e2009-09-05 21:47:34 +00004831 mng_info->jhdr_warning++;
4832 }
4833#endif
4834 if (memcmp(type,mng_DHDR,4) == 0)
4835 {
4836 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004837
cristy3ed852e2009-09-05 21:47:34 +00004838 if (mng_info->dhdr_warning == 0)
4839 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4840 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004841
cristy3ed852e2009-09-05 21:47:34 +00004842 mng_info->dhdr_warning++;
4843 }
4844 if (memcmp(type,mng_MEND,4) == 0)
4845 break;
glennrp47b9dd52010-11-24 18:12:06 +00004846
cristy3ed852e2009-09-05 21:47:34 +00004847 if (skip_to_iend)
4848 {
4849 if (memcmp(type,mng_IEND,4) == 0)
4850 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004851
cristy3ed852e2009-09-05 21:47:34 +00004852 if (length)
4853 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004854
cristy3ed852e2009-09-05 21:47:34 +00004855 if (logging != MagickFalse)
4856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4857 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004858
cristy3ed852e2009-09-05 21:47:34 +00004859 continue;
4860 }
glennrp0fe50b42010-11-16 03:52:51 +00004861
cristy3ed852e2009-09-05 21:47:34 +00004862 if (memcmp(type,mng_MHDR,4) == 0)
4863 {
cristybb503372010-05-27 20:51:26 +00004864 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004865 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004866
cristybb503372010-05-27 20:51:26 +00004867 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004868 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004869
cristy3ed852e2009-09-05 21:47:34 +00004870 if (logging != MagickFalse)
4871 {
4872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004873 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004875 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004876 }
glennrp0fe50b42010-11-16 03:52:51 +00004877
cristy3ed852e2009-09-05 21:47:34 +00004878 p+=8;
cristy8182b072010-05-30 20:10:53 +00004879 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004880
cristy3ed852e2009-09-05 21:47:34 +00004881 if (mng_info->ticks_per_second == 0)
4882 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004883
cristy3ed852e2009-09-05 21:47:34 +00004884 else
4885 default_frame_delay=1UL*image->ticks_per_second/
4886 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004887
cristy3ed852e2009-09-05 21:47:34 +00004888 frame_delay=default_frame_delay;
4889 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004890
cristy3ed852e2009-09-05 21:47:34 +00004891 if (length > 16)
4892 {
4893 p+=16;
cristy8182b072010-05-30 20:10:53 +00004894 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004895 }
glennrp0fe50b42010-11-16 03:52:51 +00004896
cristy3ed852e2009-09-05 21:47:34 +00004897 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004898
cristy3ed852e2009-09-05 21:47:34 +00004899 if ((simplicity != 0) && ((simplicity | 11) == 11))
4900 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004901
cristy3ed852e2009-09-05 21:47:34 +00004902 if ((simplicity != 0) && ((simplicity | 9) == 9))
4903 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004904
cristy3ed852e2009-09-05 21:47:34 +00004905#if defined(MNG_INSERT_LAYERS)
4906 if (mng_type != 3)
4907 insert_layers=MagickTrue;
4908#endif
4909 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
4910 {
glennrp47b9dd52010-11-24 18:12:06 +00004911 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004912 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004913
cristy3ed852e2009-09-05 21:47:34 +00004914 if (GetNextImageInList(image) == (Image *) NULL)
4915 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004916
cristy3ed852e2009-09-05 21:47:34 +00004917 image=SyncNextImageInList(image);
4918 mng_info->image=image;
4919 }
4920
4921 if ((mng_info->mng_width > 65535L) ||
4922 (mng_info->mng_height > 65535L))
4923 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004924
cristy3b6fd2e2011-05-20 12:53:50 +00004925 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004926 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004927 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004928
cristy3ed852e2009-09-05 21:47:34 +00004929 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004930 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004931 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004932 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004933 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004934
cristy3ed852e2009-09-05 21:47:34 +00004935 for (i=0; i < MNG_MAX_OBJECTS; i++)
4936 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004937
cristy3ed852e2009-09-05 21:47:34 +00004938 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4939 continue;
4940 }
4941
4942 if (memcmp(type,mng_TERM,4) == 0)
4943 {
4944 int
4945 repeat=0;
4946
4947
4948 if (length)
4949 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004950
cristy3ed852e2009-09-05 21:47:34 +00004951 if (repeat == 3)
4952 {
cristy8182b072010-05-30 20:10:53 +00004953 final_delay=(png_uint_32) mng_get_long(&p[2]);
4954 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004955
cristy3ed852e2009-09-05 21:47:34 +00004956 if (mng_iterations == PNG_UINT_31_MAX)
4957 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004958
cristy3ed852e2009-09-05 21:47:34 +00004959 image->iterations=mng_iterations;
4960 term_chunk_found=MagickTrue;
4961 }
glennrp0fe50b42010-11-16 03:52:51 +00004962
cristy3ed852e2009-09-05 21:47:34 +00004963 if (logging != MagickFalse)
4964 {
4965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4966 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004967
cristy3ed852e2009-09-05 21:47:34 +00004968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004969 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004970
cristy3ed852e2009-09-05 21:47:34 +00004971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004972 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004973 }
glennrp0fe50b42010-11-16 03:52:51 +00004974
cristy3ed852e2009-09-05 21:47:34 +00004975 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4976 continue;
4977 }
4978 if (memcmp(type,mng_DEFI,4) == 0)
4979 {
4980 if (mng_type == 3)
4981 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4982 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4983 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004984
cristy3ed852e2009-09-05 21:47:34 +00004985 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004986
cristy3ed852e2009-09-05 21:47:34 +00004987 if (mng_type == 2 && object_id != 0)
4988 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4989 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4990 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004991
cristy3ed852e2009-09-05 21:47:34 +00004992 if (object_id > MNG_MAX_OBJECTS)
4993 {
4994 /*
4995 Instead ofsuing a warning we should allocate a larger
4996 MngInfo structure and continue.
4997 */
4998 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4999 CoderError,"object id too large","`%s'",image->filename);
5000 object_id=MNG_MAX_OBJECTS;
5001 }
glennrp0fe50b42010-11-16 03:52:51 +00005002
cristy3ed852e2009-09-05 21:47:34 +00005003 if (mng_info->exists[object_id])
5004 if (mng_info->frozen[object_id])
5005 {
5006 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5007 (void) ThrowMagickException(&image->exception,
5008 GetMagickModule(),CoderError,
5009 "DEFI cannot redefine a frozen MNG object","`%s'",
5010 image->filename);
5011 continue;
5012 }
glennrp0fe50b42010-11-16 03:52:51 +00005013
cristy3ed852e2009-09-05 21:47:34 +00005014 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005015
cristy3ed852e2009-09-05 21:47:34 +00005016 if (length > 2)
5017 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005018
cristy3ed852e2009-09-05 21:47:34 +00005019 /*
5020 Extract object offset info.
5021 */
5022 if (length > 11)
5023 {
glennrp0fe50b42010-11-16 03:52:51 +00005024 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5025 (p[5] << 16) | (p[6] << 8) | p[7]);
5026
5027 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5028 (p[9] << 16) | (p[10] << 8) | p[11]);
5029
cristy3ed852e2009-09-05 21:47:34 +00005030 if (logging != MagickFalse)
5031 {
5032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005033 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005034 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005035
cristy3ed852e2009-09-05 21:47:34 +00005036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005037 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005038 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005039 }
5040 }
glennrp0fe50b42010-11-16 03:52:51 +00005041
cristy3ed852e2009-09-05 21:47:34 +00005042 /*
5043 Extract object clipping info.
5044 */
5045 if (length > 27)
5046 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5047 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5050 continue;
5051 }
5052 if (memcmp(type,mng_bKGD,4) == 0)
5053 {
5054 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005055
cristy3ed852e2009-09-05 21:47:34 +00005056 if (length > 5)
5057 {
5058 mng_info->mng_global_bkgd.red=
5059 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005060
cristy3ed852e2009-09-05 21:47:34 +00005061 mng_info->mng_global_bkgd.green=
5062 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005063
cristy3ed852e2009-09-05 21:47:34 +00005064 mng_info->mng_global_bkgd.blue=
5065 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005066
cristy3ed852e2009-09-05 21:47:34 +00005067 mng_info->have_global_bkgd=MagickTrue;
5068 }
glennrp0fe50b42010-11-16 03:52:51 +00005069
cristy3ed852e2009-09-05 21:47:34 +00005070 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5071 continue;
5072 }
5073 if (memcmp(type,mng_BACK,4) == 0)
5074 {
5075#if defined(MNG_INSERT_LAYERS)
5076 if (length > 6)
5077 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005078
cristy3ed852e2009-09-05 21:47:34 +00005079 else
5080 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005081
cristy3ed852e2009-09-05 21:47:34 +00005082 if (mandatory_back && length > 5)
5083 {
5084 mng_background_color.red=
5085 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005086
cristy3ed852e2009-09-05 21:47:34 +00005087 mng_background_color.green=
5088 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005089
cristy3ed852e2009-09-05 21:47:34 +00005090 mng_background_color.blue=
5091 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005092
cristy3ed852e2009-09-05 21:47:34 +00005093 mng_background_color.opacity=OpaqueOpacity;
5094 }
glennrp0fe50b42010-11-16 03:52:51 +00005095
cristy3ed852e2009-09-05 21:47:34 +00005096#ifdef MNG_OBJECT_BUFFERS
5097 if (length > 8)
5098 mng_background_object=(p[7] << 8) | p[8];
5099#endif
5100#endif
5101 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5102 continue;
5103 }
glennrp47b9dd52010-11-24 18:12:06 +00005104
cristy3ed852e2009-09-05 21:47:34 +00005105 if (memcmp(type,mng_PLTE,4) == 0)
5106 {
glennrp47b9dd52010-11-24 18:12:06 +00005107 /* Read global PLTE. */
5108
cristy3ed852e2009-09-05 21:47:34 +00005109 if (length && (length < 769))
5110 {
5111 if (mng_info->global_plte == (png_colorp) NULL)
5112 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5113 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005114
cristybb503372010-05-27 20:51:26 +00005115 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005116 {
5117 mng_info->global_plte[i].red=p[3*i];
5118 mng_info->global_plte[i].green=p[3*i+1];
5119 mng_info->global_plte[i].blue=p[3*i+2];
5120 }
glennrp0fe50b42010-11-16 03:52:51 +00005121
cristy35ef8242010-06-03 16:24:13 +00005122 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005123 }
5124#ifdef MNG_LOOSE
5125 for ( ; i < 256; i++)
5126 {
5127 mng_info->global_plte[i].red=i;
5128 mng_info->global_plte[i].green=i;
5129 mng_info->global_plte[i].blue=i;
5130 }
glennrp0fe50b42010-11-16 03:52:51 +00005131
cristy3ed852e2009-09-05 21:47:34 +00005132 if (length)
5133 mng_info->global_plte_length=256;
5134#endif
5135 else
5136 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005137
cristy3ed852e2009-09-05 21:47:34 +00005138 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5139 continue;
5140 }
glennrp47b9dd52010-11-24 18:12:06 +00005141
cristy3ed852e2009-09-05 21:47:34 +00005142 if (memcmp(type,mng_tRNS,4) == 0)
5143 {
5144 /* read global tRNS */
5145
5146 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005147 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005148 mng_info->global_trns[i]=p[i];
5149
5150#ifdef MNG_LOOSE
5151 for ( ; i < 256; i++)
5152 mng_info->global_trns[i]=255;
5153#endif
cristy12560f32010-06-03 16:51:08 +00005154 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005155 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5156 continue;
5157 }
5158 if (memcmp(type,mng_gAMA,4) == 0)
5159 {
5160 if (length == 4)
5161 {
cristybb503372010-05-27 20:51:26 +00005162 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005163 igamma;
5164
cristy8182b072010-05-30 20:10:53 +00005165 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005166 mng_info->global_gamma=((float) igamma)*0.00001;
5167 mng_info->have_global_gama=MagickTrue;
5168 }
glennrp0fe50b42010-11-16 03:52:51 +00005169
cristy3ed852e2009-09-05 21:47:34 +00005170 else
5171 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005172
cristy3ed852e2009-09-05 21:47:34 +00005173 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5174 continue;
5175 }
5176
5177 if (memcmp(type,mng_cHRM,4) == 0)
5178 {
glennrp47b9dd52010-11-24 18:12:06 +00005179 /* Read global cHRM */
5180
cristy3ed852e2009-09-05 21:47:34 +00005181 if (length == 32)
5182 {
cristy8182b072010-05-30 20:10:53 +00005183 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5184 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5185 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005186 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005187 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005188 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005189 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005190 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005191 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005192 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005193 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005194 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005195 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005196 mng_info->have_global_chrm=MagickTrue;
5197 }
5198 else
5199 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005200
cristy3ed852e2009-09-05 21:47:34 +00005201 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5202 continue;
5203 }
glennrp47b9dd52010-11-24 18:12:06 +00005204
cristy3ed852e2009-09-05 21:47:34 +00005205 if (memcmp(type,mng_sRGB,4) == 0)
5206 {
5207 /*
5208 Read global sRGB.
5209 */
5210 if (length)
5211 {
glennrpe610a072010-08-05 17:08:46 +00005212 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005213 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005214 mng_info->have_global_srgb=MagickTrue;
5215 }
5216 else
5217 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005218
cristy3ed852e2009-09-05 21:47:34 +00005219 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5220 continue;
5221 }
glennrp47b9dd52010-11-24 18:12:06 +00005222
cristy3ed852e2009-09-05 21:47:34 +00005223 if (memcmp(type,mng_iCCP,4) == 0)
5224 {
glennrpfd05d622011-02-25 04:10:33 +00005225 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005226
5227 /*
5228 Read global iCCP.
5229 */
5230 if (length)
5231 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005232
cristy3ed852e2009-09-05 21:47:34 +00005233 continue;
5234 }
glennrp47b9dd52010-11-24 18:12:06 +00005235
cristy3ed852e2009-09-05 21:47:34 +00005236 if (memcmp(type,mng_FRAM,4) == 0)
5237 {
5238 if (mng_type == 3)
5239 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5240 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5241 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005242
cristy3ed852e2009-09-05 21:47:34 +00005243 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5244 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005245
cristy3ed852e2009-09-05 21:47:34 +00005246 frame_delay=default_frame_delay;
5247 frame_timeout=default_frame_timeout;
5248 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005249
cristy3ed852e2009-09-05 21:47:34 +00005250 if (length)
5251 if (p[0])
5252 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005253
cristy3ed852e2009-09-05 21:47:34 +00005254 if (logging != MagickFalse)
5255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5256 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005257
cristy3ed852e2009-09-05 21:47:34 +00005258 if (length > 6)
5259 {
glennrp47b9dd52010-11-24 18:12:06 +00005260 /* Note the delay and frame clipping boundaries. */
5261
cristy3ed852e2009-09-05 21:47:34 +00005262 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005263
cristybb503372010-05-27 20:51:26 +00005264 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005265 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005266
cristy3ed852e2009-09-05 21:47:34 +00005267 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005268
cristybb503372010-05-27 20:51:26 +00005269 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005270 {
5271 int
5272 change_delay,
5273 change_timeout,
5274 change_clipping;
5275
5276 change_delay=(*p++);
5277 change_timeout=(*p++);
5278 change_clipping=(*p++);
5279 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005280
cristy3ed852e2009-09-05 21:47:34 +00005281 if (change_delay)
5282 {
cristy8182b072010-05-30 20:10:53 +00005283 frame_delay=1UL*image->ticks_per_second*
5284 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005285
cristy8182b072010-05-30 20:10:53 +00005286 if (mng_info->ticks_per_second != 0)
5287 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005288
glennrpbb010dd2010-06-01 13:07:15 +00005289 else
5290 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005291
cristy3ed852e2009-09-05 21:47:34 +00005292 if (change_delay == 2)
5293 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005294
cristy3ed852e2009-09-05 21:47:34 +00005295 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005296
cristy3ed852e2009-09-05 21:47:34 +00005297 if (logging != MagickFalse)
5298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005299 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005300 }
glennrp47b9dd52010-11-24 18:12:06 +00005301
cristy3ed852e2009-09-05 21:47:34 +00005302 if (change_timeout)
5303 {
glennrpbb010dd2010-06-01 13:07:15 +00005304 frame_timeout=1UL*image->ticks_per_second*
5305 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005306
glennrpbb010dd2010-06-01 13:07:15 +00005307 if (mng_info->ticks_per_second != 0)
5308 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005309
glennrpbb010dd2010-06-01 13:07:15 +00005310 else
5311 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005312
cristy3ed852e2009-09-05 21:47:34 +00005313 if (change_delay == 2)
5314 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005315
cristy3ed852e2009-09-05 21:47:34 +00005316 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005317
cristy3ed852e2009-09-05 21:47:34 +00005318 if (logging != MagickFalse)
5319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005320 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005321 }
glennrp47b9dd52010-11-24 18:12:06 +00005322
cristy3ed852e2009-09-05 21:47:34 +00005323 if (change_clipping)
5324 {
5325 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5326 p+=17;
5327 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005328
cristy3ed852e2009-09-05 21:47:34 +00005329 if (logging != MagickFalse)
5330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005331 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005332 (double) fb.left,(double) fb.right,(double) fb.top,
5333 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005334
cristy3ed852e2009-09-05 21:47:34 +00005335 if (change_clipping == 2)
5336 default_fb=fb;
5337 }
5338 }
5339 }
5340 mng_info->clip=fb;
5341 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005342
cristybb503372010-05-27 20:51:26 +00005343 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005344 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005345
cristybb503372010-05-27 20:51:26 +00005346 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005347 -mng_info->clip.top);
5348 /*
5349 Insert a background layer behind the frame if framing_mode is 4.
5350 */
5351#if defined(MNG_INSERT_LAYERS)
5352 if (logging != MagickFalse)
5353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005354 " subframe_width=%.20g, subframe_height=%.20g",(double)
5355 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005356
cristy3ed852e2009-09-05 21:47:34 +00005357 if (insert_layers && (mng_info->framing_mode == 4) &&
5358 (subframe_width) && (subframe_height))
5359 {
glennrp47b9dd52010-11-24 18:12:06 +00005360 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00005361 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5362 {
5363 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005364
cristy3ed852e2009-09-05 21:47:34 +00005365 if (GetNextImageInList(image) == (Image *) NULL)
5366 {
5367 image=DestroyImageList(image);
5368 MngInfoFreeStruct(mng_info,&have_mng_structure);
5369 return((Image *) NULL);
5370 }
glennrp47b9dd52010-11-24 18:12:06 +00005371
cristy3ed852e2009-09-05 21:47:34 +00005372 image=SyncNextImageInList(image);
5373 }
glennrp0fe50b42010-11-16 03:52:51 +00005374
cristy3ed852e2009-09-05 21:47:34 +00005375 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005376
cristy3ed852e2009-09-05 21:47:34 +00005377 if (term_chunk_found)
5378 {
5379 image->start_loop=MagickTrue;
5380 image->iterations=mng_iterations;
5381 term_chunk_found=MagickFalse;
5382 }
glennrp0fe50b42010-11-16 03:52:51 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 else
5385 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005386
cristy3ed852e2009-09-05 21:47:34 +00005387 image->columns=subframe_width;
5388 image->rows=subframe_height;
5389 image->page.width=subframe_width;
5390 image->page.height=subframe_height;
5391 image->page.x=mng_info->clip.left;
5392 image->page.y=mng_info->clip.top;
5393 image->background_color=mng_background_color;
5394 image->matte=MagickFalse;
5395 image->delay=0;
5396 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005397
cristy3ed852e2009-09-05 21:47:34 +00005398 if (logging != MagickFalse)
5399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005400 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005401 (double) mng_info->clip.left,(double) mng_info->clip.right,
5402 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005403 }
5404#endif
5405 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5406 continue;
5407 }
5408 if (memcmp(type,mng_CLIP,4) == 0)
5409 {
5410 unsigned int
5411 first_object,
5412 last_object;
5413
5414 /*
5415 Read CLIP.
5416 */
5417 first_object=(p[0] << 8) | p[1];
5418 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005419
cristy3ed852e2009-09-05 21:47:34 +00005420 for (i=(int) first_object; i <= (int) last_object; i++)
5421 {
5422 if (mng_info->exists[i] && !mng_info->frozen[i])
5423 {
5424 MngBox
5425 box;
5426
5427 box=mng_info->object_clip[i];
5428 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5429 }
5430 }
glennrp47b9dd52010-11-24 18:12:06 +00005431
cristy3ed852e2009-09-05 21:47:34 +00005432 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5433 continue;
5434 }
5435 if (memcmp(type,mng_SAVE,4) == 0)
5436 {
5437 for (i=1; i < MNG_MAX_OBJECTS; i++)
5438 if (mng_info->exists[i])
5439 {
5440 mng_info->frozen[i]=MagickTrue;
5441#ifdef MNG_OBJECT_BUFFERS
5442 if (mng_info->ob[i] != (MngBuffer *) NULL)
5443 mng_info->ob[i]->frozen=MagickTrue;
5444#endif
5445 }
glennrp0fe50b42010-11-16 03:52:51 +00005446
cristy3ed852e2009-09-05 21:47:34 +00005447 if (length)
5448 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005449
cristy3ed852e2009-09-05 21:47:34 +00005450 continue;
5451 }
5452
5453 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5454 {
glennrp47b9dd52010-11-24 18:12:06 +00005455 /* Read DISC or SEEK. */
5456
cristy3ed852e2009-09-05 21:47:34 +00005457 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5458 {
5459 for (i=1; i < MNG_MAX_OBJECTS; i++)
5460 MngInfoDiscardObject(mng_info,i);
5461 }
glennrp0fe50b42010-11-16 03:52:51 +00005462
cristy3ed852e2009-09-05 21:47:34 +00005463 else
5464 {
cristybb503372010-05-27 20:51:26 +00005465 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005466 j;
5467
cristybb503372010-05-27 20:51:26 +00005468 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005469 {
5470 i=p[j] << 8 | p[j+1];
5471 MngInfoDiscardObject(mng_info,i);
5472 }
5473 }
glennrp0fe50b42010-11-16 03:52:51 +00005474
cristy3ed852e2009-09-05 21:47:34 +00005475 if (length)
5476 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005477
cristy3ed852e2009-09-05 21:47:34 +00005478 continue;
5479 }
glennrp47b9dd52010-11-24 18:12:06 +00005480
cristy3ed852e2009-09-05 21:47:34 +00005481 if (memcmp(type,mng_MOVE,4) == 0)
5482 {
cristybb503372010-05-27 20:51:26 +00005483 size_t
cristy3ed852e2009-09-05 21:47:34 +00005484 first_object,
5485 last_object;
5486
glennrp47b9dd52010-11-24 18:12:06 +00005487 /* read MOVE */
5488
cristy3ed852e2009-09-05 21:47:34 +00005489 first_object=(p[0] << 8) | p[1];
5490 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005491 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005492 {
5493 if (mng_info->exists[i] && !mng_info->frozen[i])
5494 {
5495 MngPair
5496 new_pair;
5497
5498 MngPair
5499 old_pair;
5500
5501 old_pair.a=mng_info->x_off[i];
5502 old_pair.b=mng_info->y_off[i];
5503 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5504 mng_info->x_off[i]=new_pair.a;
5505 mng_info->y_off[i]=new_pair.b;
5506 }
5507 }
glennrp47b9dd52010-11-24 18:12:06 +00005508
cristy3ed852e2009-09-05 21:47:34 +00005509 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5510 continue;
5511 }
5512
5513 if (memcmp(type,mng_LOOP,4) == 0)
5514 {
cristybb503372010-05-27 20:51:26 +00005515 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005516 loop_level=chunk[0];
5517 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005518
5519 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005520 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005521
cristy3ed852e2009-09-05 21:47:34 +00005522 if (logging != MagickFalse)
5523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005524 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5525 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005526
cristy3ed852e2009-09-05 21:47:34 +00005527 if (loop_iters == 0)
5528 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005529
cristy3ed852e2009-09-05 21:47:34 +00005530 else
5531 {
5532 mng_info->loop_jump[loop_level]=TellBlob(image);
5533 mng_info->loop_count[loop_level]=loop_iters;
5534 }
glennrp0fe50b42010-11-16 03:52:51 +00005535
cristy3ed852e2009-09-05 21:47:34 +00005536 mng_info->loop_iteration[loop_level]=0;
5537 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5538 continue;
5539 }
glennrp47b9dd52010-11-24 18:12:06 +00005540
cristy3ed852e2009-09-05 21:47:34 +00005541 if (memcmp(type,mng_ENDL,4) == 0)
5542 {
5543 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005544
cristy3ed852e2009-09-05 21:47:34 +00005545 if (skipping_loop > 0)
5546 {
5547 if (skipping_loop == loop_level)
5548 {
5549 /*
5550 Found end of zero-iteration loop.
5551 */
5552 skipping_loop=(-1);
5553 mng_info->loop_active[loop_level]=0;
5554 }
5555 }
glennrp47b9dd52010-11-24 18:12:06 +00005556
cristy3ed852e2009-09-05 21:47:34 +00005557 else
5558 {
5559 if (mng_info->loop_active[loop_level] == 1)
5560 {
5561 mng_info->loop_count[loop_level]--;
5562 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005563
cristy3ed852e2009-09-05 21:47:34 +00005564 if (logging != MagickFalse)
5565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005566 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005567 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005568 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005569
cristy3ed852e2009-09-05 21:47:34 +00005570 if (mng_info->loop_count[loop_level] != 0)
5571 {
5572 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5573 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005574
cristy3ed852e2009-09-05 21:47:34 +00005575 if (offset < 0)
5576 ThrowReaderException(CorruptImageError,
5577 "ImproperImageHeader");
5578 }
glennrp47b9dd52010-11-24 18:12:06 +00005579
cristy3ed852e2009-09-05 21:47:34 +00005580 else
5581 {
5582 short
5583 last_level;
5584
5585 /*
5586 Finished loop.
5587 */
5588 mng_info->loop_active[loop_level]=0;
5589 last_level=(-1);
5590 for (i=0; i < loop_level; i++)
5591 if (mng_info->loop_active[i] == 1)
5592 last_level=(short) i;
5593 loop_level=last_level;
5594 }
5595 }
5596 }
glennrp47b9dd52010-11-24 18:12:06 +00005597
cristy3ed852e2009-09-05 21:47:34 +00005598 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5599 continue;
5600 }
glennrp47b9dd52010-11-24 18:12:06 +00005601
cristy3ed852e2009-09-05 21:47:34 +00005602 if (memcmp(type,mng_CLON,4) == 0)
5603 {
5604 if (mng_info->clon_warning == 0)
5605 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5606 CoderError,"CLON is not implemented yet","`%s'",
5607 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005608
cristy3ed852e2009-09-05 21:47:34 +00005609 mng_info->clon_warning++;
5610 }
glennrp47b9dd52010-11-24 18:12:06 +00005611
cristy3ed852e2009-09-05 21:47:34 +00005612 if (memcmp(type,mng_MAGN,4) == 0)
5613 {
5614 png_uint_16
5615 magn_first,
5616 magn_last,
5617 magn_mb,
5618 magn_ml,
5619 magn_mr,
5620 magn_mt,
5621 magn_mx,
5622 magn_my,
5623 magn_methx,
5624 magn_methy;
5625
5626 if (length > 1)
5627 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005628
cristy3ed852e2009-09-05 21:47:34 +00005629 else
5630 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005631
cristy3ed852e2009-09-05 21:47:34 +00005632 if (length > 3)
5633 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005634
cristy3ed852e2009-09-05 21:47:34 +00005635 else
5636 magn_last=magn_first;
5637#ifndef MNG_OBJECT_BUFFERS
5638 if (magn_first || magn_last)
5639 if (mng_info->magn_warning == 0)
5640 {
5641 (void) ThrowMagickException(&image->exception,
5642 GetMagickModule(),CoderError,
5643 "MAGN is not implemented yet for nonzero objects",
5644 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005645
cristy3ed852e2009-09-05 21:47:34 +00005646 mng_info->magn_warning++;
5647 }
5648#endif
5649 if (length > 4)
5650 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005651
cristy3ed852e2009-09-05 21:47:34 +00005652 else
5653 magn_methx=0;
5654
5655 if (length > 6)
5656 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005657
cristy3ed852e2009-09-05 21:47:34 +00005658 else
5659 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005660
cristy3ed852e2009-09-05 21:47:34 +00005661 if (magn_mx == 0)
5662 magn_mx=1;
5663
5664 if (length > 8)
5665 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005666
cristy3ed852e2009-09-05 21:47:34 +00005667 else
5668 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005669
cristy3ed852e2009-09-05 21:47:34 +00005670 if (magn_my == 0)
5671 magn_my=1;
5672
5673 if (length > 10)
5674 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005675
cristy3ed852e2009-09-05 21:47:34 +00005676 else
5677 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005678
cristy3ed852e2009-09-05 21:47:34 +00005679 if (magn_ml == 0)
5680 magn_ml=1;
5681
5682 if (length > 12)
5683 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005684
cristy3ed852e2009-09-05 21:47:34 +00005685 else
5686 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005687
cristy3ed852e2009-09-05 21:47:34 +00005688 if (magn_mr == 0)
5689 magn_mr=1;
5690
5691 if (length > 14)
5692 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005693
cristy3ed852e2009-09-05 21:47:34 +00005694 else
5695 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005696
cristy3ed852e2009-09-05 21:47:34 +00005697 if (magn_mt == 0)
5698 magn_mt=1;
5699
5700 if (length > 16)
5701 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005702
cristy3ed852e2009-09-05 21:47:34 +00005703 else
5704 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005705
cristy3ed852e2009-09-05 21:47:34 +00005706 if (magn_mb == 0)
5707 magn_mb=1;
5708
5709 if (length > 17)
5710 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005711
cristy3ed852e2009-09-05 21:47:34 +00005712 else
5713 magn_methy=magn_methx;
5714
glennrp47b9dd52010-11-24 18:12:06 +00005715
cristy3ed852e2009-09-05 21:47:34 +00005716 if (magn_methx > 5 || magn_methy > 5)
5717 if (mng_info->magn_warning == 0)
5718 {
5719 (void) ThrowMagickException(&image->exception,
5720 GetMagickModule(),CoderError,
5721 "Unknown MAGN method in MNG datastream","`%s'",
5722 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005723
cristy3ed852e2009-09-05 21:47:34 +00005724 mng_info->magn_warning++;
5725 }
5726#ifdef MNG_OBJECT_BUFFERS
5727 /* Magnify existing objects in the range magn_first to magn_last */
5728#endif
5729 if (magn_first == 0 || magn_last == 0)
5730 {
5731 /* Save the magnification factors for object 0 */
5732 mng_info->magn_mb=magn_mb;
5733 mng_info->magn_ml=magn_ml;
5734 mng_info->magn_mr=magn_mr;
5735 mng_info->magn_mt=magn_mt;
5736 mng_info->magn_mx=magn_mx;
5737 mng_info->magn_my=magn_my;
5738 mng_info->magn_methx=magn_methx;
5739 mng_info->magn_methy=magn_methy;
5740 }
5741 }
glennrp47b9dd52010-11-24 18:12:06 +00005742
cristy3ed852e2009-09-05 21:47:34 +00005743 if (memcmp(type,mng_PAST,4) == 0)
5744 {
5745 if (mng_info->past_warning == 0)
5746 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5747 CoderError,"PAST is not implemented yet","`%s'",
5748 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005749
cristy3ed852e2009-09-05 21:47:34 +00005750 mng_info->past_warning++;
5751 }
glennrp47b9dd52010-11-24 18:12:06 +00005752
cristy3ed852e2009-09-05 21:47:34 +00005753 if (memcmp(type,mng_SHOW,4) == 0)
5754 {
5755 if (mng_info->show_warning == 0)
5756 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5757 CoderError,"SHOW is not implemented yet","`%s'",
5758 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005759
cristy3ed852e2009-09-05 21:47:34 +00005760 mng_info->show_warning++;
5761 }
glennrp47b9dd52010-11-24 18:12:06 +00005762
cristy3ed852e2009-09-05 21:47:34 +00005763 if (memcmp(type,mng_sBIT,4) == 0)
5764 {
5765 if (length < 4)
5766 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005767
cristy3ed852e2009-09-05 21:47:34 +00005768 else
5769 {
5770 mng_info->global_sbit.gray=p[0];
5771 mng_info->global_sbit.red=p[0];
5772 mng_info->global_sbit.green=p[1];
5773 mng_info->global_sbit.blue=p[2];
5774 mng_info->global_sbit.alpha=p[3];
5775 mng_info->have_global_sbit=MagickTrue;
5776 }
5777 }
5778 if (memcmp(type,mng_pHYs,4) == 0)
5779 {
5780 if (length > 8)
5781 {
5782 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005783 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005784 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005785 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005786 mng_info->global_phys_unit_type=p[8];
5787 mng_info->have_global_phys=MagickTrue;
5788 }
glennrp47b9dd52010-11-24 18:12:06 +00005789
cristy3ed852e2009-09-05 21:47:34 +00005790 else
5791 mng_info->have_global_phys=MagickFalse;
5792 }
5793 if (memcmp(type,mng_pHYg,4) == 0)
5794 {
5795 if (mng_info->phyg_warning == 0)
5796 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5797 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005798
cristy3ed852e2009-09-05 21:47:34 +00005799 mng_info->phyg_warning++;
5800 }
5801 if (memcmp(type,mng_BASI,4) == 0)
5802 {
5803 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005804
cristy3ed852e2009-09-05 21:47:34 +00005805 if (mng_info->basi_warning == 0)
5806 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5807 CoderError,"BASI is not implemented yet","`%s'",
5808 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005809
cristy3ed852e2009-09-05 21:47:34 +00005810 mng_info->basi_warning++;
5811#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005812 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005813 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005814 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005815 (p[6] << 8) | p[7]);
5816 basi_color_type=p[8];
5817 basi_compression_method=p[9];
5818 basi_filter_type=p[10];
5819 basi_interlace_method=p[11];
5820 if (length > 11)
5821 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005822
cristy3ed852e2009-09-05 21:47:34 +00005823 else
5824 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005825
cristy3ed852e2009-09-05 21:47:34 +00005826 if (length > 13)
5827 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005828
cristy3ed852e2009-09-05 21:47:34 +00005829 else
5830 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005831
cristy3ed852e2009-09-05 21:47:34 +00005832 if (length > 15)
5833 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristy3ed852e2009-09-05 21:47:34 +00005835 else
5836 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005837
cristy3ed852e2009-09-05 21:47:34 +00005838 if (length > 17)
5839 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005840
cristy3ed852e2009-09-05 21:47:34 +00005841 else
5842 {
5843 if (basi_sample_depth == 16)
5844 basi_alpha=65535L;
5845 else
5846 basi_alpha=255;
5847 }
glennrp47b9dd52010-11-24 18:12:06 +00005848
cristy3ed852e2009-09-05 21:47:34 +00005849 if (length > 19)
5850 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005851
cristy3ed852e2009-09-05 21:47:34 +00005852 else
5853 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005854
cristy3ed852e2009-09-05 21:47:34 +00005855#endif
5856 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5857 continue;
5858 }
glennrp47b9dd52010-11-24 18:12:06 +00005859
cristy3ed852e2009-09-05 21:47:34 +00005860 if (memcmp(type,mng_IHDR,4)
5861#if defined(JNG_SUPPORTED)
5862 && memcmp(type,mng_JHDR,4)
5863#endif
5864 )
5865 {
5866 /* Not an IHDR or JHDR chunk */
5867 if (length)
5868 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005869
cristy3ed852e2009-09-05 21:47:34 +00005870 continue;
5871 }
5872/* Process IHDR */
5873 if (logging != MagickFalse)
5874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5875 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005876
cristy3ed852e2009-09-05 21:47:34 +00005877 mng_info->exists[object_id]=MagickTrue;
5878 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005879
cristy3ed852e2009-09-05 21:47:34 +00005880 if (mng_info->invisible[object_id])
5881 {
5882 if (logging != MagickFalse)
5883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5884 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005885
cristy3ed852e2009-09-05 21:47:34 +00005886 skip_to_iend=MagickTrue;
5887 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5888 continue;
5889 }
5890#if defined(MNG_INSERT_LAYERS)
5891 if (length < 8)
5892 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005893
cristy8182b072010-05-30 20:10:53 +00005894 image_width=(size_t) mng_get_long(p);
5895 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005896#endif
5897 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5898
5899 /*
5900 Insert a transparent background layer behind the entire animation
5901 if it is not full screen.
5902 */
5903#if defined(MNG_INSERT_LAYERS)
5904 if (insert_layers && mng_type && first_mng_object)
5905 {
5906 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5907 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005908 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005909 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005910 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005911 {
5912 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5913 {
5914 /*
5915 Allocate next image structure.
5916 */
5917 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005918
cristy3ed852e2009-09-05 21:47:34 +00005919 if (GetNextImageInList(image) == (Image *) NULL)
5920 {
5921 image=DestroyImageList(image);
5922 MngInfoFreeStruct(mng_info,&have_mng_structure);
5923 return((Image *) NULL);
5924 }
glennrp47b9dd52010-11-24 18:12:06 +00005925
cristy3ed852e2009-09-05 21:47:34 +00005926 image=SyncNextImageInList(image);
5927 }
5928 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005929
cristy3ed852e2009-09-05 21:47:34 +00005930 if (term_chunk_found)
5931 {
5932 image->start_loop=MagickTrue;
5933 image->iterations=mng_iterations;
5934 term_chunk_found=MagickFalse;
5935 }
glennrp47b9dd52010-11-24 18:12:06 +00005936
cristy3ed852e2009-09-05 21:47:34 +00005937 else
5938 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005939
5940 /* Make a background rectangle. */
5941
cristy3ed852e2009-09-05 21:47:34 +00005942 image->delay=0;
5943 image->columns=mng_info->mng_width;
5944 image->rows=mng_info->mng_height;
5945 image->page.width=mng_info->mng_width;
5946 image->page.height=mng_info->mng_height;
5947 image->page.x=0;
5948 image->page.y=0;
5949 image->background_color=mng_background_color;
5950 (void) SetImageBackgroundColor(image);
5951 if (logging != MagickFalse)
5952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005953 " Inserted transparent background layer, W=%.20g, H=%.20g",
5954 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005955 }
5956 }
5957 /*
5958 Insert a background layer behind the upcoming image if
5959 framing_mode is 3, and we haven't already inserted one.
5960 */
5961 if (insert_layers && (mng_info->framing_mode == 3) &&
5962 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5963 (simplicity & 0x08)))
5964 {
5965 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
5966 {
5967 /*
5968 Allocate next image structure.
5969 */
5970 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005971
cristy3ed852e2009-09-05 21:47:34 +00005972 if (GetNextImageInList(image) == (Image *) NULL)
5973 {
5974 image=DestroyImageList(image);
5975 MngInfoFreeStruct(mng_info,&have_mng_structure);
5976 return((Image *) NULL);
5977 }
glennrp47b9dd52010-11-24 18:12:06 +00005978
cristy3ed852e2009-09-05 21:47:34 +00005979 image=SyncNextImageInList(image);
5980 }
glennrp0fe50b42010-11-16 03:52:51 +00005981
cristy3ed852e2009-09-05 21:47:34 +00005982 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005983
cristy3ed852e2009-09-05 21:47:34 +00005984 if (term_chunk_found)
5985 {
5986 image->start_loop=MagickTrue;
5987 image->iterations=mng_iterations;
5988 term_chunk_found=MagickFalse;
5989 }
glennrp0fe50b42010-11-16 03:52:51 +00005990
cristy3ed852e2009-09-05 21:47:34 +00005991 else
5992 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005993
cristy3ed852e2009-09-05 21:47:34 +00005994 image->delay=0;
5995 image->columns=subframe_width;
5996 image->rows=subframe_height;
5997 image->page.width=subframe_width;
5998 image->page.height=subframe_height;
5999 image->page.x=mng_info->clip.left;
6000 image->page.y=mng_info->clip.top;
6001 image->background_color=mng_background_color;
6002 image->matte=MagickFalse;
6003 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006004
cristy3ed852e2009-09-05 21:47:34 +00006005 if (logging != MagickFalse)
6006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006007 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006008 (double) mng_info->clip.left,(double) mng_info->clip.right,
6009 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006010 }
6011#endif /* MNG_INSERT_LAYERS */
6012 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006013
cristy3ed852e2009-09-05 21:47:34 +00006014 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6015 {
6016 /*
6017 Allocate next image structure.
6018 */
6019 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00006020
cristy3ed852e2009-09-05 21:47:34 +00006021 if (GetNextImageInList(image) == (Image *) NULL)
6022 {
6023 image=DestroyImageList(image);
6024 MngInfoFreeStruct(mng_info,&have_mng_structure);
6025 return((Image *) NULL);
6026 }
glennrp47b9dd52010-11-24 18:12:06 +00006027
cristy3ed852e2009-09-05 21:47:34 +00006028 image=SyncNextImageInList(image);
6029 }
6030 mng_info->image=image;
6031 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6032 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006033
cristy3ed852e2009-09-05 21:47:34 +00006034 if (status == MagickFalse)
6035 break;
glennrp0fe50b42010-11-16 03:52:51 +00006036
cristy3ed852e2009-09-05 21:47:34 +00006037 if (term_chunk_found)
6038 {
6039 image->start_loop=MagickTrue;
6040 term_chunk_found=MagickFalse;
6041 }
glennrp0fe50b42010-11-16 03:52:51 +00006042
cristy3ed852e2009-09-05 21:47:34 +00006043 else
6044 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006045
cristy3ed852e2009-09-05 21:47:34 +00006046 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6047 {
6048 image->delay=frame_delay;
6049 frame_delay=default_frame_delay;
6050 }
glennrp0fe50b42010-11-16 03:52:51 +00006051
cristy3ed852e2009-09-05 21:47:34 +00006052 else
6053 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006054
cristy3ed852e2009-09-05 21:47:34 +00006055 image->page.width=mng_info->mng_width;
6056 image->page.height=mng_info->mng_height;
6057 image->page.x=mng_info->x_off[object_id];
6058 image->page.y=mng_info->y_off[object_id];
6059 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006060
cristy3ed852e2009-09-05 21:47:34 +00006061 /*
6062 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6063 */
glennrp47b9dd52010-11-24 18:12:06 +00006064
cristy3ed852e2009-09-05 21:47:34 +00006065 if (logging != MagickFalse)
6066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6067 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6068 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006069
cristybb503372010-05-27 20:51:26 +00006070 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006071
cristy3ed852e2009-09-05 21:47:34 +00006072 if (offset < 0)
6073 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6074 }
6075
6076 previous=image;
6077 mng_info->image=image;
6078 mng_info->mng_type=mng_type;
6079 mng_info->object_id=object_id;
6080
6081 if (memcmp(type,mng_IHDR,4) == 0)
6082 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006083
cristy3ed852e2009-09-05 21:47:34 +00006084#if defined(JNG_SUPPORTED)
6085 else
6086 image=ReadOneJNGImage(mng_info,image_info,exception);
6087#endif
6088
6089 if (image == (Image *) NULL)
6090 {
6091 if (IsImageObject(previous) != MagickFalse)
6092 {
6093 (void) DestroyImageList(previous);
6094 (void) CloseBlob(previous);
6095 }
glennrp47b9dd52010-11-24 18:12:06 +00006096
cristy3ed852e2009-09-05 21:47:34 +00006097 MngInfoFreeStruct(mng_info,&have_mng_structure);
6098 return((Image *) NULL);
6099 }
glennrp0fe50b42010-11-16 03:52:51 +00006100
cristy3ed852e2009-09-05 21:47:34 +00006101 if (image->columns == 0 || image->rows == 0)
6102 {
6103 (void) CloseBlob(image);
6104 image=DestroyImageList(image);
6105 MngInfoFreeStruct(mng_info,&have_mng_structure);
6106 return((Image *) NULL);
6107 }
glennrp0fe50b42010-11-16 03:52:51 +00006108
cristy3ed852e2009-09-05 21:47:34 +00006109 mng_info->image=image;
6110
6111 if (mng_type)
6112 {
6113 MngBox
6114 crop_box;
6115
6116 if (mng_info->magn_methx || mng_info->magn_methy)
6117 {
6118 png_uint_32
6119 magnified_height,
6120 magnified_width;
6121
6122 if (logging != MagickFalse)
6123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6124 " Processing MNG MAGN chunk");
6125
6126 if (mng_info->magn_methx == 1)
6127 {
6128 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006129
cristy3ed852e2009-09-05 21:47:34 +00006130 if (image->columns > 1)
6131 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006132
cristy3ed852e2009-09-05 21:47:34 +00006133 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006134 magnified_width += (png_uint_32)
6135 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006136 }
glennrp47b9dd52010-11-24 18:12:06 +00006137
cristy3ed852e2009-09-05 21:47:34 +00006138 else
6139 {
cristy4e5bc842010-06-09 13:56:01 +00006140 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006141
cristy3ed852e2009-09-05 21:47:34 +00006142 if (image->columns > 1)
6143 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006144
cristy3ed852e2009-09-05 21:47:34 +00006145 if (image->columns > 2)
6146 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006147
cristy3ed852e2009-09-05 21:47:34 +00006148 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006149 magnified_width += (png_uint_32)
6150 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006151 }
glennrp47b9dd52010-11-24 18:12:06 +00006152
cristy3ed852e2009-09-05 21:47:34 +00006153 if (mng_info->magn_methy == 1)
6154 {
6155 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006156
cristy3ed852e2009-09-05 21:47:34 +00006157 if (image->rows > 1)
6158 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006159
cristy3ed852e2009-09-05 21:47:34 +00006160 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006161 magnified_height += (png_uint_32)
6162 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006163 }
glennrp47b9dd52010-11-24 18:12:06 +00006164
cristy3ed852e2009-09-05 21:47:34 +00006165 else
6166 {
cristy4e5bc842010-06-09 13:56:01 +00006167 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006168
cristy3ed852e2009-09-05 21:47:34 +00006169 if (image->rows > 1)
6170 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006171
cristy3ed852e2009-09-05 21:47:34 +00006172 if (image->rows > 2)
6173 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006174
cristy3ed852e2009-09-05 21:47:34 +00006175 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006176 magnified_height += (png_uint_32)
6177 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006178 }
glennrp47b9dd52010-11-24 18:12:06 +00006179
cristy3ed852e2009-09-05 21:47:34 +00006180 if (magnified_height > image->rows ||
6181 magnified_width > image->columns)
6182 {
6183 Image
6184 *large_image;
6185
6186 int
6187 yy;
6188
cristybb503372010-05-27 20:51:26 +00006189 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006190 m,
6191 y;
6192
cristybb503372010-05-27 20:51:26 +00006193 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00006194 x;
6195
6196 register PixelPacket
6197 *n,
6198 *q;
6199
6200 PixelPacket
6201 *next,
6202 *prev;
6203
6204 png_uint_16
6205 magn_methx,
6206 magn_methy;
6207
glennrp47b9dd52010-11-24 18:12:06 +00006208 /* Allocate next image structure. */
6209
cristy3ed852e2009-09-05 21:47:34 +00006210 if (logging != MagickFalse)
6211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6212 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006213
cristy3ed852e2009-09-05 21:47:34 +00006214 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00006215
cristy3ed852e2009-09-05 21:47:34 +00006216 if (GetNextImageInList(image) == (Image *) NULL)
6217 {
6218 image=DestroyImageList(image);
6219 MngInfoFreeStruct(mng_info,&have_mng_structure);
6220 return((Image *) NULL);
6221 }
6222
6223 large_image=SyncNextImageInList(image);
6224
6225 large_image->columns=magnified_width;
6226 large_image->rows=magnified_height;
6227
6228 magn_methx=mng_info->magn_methx;
6229 magn_methy=mng_info->magn_methy;
6230
glennrp3faa9a32011-04-23 14:00:25 +00006231#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006232#define QM unsigned short
6233 if (magn_methx != 1 || magn_methy != 1)
6234 {
6235 /*
6236 Scale pixels to unsigned shorts to prevent
6237 overflow of intermediate values of interpolations
6238 */
cristybb503372010-05-27 20:51:26 +00006239 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006240 {
6241 q=GetAuthenticPixels(image,0,y,image->columns,1,
6242 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006243
cristybb503372010-05-27 20:51:26 +00006244 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006245 {
glennrp7c7b3152011-04-26 04:01:27 +00006246 SetRedPixelComponent(q,ScaleQuantumToShort(
cristy3b6fd2e2011-05-20 12:53:50 +00006247 GetRedPixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006248 SetGreenPixelComponent(q,ScaleQuantumToShort(
cristy3b6fd2e2011-05-20 12:53:50 +00006249 GetGreenPixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006250 SetBluePixelComponent(q,ScaleQuantumToShort(
cristy3b6fd2e2011-05-20 12:53:50 +00006251 GetBluePixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006252 SetOpacityPixelComponent(q,ScaleQuantumToShort(
cristy3b6fd2e2011-05-20 12:53:50 +00006253 GetOpacityPixelComponent(q)));
cristy3ed852e2009-09-05 21:47:34 +00006254 q++;
6255 }
glennrp47b9dd52010-11-24 18:12:06 +00006256
cristy3ed852e2009-09-05 21:47:34 +00006257 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6258 break;
6259 }
6260 }
6261#else
6262#define QM Quantum
6263#endif
6264
6265 if (image->matte != MagickFalse)
6266 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006267
cristy3ed852e2009-09-05 21:47:34 +00006268 else
6269 {
6270 large_image->background_color.opacity=OpaqueOpacity;
6271 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006272
cristy3ed852e2009-09-05 21:47:34 +00006273 if (magn_methx == 4)
6274 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006275
cristy3ed852e2009-09-05 21:47:34 +00006276 if (magn_methx == 5)
6277 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006278
cristy3ed852e2009-09-05 21:47:34 +00006279 if (magn_methy == 4)
6280 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006281
cristy3ed852e2009-09-05 21:47:34 +00006282 if (magn_methy == 5)
6283 magn_methy=3;
6284 }
6285
6286 /* magnify the rows into the right side of the large image */
6287
6288 if (logging != MagickFalse)
6289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006290 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006291 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006292 yy=0;
6293 length=(size_t) image->columns;
6294 next=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*next));
6295 prev=(PixelPacket *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006296
cristy3ed852e2009-09-05 21:47:34 +00006297 if ((prev == (PixelPacket *) NULL) ||
6298 (next == (PixelPacket *) NULL))
6299 {
6300 image=DestroyImageList(image);
6301 MngInfoFreeStruct(mng_info,&have_mng_structure);
6302 ThrowReaderException(ResourceLimitError,
6303 "MemoryAllocationFailed");
6304 }
glennrp47b9dd52010-11-24 18:12:06 +00006305
cristy3ed852e2009-09-05 21:47:34 +00006306 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6307 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006308
cristybb503372010-05-27 20:51:26 +00006309 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006310 {
6311 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006312 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006313
cristybb503372010-05-27 20:51:26 +00006314 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6315 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006316
cristybb503372010-05-27 20:51:26 +00006317 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6318 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006319
cristybb503372010-05-27 20:51:26 +00006320 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006321 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006322
cristy3ed852e2009-09-05 21:47:34 +00006323 else
cristybb503372010-05-27 20:51:26 +00006324 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006325
cristy3ed852e2009-09-05 21:47:34 +00006326 n=prev;
6327 prev=next;
6328 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006329
cristybb503372010-05-27 20:51:26 +00006330 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006331 {
6332 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6333 exception);
6334 (void) CopyMagickMemory(next,n,length);
6335 }
glennrp47b9dd52010-11-24 18:12:06 +00006336
cristy3ed852e2009-09-05 21:47:34 +00006337 for (i=0; i < m; i++, yy++)
6338 {
6339 register PixelPacket
6340 *pixels;
6341
cristybb503372010-05-27 20:51:26 +00006342 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006343 pixels=prev;
6344 n=next;
6345 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006346 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00006347 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00006348
cristybb503372010-05-27 20:51:26 +00006349 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006350 {
glennrpfd05d622011-02-25 04:10:33 +00006351 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006352 /*
6353 if (image->storage_class == PseudoClass)
6354 {
6355 }
6356 */
6357
6358 if (magn_methy <= 1)
6359 {
glennrpbb4f99d2011-05-22 11:13:17 +00006360 /* replicate previous */
6361 SetRGBOPixelComponent(q,(pixels));
cristy3ed852e2009-09-05 21:47:34 +00006362 }
glennrp47b9dd52010-11-24 18:12:06 +00006363
cristy3ed852e2009-09-05 21:47:34 +00006364 else if (magn_methy == 2 || magn_methy == 4)
6365 {
6366 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006367 {
6368 SetRGBOPixelComponent(q,(pixels));
6369 }
glennrp47b9dd52010-11-24 18:12:06 +00006370
cristy3ed852e2009-09-05 21:47:34 +00006371 else
6372 {
6373 /* Interpolate */
glennrpbb4f99d2011-05-22 11:13:17 +00006374 SetRedPixelComponent(q,
6375 ((QM) (((ssize_t)
6376 (2*i*(GetRedPixelComponent(n)
6377 -GetRedPixelComponent(pixels)+m))/
6378 ((ssize_t) (m*2))
6379 +GetRedPixelComponent(pixels)))));
6380 SetGreenPixelComponent(q,
6381 ((QM) (((ssize_t)
6382 (2*i*(GetGreenPixelComponent(n)
6383 -GetGreenPixelComponent(pixels)+m))/
6384 ((ssize_t) (m*2))
6385 +GetGreenPixelComponent(pixels)))));
6386 SetBluePixelComponent(q,
6387 ((QM) (((ssize_t)
6388 (2*i*(GetBluePixelComponent(n)
6389 -GetBluePixelComponent(pixels)+m))/
6390 ((ssize_t) (m*2))
6391 +GetBluePixelComponent(pixels)))));
glennrp47b9dd52010-11-24 18:12:06 +00006392
cristy3ed852e2009-09-05 21:47:34 +00006393 if (image->matte != MagickFalse)
glennrpbb4f99d2011-05-22 11:13:17 +00006394 SetOpacityPixelComponent(q,
6395 ((QM) (((ssize_t)
6396 (2*i*(GetOpacityPixelComponent(n)
6397 -GetOpacityPixelComponent(pixels)+m))
6398 /((ssize_t) (m*2))+
6399 GetOpacityPixelComponent(pixels)))));
cristy3ed852e2009-09-05 21:47:34 +00006400 }
glennrp47b9dd52010-11-24 18:12:06 +00006401
cristy3ed852e2009-09-05 21:47:34 +00006402 if (magn_methy == 4)
6403 {
6404 /* Replicate nearest */
6405 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006406 SetOpacityPixelComponent(q,
6407 (*pixels).opacity+0);
cristy3ed852e2009-09-05 21:47:34 +00006408 else
glennrpbb4f99d2011-05-22 11:13:17 +00006409 SetOpacityPixelComponent(q,
6410 (*n).opacity+0);
cristy3ed852e2009-09-05 21:47:34 +00006411 }
6412 }
glennrp47b9dd52010-11-24 18:12:06 +00006413
cristy3ed852e2009-09-05 21:47:34 +00006414 else /* if (magn_methy == 3 || magn_methy == 5) */
6415 {
6416 /* Replicate nearest */
6417 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006418 {
6419 SetRGBOPixelComponent(q,(pixels));
6420 }
glennrp47b9dd52010-11-24 18:12:06 +00006421
cristy3ed852e2009-09-05 21:47:34 +00006422 else
glennrpbb4f99d2011-05-22 11:13:17 +00006423 {
6424 SetRGBOPixelComponent(q,(n));
6425 }
glennrp47b9dd52010-11-24 18:12:06 +00006426
cristy3ed852e2009-09-05 21:47:34 +00006427 if (magn_methy == 5)
6428 {
glennrpbb4f99d2011-05-22 11:13:17 +00006429 SetOpacityPixelComponent(q,
6430 (QM) (((ssize_t) (2*i*
6431 (GetOpacityPixelComponent(n)
6432 -GetOpacityPixelComponent(pixels))
6433 +m))/((ssize_t) (m*2))
6434 +GetOpacityPixelComponent(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006435 }
6436 }
6437 n++;
6438 q++;
6439 pixels++;
6440 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006441
cristy3ed852e2009-09-05 21:47:34 +00006442 if (SyncAuthenticPixels(large_image,exception) == 0)
6443 break;
glennrp47b9dd52010-11-24 18:12:06 +00006444
cristy3ed852e2009-09-05 21:47:34 +00006445 } /* i */
6446 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006447
cristy3ed852e2009-09-05 21:47:34 +00006448 prev=(PixelPacket *) RelinquishMagickMemory(prev);
6449 next=(PixelPacket *) RelinquishMagickMemory(next);
6450
6451 length=image->columns;
6452
6453 if (logging != MagickFalse)
6454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6455 " Delete original image");
6456
6457 DeleteImageFromList(&image);
6458
6459 image=large_image;
6460
6461 mng_info->image=image;
6462
6463 /* magnify the columns */
6464 if (logging != MagickFalse)
6465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006466 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006467
cristybb503372010-05-27 20:51:26 +00006468 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006469 {
6470 register PixelPacket
6471 *pixels;
6472
6473 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6474 pixels=q+(image->columns-length);
6475 n=pixels+1;
glennrp47b9dd52010-11-24 18:12:06 +00006476
cristybb503372010-05-27 20:51:26 +00006477 for (x=(ssize_t) (image->columns-length);
6478 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006479 {
glennrp7c7b3152011-04-26 04:01:27 +00006480 /* To do: Rewrite using Get/Set***PixelComponent() */
6481
cristybb503372010-05-27 20:51:26 +00006482 if (x == (ssize_t) (image->columns-length))
6483 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006484
cristybb503372010-05-27 20:51:26 +00006485 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6486 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006487
cristybb503372010-05-27 20:51:26 +00006488 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6489 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006490
cristybb503372010-05-27 20:51:26 +00006491 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006492 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006493
cristy3ed852e2009-09-05 21:47:34 +00006494 else
cristybb503372010-05-27 20:51:26 +00006495 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006496
cristy3ed852e2009-09-05 21:47:34 +00006497 for (i=0; i < m; i++)
6498 {
6499 if (magn_methx <= 1)
6500 {
6501 /* replicate previous */
glennrpbb4f99d2011-05-22 11:13:17 +00006502 SetRGBOPixelComponent(q,(pixels));
cristy3ed852e2009-09-05 21:47:34 +00006503 }
glennrp47b9dd52010-11-24 18:12:06 +00006504
cristy3ed852e2009-09-05 21:47:34 +00006505 else if (magn_methx == 2 || magn_methx == 4)
6506 {
6507 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006508 {
6509 SetRGBOPixelComponent(q,(pixels));
6510 }
glennrp47b9dd52010-11-24 18:12:06 +00006511
glennrpbb4f99d2011-05-22 11:13:17 +00006512 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00006513 else
6514 {
6515 /* Interpolate */
glennrpbb4f99d2011-05-22 11:13:17 +00006516 SetRedPixelComponent(q,
6517 (QM) ((2*i*(
6518 GetRedPixelComponent(n)
6519 -GetRedPixelComponent(pixels))+m)
6520 /((ssize_t) (m*2))+
6521 GetRedPixelComponent(pixels)));
6522
6523 SetGreenPixelComponent(q,
6524 (QM) ((2*i*(
6525 GetGreenPixelComponent(n)
6526 -GetGreenPixelComponent(pixels))+m)
6527 /((ssize_t) (m*2))+
6528 GetGreenPixelComponent(pixels)));
6529
6530 SetBluePixelComponent(q,
6531 (QM) ((2*i*(
6532 GetBluePixelComponent(n)
6533 -GetBluePixelComponent(pixels))+m)
6534 /((ssize_t) (m*2))+
6535 GetBluePixelComponent(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006536 if (image->matte != MagickFalse)
glennrpbb4f99d2011-05-22 11:13:17 +00006537 SetOpacityPixelComponent(q,
6538 (QM) ((2*i*(
6539 GetOpacityPixelComponent(n)
6540 -GetOpacityPixelComponent(pixels))+m)
6541 /((ssize_t) (m*2))+
6542 GetOpacityPixelComponent(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006543 }
glennrp47b9dd52010-11-24 18:12:06 +00006544
cristy3ed852e2009-09-05 21:47:34 +00006545 if (magn_methx == 4)
6546 {
6547 /* Replicate nearest */
6548 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006549 {
6550 SetOpacityPixelComponent(q,
6551 GetOpacityPixelComponent(pixels)+0);
6552 }
cristy3ed852e2009-09-05 21:47:34 +00006553 else
glennrpbb4f99d2011-05-22 11:13:17 +00006554 {
6555 SetOpacityPixelComponent(q,
6556 GetOpacityPixelComponent(n)+0);
6557 }
cristy3ed852e2009-09-05 21:47:34 +00006558 }
6559 }
glennrp47b9dd52010-11-24 18:12:06 +00006560
cristy3ed852e2009-09-05 21:47:34 +00006561 else /* if (magn_methx == 3 || magn_methx == 5) */
6562 {
6563 /* Replicate nearest */
6564 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006565 {
6566 SetRGBOPixelComponent(q,(pixels));
6567 }
glennrp47b9dd52010-11-24 18:12:06 +00006568
cristy3ed852e2009-09-05 21:47:34 +00006569 else
glennrpbb4f99d2011-05-22 11:13:17 +00006570 {
6571 SetRGBOPixelComponent(q,(n));
6572 }
glennrp47b9dd52010-11-24 18:12:06 +00006573
cristy3ed852e2009-09-05 21:47:34 +00006574 if (magn_methx == 5)
6575 {
6576 /* Interpolate */
glennrpbb4f99d2011-05-22 11:13:17 +00006577 SetOpacityPixelComponent(q,
6578 (QM) ((2*i*( GetOpacityPixelComponent(n)
6579 -GetOpacityPixelComponent(pixels))+m)/
6580 ((ssize_t) (m*2))
6581 +GetOpacityPixelComponent(pixels)));
cristy3ed852e2009-09-05 21:47:34 +00006582 }
6583 }
6584 q++;
6585 }
6586 n++;
6587 p++;
6588 }
glennrp47b9dd52010-11-24 18:12:06 +00006589
cristy3ed852e2009-09-05 21:47:34 +00006590 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6591 break;
6592 }
glennrp3faa9a32011-04-23 14:00:25 +00006593#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006594 if (magn_methx != 1 || magn_methy != 1)
6595 {
6596 /*
6597 Rescale pixels to Quantum
6598 */
cristybb503372010-05-27 20:51:26 +00006599 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006600 {
6601 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006602
cristybb503372010-05-27 20:51:26 +00006603 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006604 {
glennrp7c7b3152011-04-26 04:01:27 +00006605 SetRedPixelComponent(q,ScaleShortToQuantum(
cristy3b6fd2e2011-05-20 12:53:50 +00006606 GetRedPixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006607 SetGreenPixelComponent(q,ScaleShortToQuantum(
cristy3b6fd2e2011-05-20 12:53:50 +00006608 GetGreenPixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006609 SetBluePixelComponent(q,ScaleShortToQuantum(
cristy3b6fd2e2011-05-20 12:53:50 +00006610 GetBluePixelComponent(q)));
glennrp7c7b3152011-04-26 04:01:27 +00006611 SetOpacityPixelComponent(q,ScaleShortToQuantum(
cristy3b6fd2e2011-05-20 12:53:50 +00006612 GetOpacityPixelComponent(q)));
cristy3ed852e2009-09-05 21:47:34 +00006613 q++;
6614 }
glennrp47b9dd52010-11-24 18:12:06 +00006615
cristy3ed852e2009-09-05 21:47:34 +00006616 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6617 break;
6618 }
6619 }
6620#endif
6621 if (logging != MagickFalse)
6622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6623 " Finished MAGN processing");
6624 }
6625 }
6626
6627 /*
6628 Crop_box is with respect to the upper left corner of the MNG.
6629 */
6630 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6631 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6632 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6633 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6634 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6635 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6636 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6637 if ((crop_box.left != (mng_info->image_box.left
6638 +mng_info->x_off[object_id])) ||
6639 (crop_box.right != (mng_info->image_box.right
6640 +mng_info->x_off[object_id])) ||
6641 (crop_box.top != (mng_info->image_box.top
6642 +mng_info->y_off[object_id])) ||
6643 (crop_box.bottom != (mng_info->image_box.bottom
6644 +mng_info->y_off[object_id])))
6645 {
6646 if (logging != MagickFalse)
6647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6648 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006649
cristy3ed852e2009-09-05 21:47:34 +00006650 if ((crop_box.left < crop_box.right) &&
6651 (crop_box.top < crop_box.bottom))
6652 {
6653 Image
6654 *im;
6655
6656 RectangleInfo
6657 crop_info;
6658
6659 /*
6660 Crop_info is with respect to the upper left corner of
6661 the image.
6662 */
6663 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6664 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006665 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6666 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006667 image->page.width=image->columns;
6668 image->page.height=image->rows;
6669 image->page.x=0;
6670 image->page.y=0;
6671 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006672
cristy3ed852e2009-09-05 21:47:34 +00006673 if (im != (Image *) NULL)
6674 {
6675 image->columns=im->columns;
6676 image->rows=im->rows;
6677 im=DestroyImage(im);
6678 image->page.width=image->columns;
6679 image->page.height=image->rows;
6680 image->page.x=crop_box.left;
6681 image->page.y=crop_box.top;
6682 }
6683 }
glennrp47b9dd52010-11-24 18:12:06 +00006684
cristy3ed852e2009-09-05 21:47:34 +00006685 else
6686 {
6687 /*
6688 No pixels in crop area. The MNG spec still requires
6689 a layer, though, so make a single transparent pixel in
6690 the top left corner.
6691 */
6692 image->columns=1;
6693 image->rows=1;
6694 image->colors=2;
6695 (void) SetImageBackgroundColor(image);
6696 image->page.width=1;
6697 image->page.height=1;
6698 image->page.x=0;
6699 image->page.y=0;
6700 }
6701 }
6702#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6703 image=mng_info->image;
6704#endif
6705 }
6706
glennrp2b013e42010-11-24 16:55:50 +00006707#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6708 /* PNG does not handle depths greater than 16 so reduce it even
6709 * if lossy
6710 */
6711 if (image->depth > 16)
6712 image->depth=16;
6713#endif
6714
glennrp3faa9a32011-04-23 14:00:25 +00006715#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006716 if (LosslessReduceDepthOK(image) != MagickFalse)
6717 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006718#endif
glennrpd6afd542010-11-19 01:53:05 +00006719
cristy3ed852e2009-09-05 21:47:34 +00006720 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006721
cristy3ed852e2009-09-05 21:47:34 +00006722 if (image_info->number_scenes != 0)
6723 {
6724 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006725 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006726 break;
6727 }
glennrpd6afd542010-11-19 01:53:05 +00006728
cristy3ed852e2009-09-05 21:47:34 +00006729 if (logging != MagickFalse)
6730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6731 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006732
cristy3ed852e2009-09-05 21:47:34 +00006733 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006734
cristy3ed852e2009-09-05 21:47:34 +00006735 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006736
cristy3ed852e2009-09-05 21:47:34 +00006737 if (logging != MagickFalse)
6738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6739 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006740
cristy3ed852e2009-09-05 21:47:34 +00006741#if defined(MNG_INSERT_LAYERS)
6742 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6743 (mng_info->mng_height))
6744 {
6745 /*
6746 Insert a background layer if nothing else was found.
6747 */
6748 if (logging != MagickFalse)
6749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6750 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006751
cristy3ed852e2009-09-05 21:47:34 +00006752 if (GetAuthenticPixelQueue(image) != (PixelPacket *) NULL)
6753 {
6754 /*
6755 Allocate next image structure.
6756 */
6757 AcquireNextImage(image_info,image);
6758 if (GetNextImageInList(image) == (Image *) NULL)
6759 {
6760 image=DestroyImageList(image);
6761 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006762
cristy3ed852e2009-09-05 21:47:34 +00006763 if (logging != MagickFalse)
6764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6765 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006766
cristy3ed852e2009-09-05 21:47:34 +00006767 return((Image *) NULL);
6768 }
6769 image=SyncNextImageInList(image);
6770 }
6771 image->columns=mng_info->mng_width;
6772 image->rows=mng_info->mng_height;
6773 image->page.width=mng_info->mng_width;
6774 image->page.height=mng_info->mng_height;
6775 image->page.x=0;
6776 image->page.y=0;
6777 image->background_color=mng_background_color;
6778 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006779
cristy3ed852e2009-09-05 21:47:34 +00006780 if (image_info->ping == MagickFalse)
6781 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006782
cristy3ed852e2009-09-05 21:47:34 +00006783 mng_info->image_found++;
6784 }
6785#endif
6786 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006787
cristy3ed852e2009-09-05 21:47:34 +00006788 if (mng_iterations == 1)
6789 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006790
cristy3ed852e2009-09-05 21:47:34 +00006791 while (GetPreviousImageInList(image) != (Image *) NULL)
6792 {
6793 image_count++;
6794 if (image_count > 10*mng_info->image_found)
6795 {
6796 if (logging != MagickFalse)
6797 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006798
cristy3ed852e2009-09-05 21:47:34 +00006799 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6800 CoderError,"Linked list is corrupted, beginning of list not found",
6801 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006802
cristy3ed852e2009-09-05 21:47:34 +00006803 return((Image *) NULL);
6804 }
glennrp0fe50b42010-11-16 03:52:51 +00006805
cristy3ed852e2009-09-05 21:47:34 +00006806 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006807
cristy3ed852e2009-09-05 21:47:34 +00006808 if (GetNextImageInList(image) == (Image *) NULL)
6809 {
6810 if (logging != MagickFalse)
6811 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006812
cristy3ed852e2009-09-05 21:47:34 +00006813 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6814 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6815 image_info->filename);
6816 }
6817 }
glennrp47b9dd52010-11-24 18:12:06 +00006818
cristy3ed852e2009-09-05 21:47:34 +00006819 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6820 GetNextImageInList(image) ==
6821 (Image *) NULL)
6822 {
6823 if (logging != MagickFalse)
6824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6825 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006826
cristy3ed852e2009-09-05 21:47:34 +00006827 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6828 CoderError,"image->next for first image is NULL but shouldn't be.",
6829 "`%s'",image_info->filename);
6830 }
glennrp47b9dd52010-11-24 18:12:06 +00006831
cristy3ed852e2009-09-05 21:47:34 +00006832 if (mng_info->image_found == 0)
6833 {
6834 if (logging != MagickFalse)
6835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6836 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006837
cristy3ed852e2009-09-05 21:47:34 +00006838 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6839 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006840
cristy3ed852e2009-09-05 21:47:34 +00006841 if (image != (Image *) NULL)
6842 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006843
cristy3ed852e2009-09-05 21:47:34 +00006844 MngInfoFreeStruct(mng_info,&have_mng_structure);
6845 return((Image *) NULL);
6846 }
6847
6848 if (mng_info->ticks_per_second)
6849 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6850 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006851
cristy3ed852e2009-09-05 21:47:34 +00006852 else
6853 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006854
cristy3ed852e2009-09-05 21:47:34 +00006855 /* Find final nonzero image delay */
6856 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006857
cristy3ed852e2009-09-05 21:47:34 +00006858 while (GetNextImageInList(image) != (Image *) NULL)
6859 {
6860 if (image->delay)
6861 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006862
cristy3ed852e2009-09-05 21:47:34 +00006863 image=GetNextImageInList(image);
6864 }
glennrp0fe50b42010-11-16 03:52:51 +00006865
cristy3ed852e2009-09-05 21:47:34 +00006866 if (final_delay < final_image_delay)
6867 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006868
cristy3ed852e2009-09-05 21:47:34 +00006869 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006870
cristy3ed852e2009-09-05 21:47:34 +00006871 if (logging != MagickFalse)
6872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006873 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6874 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006875
cristy3ed852e2009-09-05 21:47:34 +00006876 if (logging != MagickFalse)
6877 {
6878 int
6879 scene;
6880
6881 scene=0;
6882 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006883
cristy3ed852e2009-09-05 21:47:34 +00006884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6885 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006886
cristy3ed852e2009-09-05 21:47:34 +00006887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006888 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006889
cristy3ed852e2009-09-05 21:47:34 +00006890 while (GetNextImageInList(image) != (Image *) NULL)
6891 {
6892 image=GetNextImageInList(image);
6893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006894 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006895 }
6896 }
6897
6898 image=GetFirstImageInList(image);
6899#ifdef MNG_COALESCE_LAYERS
6900 if (insert_layers)
6901 {
6902 Image
6903 *next_image,
6904 *next;
6905
cristybb503372010-05-27 20:51:26 +00006906 size_t
cristy3ed852e2009-09-05 21:47:34 +00006907 scene;
6908
6909 if (logging != MagickFalse)
6910 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006911
cristy3ed852e2009-09-05 21:47:34 +00006912 scene=image->scene;
6913 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006914
cristy3ed852e2009-09-05 21:47:34 +00006915 if (next_image == (Image *) NULL)
6916 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006917
cristy3ed852e2009-09-05 21:47:34 +00006918 image=DestroyImageList(image);
6919 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006920
cristy3ed852e2009-09-05 21:47:34 +00006921 for (next=image; next != (Image *) NULL; next=next_image)
6922 {
6923 next->page.width=mng_info->mng_width;
6924 next->page.height=mng_info->mng_height;
6925 next->page.x=0;
6926 next->page.y=0;
6927 next->scene=scene++;
6928 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006929
cristy3ed852e2009-09-05 21:47:34 +00006930 if (next_image == (Image *) NULL)
6931 break;
glennrp47b9dd52010-11-24 18:12:06 +00006932
cristy3ed852e2009-09-05 21:47:34 +00006933 if (next->delay == 0)
6934 {
6935 scene--;
6936 next_image->previous=GetPreviousImageInList(next);
6937 if (GetPreviousImageInList(next) == (Image *) NULL)
6938 image=next_image;
6939 else
6940 next->previous->next=next_image;
6941 next=DestroyImage(next);
6942 }
6943 }
6944 }
6945#endif
6946
6947 while (GetNextImageInList(image) != (Image *) NULL)
6948 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006949
cristy3ed852e2009-09-05 21:47:34 +00006950 image->dispose=BackgroundDispose;
6951
6952 if (logging != MagickFalse)
6953 {
6954 int
6955 scene;
6956
6957 scene=0;
6958 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006959
cristy3ed852e2009-09-05 21:47:34 +00006960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6961 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006962
cristy3ed852e2009-09-05 21:47:34 +00006963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006964 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6965 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006966
cristy3ed852e2009-09-05 21:47:34 +00006967 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006968 {
6969 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006970
cristyf2faecf2010-05-28 19:19:36 +00006971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006972 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6973 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006974 }
6975 }
glennrp47b9dd52010-11-24 18:12:06 +00006976
cristy3ed852e2009-09-05 21:47:34 +00006977 image=GetFirstImageInList(image);
6978 MngInfoFreeStruct(mng_info,&have_mng_structure);
6979 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006980
cristy3ed852e2009-09-05 21:47:34 +00006981 if (logging != MagickFalse)
6982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006983
cristy3ed852e2009-09-05 21:47:34 +00006984 return(GetFirstImageInList(image));
6985}
glennrp25c1e2b2010-03-25 01:39:56 +00006986#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006987static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6988{
6989 printf("Your PNG library is too old: You have libpng-%s\n",
6990 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006991
cristy3ed852e2009-09-05 21:47:34 +00006992 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6993 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006994
cristy3ed852e2009-09-05 21:47:34 +00006995 return(Image *) NULL;
6996}
glennrp47b9dd52010-11-24 18:12:06 +00006997
cristy3ed852e2009-09-05 21:47:34 +00006998static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6999{
7000 return(ReadPNGImage(image_info,exception));
7001}
glennrp25c1e2b2010-03-25 01:39:56 +00007002#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007003#endif
7004
7005/*
7006%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7007% %
7008% %
7009% %
7010% R e g i s t e r P N G I m a g e %
7011% %
7012% %
7013% %
7014%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7015%
7016% RegisterPNGImage() adds properties for the PNG image format to
7017% the list of supported formats. The properties include the image format
7018% tag, a method to read and/or write the format, whether the format
7019% supports the saving of more than one frame to the same file or blob,
7020% whether the format supports native in-memory I/O, and a brief
7021% description of the format.
7022%
7023% The format of the RegisterPNGImage method is:
7024%
cristybb503372010-05-27 20:51:26 +00007025% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007026%
7027*/
cristybb503372010-05-27 20:51:26 +00007028ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007029{
7030 char
7031 version[MaxTextExtent];
7032
7033 MagickInfo
7034 *entry;
7035
7036 static const char
7037 *PNGNote=
7038 {
7039 "See http://www.libpng.org/ for details about the PNG format."
7040 },
glennrp47b9dd52010-11-24 18:12:06 +00007041
cristy3ed852e2009-09-05 21:47:34 +00007042 *JNGNote=
7043 {
7044 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7045 "format."
7046 },
glennrp47b9dd52010-11-24 18:12:06 +00007047
cristy3ed852e2009-09-05 21:47:34 +00007048 *MNGNote=
7049 {
7050 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7051 "format."
7052 };
7053
7054 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007055
cristy3ed852e2009-09-05 21:47:34 +00007056#if defined(PNG_LIBPNG_VER_STRING)
7057 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7058 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007059
cristy3ed852e2009-09-05 21:47:34 +00007060 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7061 {
7062 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7063 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7064 MaxTextExtent);
7065 }
7066#endif
glennrp47b9dd52010-11-24 18:12:06 +00007067
cristy3ed852e2009-09-05 21:47:34 +00007068 entry=SetMagickInfo("MNG");
7069 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007070
cristy3ed852e2009-09-05 21:47:34 +00007071#if defined(MAGICKCORE_PNG_DELEGATE)
7072 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7073 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7074#endif
glennrp47b9dd52010-11-24 18:12:06 +00007075
cristy3ed852e2009-09-05 21:47:34 +00007076 entry->magick=(IsImageFormatHandler *) IsMNG;
7077 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007078
cristy3ed852e2009-09-05 21:47:34 +00007079 if (*version != '\0')
7080 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007081
cristy3ed852e2009-09-05 21:47:34 +00007082 entry->module=ConstantString("PNG");
7083 entry->note=ConstantString(MNGNote);
7084 (void) RegisterMagickInfo(entry);
7085
7086 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007087
cristy3ed852e2009-09-05 21:47:34 +00007088#if defined(MAGICKCORE_PNG_DELEGATE)
7089 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7090 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7091#endif
glennrp47b9dd52010-11-24 18:12:06 +00007092
cristy3ed852e2009-09-05 21:47:34 +00007093 entry->magick=(IsImageFormatHandler *) IsPNG;
7094 entry->adjoin=MagickFalse;
7095 entry->description=ConstantString("Portable Network Graphics");
7096 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007097
cristy3ed852e2009-09-05 21:47:34 +00007098 if (*version != '\0')
7099 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007100
cristy3ed852e2009-09-05 21:47:34 +00007101 entry->note=ConstantString(PNGNote);
7102 (void) RegisterMagickInfo(entry);
7103
7104 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007105
cristy3ed852e2009-09-05 21:47:34 +00007106#if defined(MAGICKCORE_PNG_DELEGATE)
7107 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7108 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7109#endif
glennrp47b9dd52010-11-24 18:12:06 +00007110
cristy3ed852e2009-09-05 21:47:34 +00007111 entry->magick=(IsImageFormatHandler *) IsPNG;
7112 entry->adjoin=MagickFalse;
7113 entry->description=ConstantString(
7114 "8-bit indexed with optional binary transparency");
7115 entry->module=ConstantString("PNG");
7116 (void) RegisterMagickInfo(entry);
7117
7118 entry=SetMagickInfo("PNG24");
7119 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007120
cristy3ed852e2009-09-05 21:47:34 +00007121#if defined(ZLIB_VERSION)
7122 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7123 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007124
cristy3ed852e2009-09-05 21:47:34 +00007125 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7126 {
7127 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7128 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7129 }
7130#endif
glennrp47b9dd52010-11-24 18:12:06 +00007131
cristy3ed852e2009-09-05 21:47:34 +00007132 if (*version != '\0')
7133 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007134
cristy3ed852e2009-09-05 21:47:34 +00007135#if defined(MAGICKCORE_PNG_DELEGATE)
7136 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7137 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7138#endif
glennrp47b9dd52010-11-24 18:12:06 +00007139
cristy3ed852e2009-09-05 21:47:34 +00007140 entry->magick=(IsImageFormatHandler *) IsPNG;
7141 entry->adjoin=MagickFalse;
7142 entry->description=ConstantString("opaque 24-bit RGB");
7143 entry->module=ConstantString("PNG");
7144 (void) RegisterMagickInfo(entry);
7145
7146 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007147
cristy3ed852e2009-09-05 21:47:34 +00007148#if defined(MAGICKCORE_PNG_DELEGATE)
7149 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7150 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7151#endif
glennrp47b9dd52010-11-24 18:12:06 +00007152
cristy3ed852e2009-09-05 21:47:34 +00007153 entry->magick=(IsImageFormatHandler *) IsPNG;
7154 entry->adjoin=MagickFalse;
7155 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7156 entry->module=ConstantString("PNG");
7157 (void) RegisterMagickInfo(entry);
7158
7159 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007160
cristy3ed852e2009-09-05 21:47:34 +00007161#if defined(JNG_SUPPORTED)
7162#if defined(MAGICKCORE_PNG_DELEGATE)
7163 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7164 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7165#endif
7166#endif
glennrp47b9dd52010-11-24 18:12:06 +00007167
cristy3ed852e2009-09-05 21:47:34 +00007168 entry->magick=(IsImageFormatHandler *) IsJNG;
7169 entry->adjoin=MagickFalse;
7170 entry->description=ConstantString("JPEG Network Graphics");
7171 entry->module=ConstantString("PNG");
7172 entry->note=ConstantString(JNGNote);
7173 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007174
cristy18b17442009-10-25 18:36:48 +00007175#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007176 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007177#endif
glennrp47b9dd52010-11-24 18:12:06 +00007178
cristy3ed852e2009-09-05 21:47:34 +00007179 return(MagickImageCoderSignature);
7180}
7181
7182/*
7183%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7184% %
7185% %
7186% %
7187% U n r e g i s t e r P N G I m a g e %
7188% %
7189% %
7190% %
7191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7192%
7193% UnregisterPNGImage() removes format registrations made by the
7194% PNG module from the list of supported formats.
7195%
7196% The format of the UnregisterPNGImage method is:
7197%
7198% UnregisterPNGImage(void)
7199%
7200*/
7201ModuleExport void UnregisterPNGImage(void)
7202{
7203 (void) UnregisterMagickInfo("MNG");
7204 (void) UnregisterMagickInfo("PNG");
7205 (void) UnregisterMagickInfo("PNG8");
7206 (void) UnregisterMagickInfo("PNG24");
7207 (void) UnregisterMagickInfo("PNG32");
7208 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007209
cristy3ed852e2009-09-05 21:47:34 +00007210#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007211 if (ping_semaphore != (SemaphoreInfo *) NULL)
7212 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007213#endif
7214}
7215
7216#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007217#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007218/*
7219%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7220% %
7221% %
7222% %
7223% W r i t e M N G I m a g e %
7224% %
7225% %
7226% %
7227%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7228%
7229% WriteMNGImage() writes an image in the Portable Network Graphics
7230% Group's "Multiple-image Network Graphics" encoded image format.
7231%
7232% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7233%
7234% The format of the WriteMNGImage method is:
7235%
7236% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
7237%
7238% A description of each parameter follows.
7239%
7240% o image_info: the image info.
7241%
7242% o image: The image.
7243%
7244%
7245% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7246% "To do" under ReadPNGImage):
7247%
cristy3ed852e2009-09-05 21:47:34 +00007248% Preserve all unknown and not-yet-handled known chunks found in input
7249% PNG file and copy them into output PNG files according to the PNG
7250% copying rules.
7251%
7252% Write the iCCP chunk at MNG level when (icc profile length > 0)
7253%
7254% Improve selection of color type (use indexed-colour or indexed-colour
7255% with tRNS when 256 or fewer unique RGBA values are present).
7256%
7257% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7258% This will be complicated if we limit ourselves to generating MNG-LC
7259% files. For now we ignore disposal method 3 and simply overlay the next
7260% image on it.
7261%
7262% Check for identical PLTE's or PLTE/tRNS combinations and use a
7263% global MNG PLTE or PLTE/tRNS combination when appropriate.
7264% [mostly done 15 June 1999 but still need to take care of tRNS]
7265%
7266% Check for identical sRGB and replace with a global sRGB (and remove
7267% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7268% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7269% local gAMA/cHRM with local sRGB if appropriate).
7270%
7271% Check for identical sBIT chunks and write global ones.
7272%
7273% Provide option to skip writing the signature tEXt chunks.
7274%
7275% Use signatures to detect identical objects and reuse the first
7276% instance of such objects instead of writing duplicate objects.
7277%
7278% Use a smaller-than-32k value of compression window size when
7279% appropriate.
7280%
7281% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7282% ancillary text chunks and save profiles.
7283%
7284% Provide an option to force LC files (to ensure exact framing rate)
7285% instead of VLC.
7286%
7287% Provide an option to force VLC files instead of LC, even when offsets
7288% are present. This will involve expanding the embedded images with a
7289% transparent region at the top and/or left.
7290*/
7291
cristy3ed852e2009-09-05 21:47:34 +00007292static void
glennrpcf002022011-01-30 02:38:15 +00007293Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007294 png_info *ping_info, unsigned char *profile_type, unsigned char
7295 *profile_description, unsigned char *profile_data, png_uint_32 length)
7296{
cristy3ed852e2009-09-05 21:47:34 +00007297 png_textp
7298 text;
7299
cristybb503372010-05-27 20:51:26 +00007300 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007301 i;
7302
7303 unsigned char
7304 *sp;
7305
7306 png_charp
7307 dp;
7308
7309 png_uint_32
7310 allocated_length,
7311 description_length;
7312
7313 unsigned char
7314 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007315
cristy3ed852e2009-09-05 21:47:34 +00007316 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7317 return;
7318
7319 if (image_info->verbose)
7320 {
glennrp0fe50b42010-11-16 03:52:51 +00007321 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7322 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007323 }
glennrp0fe50b42010-11-16 03:52:51 +00007324
cristy3ed852e2009-09-05 21:47:34 +00007325 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7326 description_length=(png_uint_32) strlen((const char *) profile_description);
7327 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7328 + description_length);
7329 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7330 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7331 text[0].key[0]='\0';
7332 (void) ConcatenateMagickString(text[0].key,
7333 "Raw profile type ",MaxTextExtent);
7334 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7335 sp=profile_data;
7336 dp=text[0].text;
7337 *dp++='\n';
7338 (void) CopyMagickString(dp,(const char *) profile_description,
7339 allocated_length);
7340 dp+=description_length;
7341 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007342 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007343 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007344 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007345
cristybb503372010-05-27 20:51:26 +00007346 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007347 {
7348 if (i%36 == 0)
7349 *dp++='\n';
7350 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7351 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7352 }
glennrp47b9dd52010-11-24 18:12:06 +00007353
cristy3ed852e2009-09-05 21:47:34 +00007354 *dp++='\n';
7355 *dp='\0';
7356 text[0].text_length=(png_size_t) (dp-text[0].text);
7357 text[0].compression=image_info->compression == NoCompression ||
7358 (image_info->compression == UndefinedCompression &&
7359 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007360
cristy3ed852e2009-09-05 21:47:34 +00007361 if (text[0].text_length <= allocated_length)
7362 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007363
cristy3ed852e2009-09-05 21:47:34 +00007364 png_free(ping,text[0].text);
7365 png_free(ping,text[0].key);
7366 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007367}
7368
glennrpcf002022011-01-30 02:38:15 +00007369static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007370 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007371{
7372 char
7373 *name;
7374
7375 const StringInfo
7376 *profile;
7377
7378 unsigned char
7379 *data;
7380
7381 png_uint_32 length;
7382
7383 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007384
7385 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7386 {
cristy3ed852e2009-09-05 21:47:34 +00007387 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007388
cristy3ed852e2009-09-05 21:47:34 +00007389 if (profile != (const StringInfo *) NULL)
7390 {
7391 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007392 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007393
glennrp47b9dd52010-11-24 18:12:06 +00007394 if (LocaleNCompare(name,string,11) == 0)
7395 {
7396 if (logging != MagickFalse)
7397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7398 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007399
glennrpcf002022011-01-30 02:38:15 +00007400 ping_profile=CloneStringInfo(profile);
7401 data=GetStringInfoDatum(ping_profile),
7402 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007403 data[4]=data[3];
7404 data[3]=data[2];
7405 data[2]=data[1];
7406 data[1]=data[0];
7407 (void) WriteBlobMSBULong(image,length-5); /* data length */
7408 (void) WriteBlob(image,length-1,data+1);
7409 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007410 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007411 }
cristy3ed852e2009-09-05 21:47:34 +00007412 }
glennrp47b9dd52010-11-24 18:12:06 +00007413
cristy3ed852e2009-09-05 21:47:34 +00007414 name=GetNextImageProfile(image);
7415 }
glennrp47b9dd52010-11-24 18:12:06 +00007416
cristy3ed852e2009-09-05 21:47:34 +00007417 return(MagickTrue);
7418}
7419
glennrpb9cfe272010-12-21 15:08:06 +00007420
cristy3ed852e2009-09-05 21:47:34 +00007421/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007422static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7423 const ImageInfo *IMimage_info,Image *IMimage)
7424{
7425 Image
7426 *image;
7427
7428 ImageInfo
7429 *image_info;
7430
cristy3ed852e2009-09-05 21:47:34 +00007431 char
7432 s[2];
7433
7434 const char
7435 *name,
7436 *property,
7437 *value;
7438
7439 const StringInfo
7440 *profile;
7441
cristy3ed852e2009-09-05 21:47:34 +00007442 int
cristy3ed852e2009-09-05 21:47:34 +00007443 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007444 pass;
7445
glennrpe9c26dc2010-05-30 01:56:35 +00007446 png_byte
7447 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007448
glennrp39992b42010-11-14 00:03:43 +00007449 png_color
7450 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007451
glennrp5af765f2010-03-30 11:12:18 +00007452 png_color_16
7453 ping_background,
7454 ping_trans_color;
7455
cristy3ed852e2009-09-05 21:47:34 +00007456 png_info
7457 *ping_info;
7458
7459 png_struct
7460 *ping;
7461
glennrp5af765f2010-03-30 11:12:18 +00007462 png_uint_32
7463 ping_height,
7464 ping_width;
7465
cristybb503372010-05-27 20:51:26 +00007466 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007467 y;
7468
7469 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007470 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007471 logging,
glennrp58e01762011-01-07 15:28:54 +00007472 matte,
7473
glennrpda8f3a72011-02-27 23:54:12 +00007474 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007475 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007476 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007477 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007478 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007479 ping_have_bKGD,
7480 ping_have_pHYs,
7481 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007482
7483 ping_exclude_bKGD,
7484 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007485 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007486 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007487 ping_exclude_gAMA,
7488 ping_exclude_iCCP,
7489 /* ping_exclude_iTXt, */
7490 ping_exclude_oFFs,
7491 ping_exclude_pHYs,
7492 ping_exclude_sRGB,
7493 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007494 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007495 ping_exclude_vpAg,
7496 ping_exclude_zCCP, /* hex-encoded iCCP */
7497 ping_exclude_zTXt,
7498
glennrp8d3d6e52011-04-19 04:39:51 +00007499 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007500 ping_need_colortype_warning,
7501
glennrp82b3c532011-03-22 19:20:54 +00007502 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007503 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007504 tried_333,
7505 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007506
7507 QuantumInfo
7508 *quantum_info;
7509
cristybb503372010-05-27 20:51:26 +00007510 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007511 i,
7512 x;
7513
7514 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007515 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007516
glennrp5af765f2010-03-30 11:12:18 +00007517 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007518 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007519 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007520 ping_color_type,
7521 ping_interlace_method,
7522 ping_compression_method,
7523 ping_filter_method,
7524 ping_num_trans;
7525
cristybb503372010-05-27 20:51:26 +00007526 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007527 image_depth,
7528 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007529
cristybb503372010-05-27 20:51:26 +00007530 size_t
cristy3ed852e2009-09-05 21:47:34 +00007531 quality,
7532 rowbytes,
7533 save_image_depth;
7534
glennrpdfd70802010-11-14 01:23:35 +00007535 int
glennrpfd05d622011-02-25 04:10:33 +00007536 j,
glennrpf09bded2011-01-08 01:15:59 +00007537 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007538 number_opaque,
7539 number_semitransparent,
7540 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007541 ping_pHYs_unit_type;
7542
7543 png_uint_32
7544 ping_pHYs_x_resolution,
7545 ping_pHYs_y_resolution;
7546
cristy3ed852e2009-09-05 21:47:34 +00007547 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007548 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007549
glennrpb9cfe272010-12-21 15:08:06 +00007550 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7551 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007552 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007553 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007554
cristy3ed852e2009-09-05 21:47:34 +00007555#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007556 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007557#endif
7558
glennrp5af765f2010-03-30 11:12:18 +00007559 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007560 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007561 ping_color_type=0,
7562 ping_interlace_method=0,
7563 ping_compression_method=0,
7564 ping_filter_method=0,
7565 ping_num_trans = 0;
7566
7567 ping_background.red = 0;
7568 ping_background.green = 0;
7569 ping_background.blue = 0;
7570 ping_background.gray = 0;
7571 ping_background.index = 0;
7572
7573 ping_trans_color.red=0;
7574 ping_trans_color.green=0;
7575 ping_trans_color.blue=0;
7576 ping_trans_color.gray=0;
7577
glennrpdfd70802010-11-14 01:23:35 +00007578 ping_pHYs_unit_type = 0;
7579 ping_pHYs_x_resolution = 0;
7580 ping_pHYs_y_resolution = 0;
7581
glennrpda8f3a72011-02-27 23:54:12 +00007582 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007583 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007584 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007585 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007586 ping_have_bKGD=MagickFalse;
7587 ping_have_pHYs=MagickFalse;
7588 ping_have_tRNS=MagickFalse;
7589
glennrp0e8ea192010-12-24 18:00:33 +00007590 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7591 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007592 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007593 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007594 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007595 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7596 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7597 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7598 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7599 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7600 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007601 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007602 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7603 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7604 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7605
glennrp8d3d6e52011-04-19 04:39:51 +00007606 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007607 ping_need_colortype_warning = MagickFalse;
7608
glennrp8bb3a022010-12-13 20:40:04 +00007609 number_opaque = 0;
7610 number_semitransparent = 0;
7611 number_transparent = 0;
7612
glennrpfd05d622011-02-25 04:10:33 +00007613 if (logging != MagickFalse)
7614 {
7615 if (image->storage_class == UndefinedClass)
7616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7617 " storage_class=UndefinedClass");
7618 if (image->storage_class == DirectClass)
7619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7620 " storage_class=DirectClass");
7621 if (image->storage_class == PseudoClass)
7622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7623 " storage_class=PseudoClass");
7624 }
glennrp28af3712011-04-06 18:07:30 +00007625
glennrpc6c391a2011-04-27 02:23:56 +00007626 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007627 {
glennrpc6c391a2011-04-27 02:23:56 +00007628 if (image->storage_class != PseudoClass && image->colormap != NULL)
7629 {
7630 /* Free the bogus colormap; it can cause trouble later */
7631 if (logging != MagickFalse)
7632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7633 " Freeing bogus colormap");
7634 (void *) RelinquishMagickMemory(image->colormap);
7635 image->colormap=NULL;
7636 }
glennrp28af3712011-04-06 18:07:30 +00007637 }
glennrpbb4f99d2011-05-22 11:13:17 +00007638
cristy3ed852e2009-09-05 21:47:34 +00007639 if (image->colorspace != RGBColorspace)
7640 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007641
glennrp3241bd02010-12-12 04:36:28 +00007642 /*
7643 Sometimes we get PseudoClass images whose RGB values don't match
7644 the colors in the colormap. This code syncs the RGB values.
7645 */
7646 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7647 (void) SyncImage(image);
7648
glennrpa6a06632011-01-19 15:15:34 +00007649#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7650 if (image->depth > 8)
7651 {
7652 if (logging != MagickFalse)
7653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7654 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7655
7656 image->depth=8;
7657 }
7658#endif
7659
glennrp8e58efd2011-05-20 12:16:29 +00007660 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007661 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7662 {
glennrp8e58efd2011-05-20 12:16:29 +00007663 register PixelPacket
7664 *r;
7665
7666 ExceptionInfo
7667 *exception;
7668
7669 exception=(&image->exception);
7670
7671 if (image->depth > 8)
7672 {
7673#if MAGICKCORE_QUANTUM_DEPTH > 16
7674 /* Scale to 16-bit */
7675 LBR16RGBOPixelPacketComponent(image->background_color);
7676
7677 for (y=0; y < (ssize_t) image->rows; y++)
7678 {
7679 r=GetAuthenticPixels(image,0,y,image->columns,1,
7680 exception);
7681
7682 if (r == (PixelPacket *) NULL)
7683 break;
7684
7685 for (x=0; x < (ssize_t) image->columns; x++)
7686 {
7687 LBR16RGBOPixelComponent(r);
7688 r++;
7689 }
glennrpbb4f99d2011-05-22 11:13:17 +00007690
glennrp8e58efd2011-05-20 12:16:29 +00007691 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7692 break;
7693 }
7694
7695 if (image->storage_class == PseudoClass && image->colormap != NULL)
7696 {
cristy3e08f112011-05-24 13:19:30 +00007697 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007698 {
7699 LBR16RGBOPixelPacketComponent(image->colormap[i]);
7700 }
7701 }
7702#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7703 }
7704
7705 else if (image->depth > 4)
7706 {
7707#if MAGICKCORE_QUANTUM_DEPTH > 8
7708 /* Scale to 8-bit */
7709 LBR08RGBOPixelPacketComponent(image->background_color);
7710
7711 for (y=0; y < (ssize_t) image->rows; y++)
7712 {
7713 r=GetAuthenticPixels(image,0,y,image->columns,1,
7714 exception);
7715
7716 if (r == (PixelPacket *) NULL)
7717 break;
7718
7719 for (x=0; x < (ssize_t) image->columns; x++)
7720 {
7721 LBR08RGBOPixelComponent(r);
7722 r++;
7723 }
glennrpbb4f99d2011-05-22 11:13:17 +00007724
glennrp8e58efd2011-05-20 12:16:29 +00007725 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7726 break;
7727 }
7728
7729 if (image->storage_class == PseudoClass && image->colormap != NULL)
7730 {
cristy3e08f112011-05-24 13:19:30 +00007731 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007732 {
7733 LBR08RGBOPixelPacketComponent(image->colormap[i]);
7734 }
7735 }
7736#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7737 }
7738 else
7739 if (image->depth > 2)
7740 {
7741 /* Scale to 4-bit */
7742 LBR04RGBOPixelPacketComponent(image->background_color);
7743
7744 for (y=0; y < (ssize_t) image->rows; y++)
7745 {
7746 r=GetAuthenticPixels(image,0,y,image->columns,1,
7747 exception);
7748
7749 if (r == (PixelPacket *) NULL)
7750 break;
7751
7752 for (x=0; x < (ssize_t) image->columns; x++)
7753 {
7754 LBR04RGBOPixelComponent(r);
7755 r++;
7756 }
glennrpbb4f99d2011-05-22 11:13:17 +00007757
glennrp8e58efd2011-05-20 12:16:29 +00007758 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7759 break;
7760 }
7761
7762 if (image->storage_class == PseudoClass && image->colormap != NULL)
7763 {
cristy3e08f112011-05-24 13:19:30 +00007764 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007765 {
7766 LBR04RGBOPixelPacketComponent(image->colormap[i]);
7767 }
7768 }
7769 }
7770
7771 else if (image->depth > 1)
7772 {
7773 /* Scale to 2-bit */
7774 LBR02RGBOPixelPacketComponent(image->background_color);
7775
7776 for (y=0; y < (ssize_t) image->rows; y++)
7777 {
7778 r=GetAuthenticPixels(image,0,y,image->columns,1,
7779 exception);
7780
7781 if (r == (PixelPacket *) NULL)
7782 break;
7783
7784 for (x=0; x < (ssize_t) image->columns; x++)
7785 {
7786 LBR02RGBOPixelComponent(r);
7787 r++;
7788 }
glennrpbb4f99d2011-05-22 11:13:17 +00007789
glennrp8e58efd2011-05-20 12:16:29 +00007790 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7791 break;
7792 }
7793
7794 if (image->storage_class == PseudoClass && image->colormap != NULL)
7795 {
cristy3e08f112011-05-24 13:19:30 +00007796 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007797 {
7798 LBR02RGBOPixelPacketComponent(image->colormap[i]);
7799 }
7800 }
7801 }
7802 else
7803 {
7804 /* Scale to 1-bit */
7805 LBR01RGBOPixelPacketComponent(image->background_color);
7806
7807 for (y=0; y < (ssize_t) image->rows; y++)
7808 {
7809 r=GetAuthenticPixels(image,0,y,image->columns,1,
7810 exception);
7811
7812 if (r == (PixelPacket *) NULL)
7813 break;
7814
7815 for (x=0; x < (ssize_t) image->columns; x++)
7816 {
7817 LBR01RGBOPixelComponent(r);
7818 r++;
7819 }
glennrpbb4f99d2011-05-22 11:13:17 +00007820
glennrp8e58efd2011-05-20 12:16:29 +00007821 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7822 break;
7823 }
7824
7825 if (image->storage_class == PseudoClass && image->colormap != NULL)
7826 {
cristy3e08f112011-05-24 13:19:30 +00007827 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007828 {
7829 LBR01RGBOPixelPacketComponent(image->colormap[i]);
7830 }
7831 }
7832 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007833 }
7834
glennrp67b9c1a2011-04-22 18:47:36 +00007835 /* To do: set to next higher multiple of 8 */
7836 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007837 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007838
glennrp2b013e42010-11-24 16:55:50 +00007839#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7840 /* PNG does not handle depths greater than 16 so reduce it even
7841 * if lossy
7842 */
glennrp8e58efd2011-05-20 12:16:29 +00007843 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007844 image->depth=16;
7845#endif
7846
glennrp3faa9a32011-04-23 14:00:25 +00007847#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007848 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007849 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007850 image->depth = 8;
7851#endif
7852
glennrpc8c2f062011-02-25 19:00:33 +00007853 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007854 * we reduce the transparency to binary and run again, then if there
7855 * 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 +00007856 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7857 * palette. Then (To do) we take care of a final reduction that is only
7858 * needed if there are still 256 colors present and one of them has both
7859 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007860 */
glennrp82b3c532011-03-22 19:20:54 +00007861
glennrp8ca51ad2011-05-12 21:22:32 +00007862 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007863 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007864 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007865
glennrp8ca51ad2011-05-12 21:22:32 +00007866 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007867 {
7868 /* BUILD_PALETTE
7869 *
7870 * Sometimes we get DirectClass images that have 256 colors or fewer.
7871 * This code will build a colormap.
7872 *
7873 * Also, sometimes we get PseudoClass images with an out-of-date
7874 * colormap. This code will replace the colormap with a new one.
7875 * Sometimes we get PseudoClass images that have more than 256 colors.
7876 * This code will delete the colormap and change the image to
7877 * DirectClass.
7878 *
7879 * If image->matte is MagickFalse, we ignore the opacity channel
7880 * even though it sometimes contains left-over non-opaque values.
7881 *
7882 * Also we gather some information (number of opaque, transparent,
7883 * and semitransparent pixels, and whether the image has any non-gray
7884 * pixels or only black-and-white pixels) that we might need later.
7885 *
7886 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7887 * we need to check for bogus non-opaque values, at least.
7888 */
glennrp3c218112010-11-27 15:31:26 +00007889
glennrpd71e86a2011-02-24 01:28:37 +00007890 ExceptionInfo
7891 *exception;
glennrp3c218112010-11-27 15:31:26 +00007892
glennrpd71e86a2011-02-24 01:28:37 +00007893 int
7894 n;
glennrp3c218112010-11-27 15:31:26 +00007895
glennrpd71e86a2011-02-24 01:28:37 +00007896 PixelPacket
7897 opaque[260],
7898 semitransparent[260],
7899 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007900
glennrpd71e86a2011-02-24 01:28:37 +00007901 register IndexPacket
7902 *indexes;
glennrp8bb3a022010-12-13 20:40:04 +00007903
glennrpd71e86a2011-02-24 01:28:37 +00007904 register const PixelPacket
7905 *s,
7906 *q;
glennrpd6bf1612010-12-17 17:28:54 +00007907
glennrpfd05d622011-02-25 04:10:33 +00007908 register PixelPacket
7909 *r;
7910
glennrpd71e86a2011-02-24 01:28:37 +00007911 if (logging != MagickFalse)
7912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7913 " Enter BUILD_PALETTE:");
7914
7915 if (logging != MagickFalse)
7916 {
glennrp03812ae2010-12-24 01:31:34 +00007917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007918 " image->columns=%.20g",(double) image->columns);
7919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7920 " image->rows=%.20g",(double) image->rows);
7921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7922 " image->matte=%.20g",(double) image->matte);
7923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7924 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007925
glennrpfd05d622011-02-25 04:10:33 +00007926 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007927 {
7928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007929 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007931 " i (red,green,blue,opacity)");
glennrp2cc891a2010-12-24 13:44:32 +00007932
glennrpd71e86a2011-02-24 01:28:37 +00007933 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007934 {
glennrpd71e86a2011-02-24 01:28:37 +00007935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7936 " %d (%d,%d,%d,%d)",
7937 (int) i,
7938 (int) image->colormap[i].red,
7939 (int) image->colormap[i].green,
7940 (int) image->colormap[i].blue,
7941 (int) image->colormap[i].opacity);
glennrp7ddcc222010-12-11 05:01:05 +00007942 }
glennrp2cc891a2010-12-24 13:44:32 +00007943
glennrpd71e86a2011-02-24 01:28:37 +00007944 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7945 {
7946 if (i > 255)
7947 {
7948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7949 " %d (%d,%d,%d,%d)",
7950 (int) i,
7951 (int) image->colormap[i].red,
7952 (int) image->colormap[i].green,
7953 (int) image->colormap[i].blue,
7954 (int) image->colormap[i].opacity);
7955 }
7956 }
glennrp03812ae2010-12-24 01:31:34 +00007957 }
glennrp7ddcc222010-12-11 05:01:05 +00007958
glennrpd71e86a2011-02-24 01:28:37 +00007959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7960 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007961
glennrpd71e86a2011-02-24 01:28:37 +00007962 if (image->colors == 0)
7963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7964 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007965
glennrp8d3d6e52011-04-19 04:39:51 +00007966 if (ping_preserve_colormap == MagickFalse)
7967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7968 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007969 }
7970
7971 exception=(&image->exception);
7972
7973 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007974 number_opaque = 0;
7975 number_semitransparent = 0;
7976 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007977
7978 for (y=0; y < (ssize_t) image->rows; y++)
7979 {
7980 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7981
7982 if (q == (PixelPacket *) NULL)
7983 break;
7984
7985 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007986 {
glennrp4737d522011-04-29 03:33:42 +00007987 if (image->matte == MagickFalse ||
7988 GetOpacityPixelComponent(q) == OpaqueOpacity)
glennrpd71e86a2011-02-24 01:28:37 +00007989 {
7990 if (number_opaque < 259)
7991 {
7992 if (number_opaque == 0)
7993 {
glennrp8e045c82011-04-27 16:40:27 +00007994 GetRGBPixelComponents(q, opaque[0]);
glennrpd71e86a2011-02-24 01:28:37 +00007995 opaque[0].opacity=OpaqueOpacity;
7996 number_opaque=1;
7997 }
glennrp2cc891a2010-12-24 13:44:32 +00007998
glennrpd71e86a2011-02-24 01:28:37 +00007999 for (i=0; i< (ssize_t) number_opaque; i++)
8000 {
glennrp0e68fac2011-04-26 04:51:47 +00008001 if (IsColorEqual(q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008002 break;
8003 }
glennrp7ddcc222010-12-11 05:01:05 +00008004
glennrpd71e86a2011-02-24 01:28:37 +00008005 if (i == (ssize_t) number_opaque &&
8006 number_opaque < 259)
8007 {
8008 number_opaque++;
glennrp8e045c82011-04-27 16:40:27 +00008009 GetRGBPixelComponents(q, opaque[i]);
glennrpca7ad3a2011-04-26 04:44:54 +00008010 opaque[i].opacity=OpaqueOpacity;
glennrpd71e86a2011-02-24 01:28:37 +00008011 }
8012 }
8013 }
8014 else if (q->opacity == TransparentOpacity)
8015 {
8016 if (number_transparent < 259)
8017 {
8018 if (number_transparent == 0)
8019 {
glennrp8e045c82011-04-27 16:40:27 +00008020 GetRGBOPixelComponents(q, transparent[0]);
glennrpa18d5bc2011-04-23 14:51:34 +00008021 ping_trans_color.red=
8022 (unsigned short) GetRedPixelComponent(q);
8023 ping_trans_color.green=
8024 (unsigned short) GetGreenPixelComponent(q);
8025 ping_trans_color.blue=
8026 (unsigned short) GetBluePixelComponent(q);
8027 ping_trans_color.gray=
8028 (unsigned short) GetRedPixelComponent(q);
glennrpd71e86a2011-02-24 01:28:37 +00008029 number_transparent = 1;
8030 }
8031
8032 for (i=0; i< (ssize_t) number_transparent; i++)
8033 {
glennrp0e68fac2011-04-26 04:51:47 +00008034 if (IsColorEqual(q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008035 break;
8036 }
8037
8038 if (i == (ssize_t) number_transparent &&
8039 number_transparent < 259)
8040 {
8041 number_transparent++;
glennrp8e045c82011-04-27 16:40:27 +00008042 GetRGBOPixelComponents(q, transparent[i]);
glennrpd71e86a2011-02-24 01:28:37 +00008043 }
8044 }
8045 }
8046 else
8047 {
8048 if (number_semitransparent < 259)
8049 {
8050 if (number_semitransparent == 0)
8051 {
glennrp8e045c82011-04-27 16:40:27 +00008052 GetRGBOPixelComponents(q, semitransparent[0]);
glennrpd71e86a2011-02-24 01:28:37 +00008053 number_semitransparent = 1;
8054 }
8055
8056 for (i=0; i< (ssize_t) number_semitransparent; i++)
8057 {
glennrp0e68fac2011-04-26 04:51:47 +00008058 if (IsColorEqual(q, semitransparent+i)
glennrpca7ad3a2011-04-26 04:44:54 +00008059 && GetOpacityPixelComponent(q) ==
8060 semitransparent[i].opacity)
glennrpd71e86a2011-02-24 01:28:37 +00008061 break;
8062 }
8063
8064 if (i == (ssize_t) number_semitransparent &&
8065 number_semitransparent < 259)
8066 {
8067 number_semitransparent++;
glennrp8e045c82011-04-27 16:40:27 +00008068 GetRGBOPixelComponents(q, semitransparent[i]);
glennrpd71e86a2011-02-24 01:28:37 +00008069 }
8070 }
8071 }
8072 q++;
8073 }
8074 }
8075
8076 if (ping_exclude_bKGD == MagickFalse)
8077 {
8078 /* Add the background color to the palette, if it
8079 * isn't already there.
8080 */
glennrpc6c391a2011-04-27 02:23:56 +00008081 if (logging != MagickFalse)
8082 {
8083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8084 " Check colormap for background (%d,%d,%d)",
8085 (int) image->background_color.red,
8086 (int) image->background_color.green,
8087 (int) image->background_color.blue);
8088 }
glennrpd71e86a2011-02-24 01:28:37 +00008089 for (i=0; i<number_opaque; i++)
8090 {
glennrpca7ad3a2011-04-26 04:44:54 +00008091 if (opaque[i].red == image->background_color.red &&
8092 opaque[i].green == image->background_color.green &&
8093 opaque[i].blue == image->background_color.blue)
8094 break;
glennrpd71e86a2011-02-24 01:28:37 +00008095 }
glennrpd71e86a2011-02-24 01:28:37 +00008096 if (number_opaque < 259 && i == number_opaque)
8097 {
glennrp8e045c82011-04-27 16:40:27 +00008098 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008099 ping_background.index = i;
8100 if (logging != MagickFalse)
8101 {
8102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8103 " background_color index is %d",(int) i);
8104 }
8105
glennrpd71e86a2011-02-24 01:28:37 +00008106 }
glennrpa080bc32011-03-11 18:03:44 +00008107 else if (logging != MagickFalse)
8108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8109 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008110 }
8111
8112 image_colors=number_opaque+number_transparent+number_semitransparent;
8113
glennrpa080bc32011-03-11 18:03:44 +00008114 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8115 {
8116 /* No room for the background color; remove it. */
8117 number_opaque--;
8118 image_colors--;
8119 }
8120
glennrpd71e86a2011-02-24 01:28:37 +00008121 if (logging != MagickFalse)
8122 {
8123 if (image_colors > 256)
8124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8125 " image has more than 256 colors");
8126
8127 else
8128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8129 " image has %d colors",image_colors);
8130 }
8131
glennrp8d3d6e52011-04-19 04:39:51 +00008132 if (ping_preserve_colormap != MagickFalse)
8133 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008134
glennrpfd05d622011-02-25 04:10:33 +00008135 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008136 {
8137 ping_have_color=MagickFalse;
8138 ping_have_non_bw=MagickFalse;
8139
8140 if(image_colors > 256)
8141 {
8142 for (y=0; y < (ssize_t) image->rows; y++)
8143 {
8144 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8145
8146 if (q == (PixelPacket *) NULL)
8147 break;
8148
8149 /* Worst case is black-and-white; we are looking at every
8150 * pixel twice.
8151 */
8152
8153 if (ping_have_color == MagickFalse)
8154 {
8155 s=q;
8156 for (x=0; x < (ssize_t) image->columns; x++)
8157 {
glennrpa18d5bc2011-04-23 14:51:34 +00008158 if (GetRedPixelComponent(s) != GetGreenPixelComponent(s)
8159 || GetRedPixelComponent(s) != GetBluePixelComponent(s))
glennrpd71e86a2011-02-24 01:28:37 +00008160 {
8161 ping_have_color=MagickTrue;
8162 ping_have_non_bw=MagickTrue;
8163 break;
8164 }
8165 s++;
8166 }
8167 }
8168
8169 if (ping_have_non_bw == MagickFalse)
8170 {
8171 s=q;
8172 for (x=0; x < (ssize_t) image->columns; x++)
8173 {
glennrpa18d5bc2011-04-23 14:51:34 +00008174 if (GetRedPixelComponent(s) != 0 &&
8175 GetRedPixelComponent(s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008176 {
8177 ping_have_non_bw=MagickTrue;
8178 }
8179 s++;
8180 }
8181 }
8182 }
glennrpbb4f99d2011-05-22 11:13:17 +00008183 }
8184 }
glennrpd71e86a2011-02-24 01:28:37 +00008185
8186 if (image_colors < 257)
8187 {
8188 PixelPacket
8189 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008190
glennrpd71e86a2011-02-24 01:28:37 +00008191 /*
8192 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008193 */
8194
glennrpd71e86a2011-02-24 01:28:37 +00008195 if (logging != MagickFalse)
8196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8197 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008198
glennrpd71e86a2011-02-24 01:28:37 +00008199 /* Sort palette, transparent first */;
8200
8201 n = 0;
8202
8203 for (i=0; i<number_transparent; i++)
8204 colormap[n++] = transparent[i];
8205
8206 for (i=0; i<number_semitransparent; i++)
8207 colormap[n++] = semitransparent[i];
8208
8209 for (i=0; i<number_opaque; i++)
8210 colormap[n++] = opaque[i];
8211
glennrpc6c391a2011-04-27 02:23:56 +00008212 ping_background.index +=
8213 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008214
glennrpd71e86a2011-02-24 01:28:37 +00008215 /* image_colors < 257; search the colormap instead of the pixels
8216 * to get ping_have_color and ping_have_non_bw
8217 */
8218 for (i=0; i<n; i++)
8219 {
8220 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008221 {
glennrpd71e86a2011-02-24 01:28:37 +00008222 if (colormap[i].red != colormap[i].green ||
8223 colormap[i].red != colormap[i].blue)
8224 {
8225 ping_have_color=MagickTrue;
8226 ping_have_non_bw=MagickTrue;
8227 break;
8228 }
8229 }
8230
8231 if (ping_have_non_bw == MagickFalse)
8232 {
8233 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008234 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008235 }
glennrp8bb3a022010-12-13 20:40:04 +00008236 }
8237
glennrpd71e86a2011-02-24 01:28:37 +00008238 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8239 (number_transparent == 0 && number_semitransparent == 0)) &&
8240 (((mng_info->write_png_colortype-1) ==
8241 PNG_COLOR_TYPE_PALETTE) ||
8242 (mng_info->write_png_colortype == 0)))
8243 {
glennrp6185c532011-01-14 17:58:40 +00008244 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008245 {
glennrpd71e86a2011-02-24 01:28:37 +00008246 if (n != (ssize_t) image_colors)
8247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8248 " image_colors (%d) and n (%d) don't match",
8249 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008250
glennrpd71e86a2011-02-24 01:28:37 +00008251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8252 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008253 }
glennrp03812ae2010-12-24 01:31:34 +00008254
glennrpd71e86a2011-02-24 01:28:37 +00008255 image->colors = image_colors;
8256
8257 if (AcquireImageColormap(image,image_colors) ==
8258 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008259 ThrowWriterException(ResourceLimitError,
8260 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008261
8262 for (i=0; i< (ssize_t) image_colors; i++)
8263 image->colormap[i] = colormap[i];
8264
8265 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008266 {
glennrpd71e86a2011-02-24 01:28:37 +00008267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8268 " image->colors=%d (%d)",
8269 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008270
glennrpd71e86a2011-02-24 01:28:37 +00008271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8272 " Update the pixel indexes");
8273 }
glennrp6185c532011-01-14 17:58:40 +00008274
glennrpfd05d622011-02-25 04:10:33 +00008275 /* Sync the pixel indices with the new colormap */
8276
glennrpd71e86a2011-02-24 01:28:37 +00008277 for (y=0; y < (ssize_t) image->rows; y++)
8278 {
8279 q=GetAuthenticPixels(image,0,y,image->columns,1,
8280 exception);
glennrp6185c532011-01-14 17:58:40 +00008281
glennrpd71e86a2011-02-24 01:28:37 +00008282 if (q == (PixelPacket *) NULL)
8283 break;
glennrp6185c532011-01-14 17:58:40 +00008284
glennrpd71e86a2011-02-24 01:28:37 +00008285 indexes=GetAuthenticIndexQueue(image);
glennrpbb4f99d2011-05-22 11:13:17 +00008286
glennrpd71e86a2011-02-24 01:28:37 +00008287 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008288 {
glennrpd71e86a2011-02-24 01:28:37 +00008289 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008290 {
glennrpd71e86a2011-02-24 01:28:37 +00008291 if ((image->matte == MagickFalse ||
glennrpbb4f99d2011-05-22 11:13:17 +00008292 image->colormap[i].opacity ==
glennrpca7ad3a2011-04-26 04:44:54 +00008293 GetOpacityPixelComponent(q)) &&
glennrpbb4f99d2011-05-22 11:13:17 +00008294 image->colormap[i].red ==
glennrpca7ad3a2011-04-26 04:44:54 +00008295 GetRedPixelComponent(q) &&
glennrpbb4f99d2011-05-22 11:13:17 +00008296 image->colormap[i].green ==
glennrpca7ad3a2011-04-26 04:44:54 +00008297 GetGreenPixelComponent(q) &&
glennrpbb4f99d2011-05-22 11:13:17 +00008298 image->colormap[i].blue ==
glennrpca7ad3a2011-04-26 04:44:54 +00008299 GetBluePixelComponent(q))
glennrp6185c532011-01-14 17:58:40 +00008300 {
cristy9fff7b42011-04-29 01:09:31 +00008301 SetIndexPixelComponent(indexes+x,i);
glennrpd71e86a2011-02-24 01:28:37 +00008302 break;
glennrp6185c532011-01-14 17:58:40 +00008303 }
glennrp6185c532011-01-14 17:58:40 +00008304 }
glennrpd71e86a2011-02-24 01:28:37 +00008305 q++;
8306 }
glennrp6185c532011-01-14 17:58:40 +00008307
glennrpd71e86a2011-02-24 01:28:37 +00008308 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8309 break;
8310 }
8311 }
8312 }
8313
8314 if (logging != MagickFalse)
8315 {
8316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8317 " image->colors=%d", (int) image->colors);
8318
8319 if (image->colormap != NULL)
8320 {
8321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8322 " i (red,green,blue,opacity)");
8323
8324 for (i=0; i < (ssize_t) image->colors; i++)
8325 {
cristy72988482011-03-29 16:34:38 +00008326 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008327 {
8328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8329 " %d (%d,%d,%d,%d)",
8330 (int) i,
8331 (int) image->colormap[i].red,
8332 (int) image->colormap[i].green,
8333 (int) image->colormap[i].blue,
8334 (int) image->colormap[i].opacity);
8335 }
glennrp6185c532011-01-14 17:58:40 +00008336 }
8337 }
glennrp03812ae2010-12-24 01:31:34 +00008338
glennrpd71e86a2011-02-24 01:28:37 +00008339 if (number_transparent < 257)
8340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8341 " number_transparent = %d",
8342 number_transparent);
8343 else
glennrp03812ae2010-12-24 01:31:34 +00008344
glennrpd71e86a2011-02-24 01:28:37 +00008345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8346 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008347
glennrpd71e86a2011-02-24 01:28:37 +00008348 if (number_opaque < 257)
8349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8350 " number_opaque = %d",
8351 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008352
glennrpd71e86a2011-02-24 01:28:37 +00008353 else
8354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8355 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008356
glennrpd71e86a2011-02-24 01:28:37 +00008357 if (number_semitransparent < 257)
8358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8359 " number_semitransparent = %d",
8360 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008361
glennrpd71e86a2011-02-24 01:28:37 +00008362 else
8363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8364 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008365
glennrpd71e86a2011-02-24 01:28:37 +00008366 if (ping_have_non_bw == MagickFalse)
8367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8368 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008369
glennrpd71e86a2011-02-24 01:28:37 +00008370 else if (ping_have_color == MagickFalse)
8371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8372 " All pixels and the background are gray");
8373
8374 else
8375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8376 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008377
glennrp03812ae2010-12-24 01:31:34 +00008378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8379 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008380 }
glennrpfd05d622011-02-25 04:10:33 +00008381
glennrpc8c2f062011-02-25 19:00:33 +00008382 if (mng_info->write_png8 == MagickFalse)
8383 break;
glennrpfd05d622011-02-25 04:10:33 +00008384
glennrpc8c2f062011-02-25 19:00:33 +00008385 /* Make any reductions necessary for the PNG8 format */
8386 if (image_colors <= 256 &&
8387 image_colors != 0 && image->colormap != NULL &&
8388 number_semitransparent == 0 &&
8389 number_transparent <= 1)
8390 break;
8391
8392 /* PNG8 can't have semitransparent colors so we threshold the
8393 * opacity to 0 or OpaqueOpacity
8394 */
8395 if (number_semitransparent != 0)
8396 {
8397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8398 " Thresholding the alpha channel to binary");
8399
8400 for (y=0; y < (ssize_t) image->rows; y++)
8401 {
8402 r=GetAuthenticPixels(image,0,y,image->columns,1,
8403 exception);
8404
8405 if (r == (PixelPacket *) NULL)
8406 break;
8407
8408 for (x=0; x < (ssize_t) image->columns; x++)
8409 {
glennrp8ca51ad2011-05-12 21:22:32 +00008410 if (GetOpacityPixelComponent(r) > TransparentOpacity/2)
8411 {
8412 SetOpacityPixelComponent(r,TransparentOpacity);
8413 SetRGBPixelComponents(r,image->background_color);
8414 }
8415 else
8416 SetOpacityPixelComponent(r,OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00008417 r++;
8418 }
glennrpbb4f99d2011-05-22 11:13:17 +00008419
glennrpc8c2f062011-02-25 19:00:33 +00008420 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8421 break;
8422
8423 if (image_colors != 0 && image_colors <= 256 &&
8424 image->colormap != NULL)
8425 for (i=0; i<image_colors; i++)
8426 image->colormap[i].opacity =
glennrp77110c32011-05-03 05:25:16 +00008427 (image->colormap[i].opacity > TransparentOpacity/2 ?
8428 TransparentOpacity : OpaqueOpacity);
glennrpc8c2f062011-02-25 19:00:33 +00008429 }
8430 continue;
8431 }
8432
8433 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008434 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8435 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8436 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008437 */
glennrpd3371642011-03-22 19:42:23 +00008438 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8439 {
8440 if (logging != MagickFalse)
8441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8442 " Quantizing the background color to 4-4-4");
8443
8444 tried_444 = MagickTrue;
8445
glennrp8e58efd2011-05-20 12:16:29 +00008446 LBR04RGBPixelPacketComponent(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008447
8448 if (logging != MagickFalse)
8449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8450 " Quantizing the pixel colors to 4-4-4");
8451
8452 if (image->colormap == NULL)
8453 {
8454 for (y=0; y < (ssize_t) image->rows; y++)
8455 {
8456 r=GetAuthenticPixels(image,0,y,image->columns,1,
8457 exception);
8458
8459 if (r == (PixelPacket *) NULL)
8460 break;
8461
8462 for (x=0; x < (ssize_t) image->columns; x++)
8463 {
glennrp8ca51ad2011-05-12 21:22:32 +00008464 if (GetOpacityPixelComponent(r) == OpaqueOpacity)
glennrp8e58efd2011-05-20 12:16:29 +00008465 LBR04RGBPixelComponent(r);
glennrpd3371642011-03-22 19:42:23 +00008466 r++;
8467 }
glennrpbb4f99d2011-05-22 11:13:17 +00008468
glennrpd3371642011-03-22 19:42:23 +00008469 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8470 break;
8471 }
8472 }
8473
8474 else /* Should not reach this; colormap already exists and
8475 must be <= 256 */
8476 {
8477 if (logging != MagickFalse)
8478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8479 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008480
glennrpd3371642011-03-22 19:42:23 +00008481 for (i=0; i<image_colors; i++)
8482 {
glennrp8e58efd2011-05-20 12:16:29 +00008483 LBR04RGBPixelPacketComponent(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008484 }
8485 }
8486 continue;
8487 }
8488
glennrp82b3c532011-03-22 19:20:54 +00008489 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8490 {
8491 if (logging != MagickFalse)
8492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8493 " Quantizing the background color to 3-3-3");
8494
8495 tried_333 = MagickTrue;
8496
glennrp8e58efd2011-05-20 12:16:29 +00008497 LBR03RGBPixelPacketComponent(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008498
8499 if (logging != MagickFalse)
8500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008501 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008502
8503 if (image->colormap == NULL)
8504 {
8505 for (y=0; y < (ssize_t) image->rows; y++)
8506 {
8507 r=GetAuthenticPixels(image,0,y,image->columns,1,
8508 exception);
8509
8510 if (r == (PixelPacket *) NULL)
8511 break;
8512
8513 for (x=0; x < (ssize_t) image->columns; x++)
8514 {
glennrp8ca51ad2011-05-12 21:22:32 +00008515 if (GetOpacityPixelComponent(r) == OpaqueOpacity)
glennrp8e58efd2011-05-20 12:16:29 +00008516 LBR03RGBPixelComponent(r);
glennrp82b3c532011-03-22 19:20:54 +00008517 r++;
8518 }
glennrpbb4f99d2011-05-22 11:13:17 +00008519
glennrp82b3c532011-03-22 19:20:54 +00008520 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8521 break;
8522 }
8523 }
8524
8525 else /* Should not reach this; colormap already exists and
8526 must be <= 256 */
8527 {
8528 if (logging != MagickFalse)
8529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008530 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008531 for (i=0; i<image_colors; i++)
8532 {
glennrp8e58efd2011-05-20 12:16:29 +00008533 LBR03RGBPixelPacketComponent(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008534 }
glennrpd3371642011-03-22 19:42:23 +00008535 }
8536 continue;
glennrp82b3c532011-03-22 19:20:54 +00008537 }
glennrpc8c2f062011-02-25 19:00:33 +00008538
glennrp8ca51ad2011-05-12 21:22:32 +00008539 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008540 {
8541 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008543 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008544
glennrp8ca51ad2011-05-12 21:22:32 +00008545 tried_332 = MagickTrue;
8546
glennrp3faa9a32011-04-23 14:00:25 +00008547 /* Red and green were already done so we only quantize the blue
8548 * channel
8549 */
8550
glennrp8e58efd2011-05-20 12:16:29 +00008551 LBR02BluePixelPacketComponent(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008552
glennrpc8c2f062011-02-25 19:00:33 +00008553 if (logging != MagickFalse)
8554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008555 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008556
glennrpc8c2f062011-02-25 19:00:33 +00008557 if (image->colormap == NULL)
8558 {
8559 for (y=0; y < (ssize_t) image->rows; y++)
8560 {
8561 r=GetAuthenticPixels(image,0,y,image->columns,1,
8562 exception);
8563
8564 if (r == (PixelPacket *) NULL)
8565 break;
8566
8567 for (x=0; x < (ssize_t) image->columns; x++)
8568 {
glennrp8ca51ad2011-05-12 21:22:32 +00008569 if (GetOpacityPixelComponent(r) == OpaqueOpacity)
glennrp8e58efd2011-05-20 12:16:29 +00008570 LBR02BluePixelComponent(r);
glennrp52a479c2011-02-26 21:14:38 +00008571 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008572 }
glennrpbb4f99d2011-05-22 11:13:17 +00008573
glennrpc8c2f062011-02-25 19:00:33 +00008574 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8575 break;
8576 }
8577 }
glennrpfd05d622011-02-25 04:10:33 +00008578
glennrpc8c2f062011-02-25 19:00:33 +00008579 else /* Should not reach this; colormap already exists and
8580 must be <= 256 */
8581 {
8582 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008584 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008585 for (i=0; i<image_colors; i++)
8586 {
glennrp8e58efd2011-05-20 12:16:29 +00008587 LBR02BluePixelPacketComponent(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008588 }
8589 }
8590 continue;
8591 }
8592 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008593
8594 if (image_colors == 0 || image_colors > 256)
8595 {
8596 /* Take care of special case with 256 colors + 1 transparent
8597 * color. We don't need to quantize to 2-3-2-1; we only need to
8598 * eliminate one color, so we'll merge the two darkest red
8599 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8600 */
8601 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8602 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8603 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8604 {
8605 image->background_color.red=ScaleCharToQuantum(0x24);
8606 }
glennrpbb4f99d2011-05-22 11:13:17 +00008607
glennrp8ca51ad2011-05-12 21:22:32 +00008608 if (image->colormap == NULL)
8609 {
8610 for (y=0; y < (ssize_t) image->rows; y++)
8611 {
8612 r=GetAuthenticPixels(image,0,y,image->columns,1,
8613 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008614
glennrp8ca51ad2011-05-12 21:22:32 +00008615 if (r == (PixelPacket *) NULL)
8616 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008617
glennrp8ca51ad2011-05-12 21:22:32 +00008618 for (x=0; x < (ssize_t) image->columns; x++)
8619 {
8620 if (ScaleQuantumToChar(GetRedPixelComponent(r)) == 0x49 &&
8621 ScaleQuantumToChar(GetGreenPixelComponent(r)) == 0x00 &&
8622 ScaleQuantumToChar(GetBluePixelComponent(r)) == 0x00 &&
8623 GetOpacityPixelComponent(r) == OpaqueOpacity)
8624 {
8625 SetRedPixelComponent(r,ScaleCharToQuantum(0x24));
8626 }
8627 r++;
8628 }
glennrpbb4f99d2011-05-22 11:13:17 +00008629
glennrp8ca51ad2011-05-12 21:22:32 +00008630 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8631 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008632
glennrp8ca51ad2011-05-12 21:22:32 +00008633 }
8634 }
8635
8636 else
8637 {
8638 for (i=0; i<image_colors; i++)
8639 {
8640 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8641 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8642 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8643 {
8644 image->colormap[i].red=ScaleCharToQuantum(0x24);
8645 }
8646 }
8647 }
8648 }
glennrpd71e86a2011-02-24 01:28:37 +00008649 }
glennrpfd05d622011-02-25 04:10:33 +00008650 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008651
glennrpfd05d622011-02-25 04:10:33 +00008652 /* If we are excluding the tRNS chunk and there is transparency,
8653 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8654 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008655 */
glennrp0e8ea192010-12-24 18:00:33 +00008656 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8657 (number_transparent != 0 || number_semitransparent != 0))
8658 {
glennrpd17915c2011-04-29 14:24:22 +00008659 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008660
8661 if (ping_have_color == MagickFalse)
8662 mng_info->write_png_colortype = 5;
8663
8664 else
8665 mng_info->write_png_colortype = 7;
8666
glennrp8d579662011-02-23 02:05:02 +00008667 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008668 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008669 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008670
glennrp0e8ea192010-12-24 18:00:33 +00008671 }
8672
glennrpfd05d622011-02-25 04:10:33 +00008673 /* See if cheap transparency is possible. It is only possible
8674 * when there is a single transparent color, no semitransparent
8675 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008676 * as the transparent color. We only need this information if
8677 * we are writing a PNG with colortype 0 or 2, and we have not
8678 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008679 */
glennrp5a39f372011-02-25 04:52:16 +00008680 if (number_transparent == 1 &&
8681 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008682 {
8683 ping_have_cheap_transparency = MagickTrue;
8684
8685 if (number_semitransparent != 0)
8686 ping_have_cheap_transparency = MagickFalse;
8687
8688 else if (image_colors == 0 || image_colors > 256 ||
8689 image->colormap == NULL)
8690 {
8691 ExceptionInfo
8692 *exception;
8693
8694 register const PixelPacket
8695 *q;
8696
8697 exception=(&image->exception);
8698
8699 for (y=0; y < (ssize_t) image->rows; y++)
8700 {
8701 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8702
8703 if (q == (PixelPacket *) NULL)
8704 break;
8705
8706 for (x=0; x < (ssize_t) image->columns; x++)
8707 {
8708 if (q->opacity != TransparentOpacity &&
glennrp7c7b3152011-04-26 04:01:27 +00008709 (unsigned short) GetRedPixelComponent(q) ==
8710 ping_trans_color.red &&
8711 (unsigned short) GetGreenPixelComponent(q) ==
8712 ping_trans_color.green &&
8713 (unsigned short) GetBluePixelComponent(q) ==
8714 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008715 {
8716 ping_have_cheap_transparency = MagickFalse;
8717 break;
8718 }
8719
8720 q++;
8721 }
glennrpbb4f99d2011-05-22 11:13:17 +00008722
glennrpfd05d622011-02-25 04:10:33 +00008723 if (ping_have_cheap_transparency == MagickFalse)
8724 break;
8725 }
8726 }
8727 else
8728 {
glennrp67b9c1a2011-04-22 18:47:36 +00008729 /* Assuming that image->colormap[0] is the one transparent color
8730 * and that all others are opaque.
8731 */
glennrpfd05d622011-02-25 04:10:33 +00008732 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008733 for (i=1; i<image_colors; i++)
8734 if (image->colormap[i].red == image->colormap[0].red &&
8735 image->colormap[i].green == image->colormap[0].green &&
8736 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008737 {
glennrp67b9c1a2011-04-22 18:47:36 +00008738 ping_have_cheap_transparency = MagickFalse;
8739 break;
glennrpfd05d622011-02-25 04:10:33 +00008740 }
8741 }
glennrpbb4f99d2011-05-22 11:13:17 +00008742
glennrpfd05d622011-02-25 04:10:33 +00008743 if (logging != MagickFalse)
8744 {
8745 if (ping_have_cheap_transparency == MagickFalse)
8746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8747 " Cheap transparency is not possible.");
8748
8749 else
8750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8751 " Cheap transparency is possible.");
8752 }
8753 }
8754 else
8755 ping_have_cheap_transparency = MagickFalse;
8756
glennrp8640fb52010-11-23 15:48:26 +00008757 image_depth=image->depth;
8758
glennrp26c990a2010-11-23 02:23:20 +00008759 quantum_info = (QuantumInfo *) NULL;
8760 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008761 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008762 image_matte=image->matte;
8763
glennrp0fe50b42010-11-16 03:52:51 +00008764 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008765 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008766
glennrp52a479c2011-02-26 21:14:38 +00008767 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8768 (image->colors == 0 || image->colormap == NULL))
8769 {
glennrp52a479c2011-02-26 21:14:38 +00008770 image_info=DestroyImageInfo(image_info);
8771 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008772 (void) ThrowMagickException(&IMimage->exception,
8773 GetMagickModule(),CoderError,
8774 "Cannot write PNG8 or color-type 3; colormap is NULL",
8775 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008776#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8777 UnlockSemaphoreInfo(ping_semaphore);
8778#endif
8779 return(MagickFalse);
8780 }
8781
cristy3ed852e2009-09-05 21:47:34 +00008782 /*
8783 Allocate the PNG structures
8784 */
8785#ifdef PNG_USER_MEM_SUPPORTED
8786 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008787 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8788 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008789
cristy3ed852e2009-09-05 21:47:34 +00008790#else
8791 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008792 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008793
cristy3ed852e2009-09-05 21:47:34 +00008794#endif
8795 if (ping == (png_struct *) NULL)
8796 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008797
cristy3ed852e2009-09-05 21:47:34 +00008798 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008799
cristy3ed852e2009-09-05 21:47:34 +00008800 if (ping_info == (png_info *) NULL)
8801 {
8802 png_destroy_write_struct(&ping,(png_info **) NULL);
8803 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8804 }
glennrp0fe50b42010-11-16 03:52:51 +00008805
cristy3ed852e2009-09-05 21:47:34 +00008806 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008807 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008808
glennrp5af765f2010-03-30 11:12:18 +00008809 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008810 {
8811 /*
8812 PNG write failed.
8813 */
8814#ifdef PNG_DEBUG
8815 if (image_info->verbose)
8816 (void) printf("PNG write has failed.\n");
8817#endif
8818 png_destroy_write_struct(&ping,&ping_info);
8819#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008820 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008821#endif
glennrpda8f3a72011-02-27 23:54:12 +00008822 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008823 (void) CloseBlob(image);
8824 image_info=DestroyImageInfo(image_info);
8825 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008826 return(MagickFalse);
8827 }
8828 /*
8829 Prepare PNG for writing.
8830 */
8831#if defined(PNG_MNG_FEATURES_SUPPORTED)
8832 if (mng_info->write_mng)
8833 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008834
cristy3ed852e2009-09-05 21:47:34 +00008835#else
8836# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8837 if (mng_info->write_mng)
8838 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008839
cristy3ed852e2009-09-05 21:47:34 +00008840# endif
8841#endif
glennrp2b013e42010-11-24 16:55:50 +00008842
cristy3ed852e2009-09-05 21:47:34 +00008843 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008844
cristy4e5bc842010-06-09 13:56:01 +00008845 ping_width=(png_uint_32) image->columns;
8846 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008847
cristy3ed852e2009-09-05 21:47:34 +00008848 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8849 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008850
cristy3ed852e2009-09-05 21:47:34 +00008851 if (mng_info->write_png_depth != 0)
8852 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008853
cristy3ed852e2009-09-05 21:47:34 +00008854 /* Adjust requested depth to next higher valid depth if necessary */
8855 if (image_depth > 8)
8856 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008857
cristy3ed852e2009-09-05 21:47:34 +00008858 if ((image_depth > 4) && (image_depth < 8))
8859 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008860
cristy3ed852e2009-09-05 21:47:34 +00008861 if (image_depth == 3)
8862 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008863
cristy3ed852e2009-09-05 21:47:34 +00008864 if (logging != MagickFalse)
8865 {
8866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008867 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008869 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008871 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008873 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008875 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008876 }
glennrp8640fb52010-11-23 15:48:26 +00008877
cristy3ed852e2009-09-05 21:47:34 +00008878 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008879 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008880
glennrp26f37912010-12-23 16:22:42 +00008881
cristy3ed852e2009-09-05 21:47:34 +00008882#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008883 if (ping_exclude_pHYs == MagickFalse)
8884 {
cristy3ed852e2009-09-05 21:47:34 +00008885 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8886 (!mng_info->write_mng || !mng_info->equal_physs))
8887 {
glennrp0fe50b42010-11-16 03:52:51 +00008888 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8890 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008891
8892 if (image->units == PixelsPerInchResolution)
8893 {
glennrpdfd70802010-11-14 01:23:35 +00008894 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008895 ping_pHYs_x_resolution=
8896 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8897 ping_pHYs_y_resolution=
8898 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008899 }
glennrpdfd70802010-11-14 01:23:35 +00008900
cristy3ed852e2009-09-05 21:47:34 +00008901 else if (image->units == PixelsPerCentimeterResolution)
8902 {
glennrpdfd70802010-11-14 01:23:35 +00008903 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008904 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8905 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008906 }
glennrp991d11d2010-11-12 21:55:28 +00008907
cristy3ed852e2009-09-05 21:47:34 +00008908 else
8909 {
glennrpdfd70802010-11-14 01:23:35 +00008910 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8911 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8912 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008913 }
glennrp991d11d2010-11-12 21:55:28 +00008914
glennrp823b55c2011-03-14 18:46:46 +00008915 if (logging != MagickFalse)
8916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8917 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8918 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8919 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008920 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008921 }
glennrp26f37912010-12-23 16:22:42 +00008922 }
cristy3ed852e2009-09-05 21:47:34 +00008923#endif
glennrpa521b2f2010-10-29 04:11:03 +00008924
glennrp26f37912010-12-23 16:22:42 +00008925 if (ping_exclude_bKGD == MagickFalse)
8926 {
glennrpa521b2f2010-10-29 04:11:03 +00008927 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008928 {
glennrpa521b2f2010-10-29 04:11:03 +00008929 unsigned int
8930 mask;
cristy3ed852e2009-09-05 21:47:34 +00008931
glennrpa521b2f2010-10-29 04:11:03 +00008932 mask=0xffff;
8933 if (ping_bit_depth == 8)
8934 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008935
glennrpa521b2f2010-10-29 04:11:03 +00008936 if (ping_bit_depth == 4)
8937 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008938
glennrpa521b2f2010-10-29 04:11:03 +00008939 if (ping_bit_depth == 2)
8940 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008941
glennrpa521b2f2010-10-29 04:11:03 +00008942 if (ping_bit_depth == 1)
8943 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008944
glennrpa521b2f2010-10-29 04:11:03 +00008945 ping_background.red=(png_uint_16)
8946 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008947
glennrpa521b2f2010-10-29 04:11:03 +00008948 ping_background.green=(png_uint_16)
8949 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008950
glennrpa521b2f2010-10-29 04:11:03 +00008951 ping_background.blue=(png_uint_16)
8952 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00008953
8954 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00008955 }
cristy3ed852e2009-09-05 21:47:34 +00008956
glennrp0fe50b42010-11-16 03:52:51 +00008957 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008958 {
8959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8960 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00008961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8962 " background_color index is %d",
8963 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008964
8965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8966 " ping_bit_depth=%d",ping_bit_depth);
8967 }
glennrp0fe50b42010-11-16 03:52:51 +00008968
8969 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008970 }
glennrp0fe50b42010-11-16 03:52:51 +00008971
cristy3ed852e2009-09-05 21:47:34 +00008972 /*
8973 Select the color type.
8974 */
8975 matte=image_matte;
8976 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008977
glennrp1273f7b2011-02-24 03:20:30 +00008978 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008979 {
glennrp0fe50b42010-11-16 03:52:51 +00008980
glennrpfd05d622011-02-25 04:10:33 +00008981 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008982 for reducing the sample depth from 8. */
8983
glennrp0fe50b42010-11-16 03:52:51 +00008984 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008985
glennrp8bb3a022010-12-13 20:40:04 +00008986 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008987
8988 /*
8989 Set image palette.
8990 */
8991 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8992
glennrp0fe50b42010-11-16 03:52:51 +00008993 if (logging != MagickFalse)
8994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8995 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008996 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008997
8998 for (i=0; i < (ssize_t) number_colors; i++)
8999 {
9000 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9001 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9002 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9003 if (logging != MagickFalse)
9004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009005#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009006 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009007#else
9008 " %5ld (%5d,%5d,%5d)",
9009#endif
glennrp0fe50b42010-11-16 03:52:51 +00009010 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9011
9012 }
glennrp2b013e42010-11-24 16:55:50 +00009013
glennrp8bb3a022010-12-13 20:40:04 +00009014 ping_have_PLTE=MagickTrue;
9015 image_depth=ping_bit_depth;
9016 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009017
glennrp58e01762011-01-07 15:28:54 +00009018 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009019 {
glennrp0fe50b42010-11-16 03:52:51 +00009020 /*
9021 Identify which colormap entry is transparent.
9022 */
9023 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009024 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009025
glennrp8bb3a022010-12-13 20:40:04 +00009026 for (i=0; i < (ssize_t) number_transparent; i++)
9027 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009028
glennrp0fe50b42010-11-16 03:52:51 +00009029
glennrp2cc891a2010-12-24 13:44:32 +00009030 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009031 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009032
9033 if (ping_num_trans == 0)
9034 ping_have_tRNS=MagickFalse;
9035
glennrp8bb3a022010-12-13 20:40:04 +00009036 else
9037 ping_have_tRNS=MagickTrue;
9038 }
glennrp0fe50b42010-11-16 03:52:51 +00009039
glennrp1273f7b2011-02-24 03:20:30 +00009040 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009041 {
glennrp1273f7b2011-02-24 03:20:30 +00009042 /*
9043 * Identify which colormap entry is the background color.
9044 */
9045
glennrp4f25bd02011-01-01 18:51:28 +00009046 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9047 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9048 break;
glennrp0fe50b42010-11-16 03:52:51 +00009049
glennrp4f25bd02011-01-01 18:51:28 +00009050 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009051
9052 if (logging != MagickFalse)
9053 {
9054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9055 " background_color index is %d",
9056 (int) ping_background.index);
9057 }
glennrp4f25bd02011-01-01 18:51:28 +00009058 }
cristy3ed852e2009-09-05 21:47:34 +00009059 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009060
cristy3ed852e2009-09-05 21:47:34 +00009061 else if (mng_info->write_png24)
9062 {
9063 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009064 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009065 }
glennrp0fe50b42010-11-16 03:52:51 +00009066
cristy3ed852e2009-09-05 21:47:34 +00009067 else if (mng_info->write_png32)
9068 {
9069 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009070 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009071 }
glennrp0fe50b42010-11-16 03:52:51 +00009072
glennrp8bb3a022010-12-13 20:40:04 +00009073 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009074 {
glennrp5af765f2010-03-30 11:12:18 +00009075 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009076
glennrp8bb3a022010-12-13 20:40:04 +00009077 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009078 {
glennrp5af765f2010-03-30 11:12:18 +00009079 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009080
glennrp5af765f2010-03-30 11:12:18 +00009081 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9082 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009083 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009084
glennrp8bb3a022010-12-13 20:40:04 +00009085 else
9086 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009087
9088 if (logging != MagickFalse)
9089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9090 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009091 }
glennrp0fe50b42010-11-16 03:52:51 +00009092
glennrp7c4c9e62011-03-21 20:23:32 +00009093 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009094 {
9095 if (logging != MagickFalse)
9096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009097 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009098
glennrpd6bf1612010-12-17 17:28:54 +00009099 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009100 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009101
glennrpd6bf1612010-12-17 17:28:54 +00009102 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009103 {
glennrp5af765f2010-03-30 11:12:18 +00009104 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009105 image_matte=MagickFalse;
9106 }
glennrp0fe50b42010-11-16 03:52:51 +00009107
glennrpd6bf1612010-12-17 17:28:54 +00009108 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009109 {
glennrp5af765f2010-03-30 11:12:18 +00009110 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009111 image_matte=MagickTrue;
9112 }
glennrp0fe50b42010-11-16 03:52:51 +00009113
glennrp5aa37f62011-01-02 03:07:57 +00009114 if (image_info->type == PaletteType ||
9115 image_info->type == PaletteMatteType)
9116 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9117
glennrp7c4c9e62011-03-21 20:23:32 +00009118 if (mng_info->write_png_colortype == 0 &&
9119 (image_info->type == UndefinedType ||
9120 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009121 {
glennrp5aa37f62011-01-02 03:07:57 +00009122 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009123 {
glennrp5aa37f62011-01-02 03:07:57 +00009124 if (image_matte == MagickFalse)
9125 {
9126 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9127 image_matte=MagickFalse;
9128 }
glennrp0fe50b42010-11-16 03:52:51 +00009129
glennrp0b206f52011-01-07 04:55:32 +00009130 else
glennrp5aa37f62011-01-02 03:07:57 +00009131 {
9132 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9133 image_matte=MagickTrue;
9134 }
9135 }
9136 else
glennrp8bb3a022010-12-13 20:40:04 +00009137 {
glennrp5aa37f62011-01-02 03:07:57 +00009138 if (image_matte == MagickFalse)
9139 {
9140 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9141 image_matte=MagickFalse;
9142 }
glennrp8bb3a022010-12-13 20:40:04 +00009143
glennrp0b206f52011-01-07 04:55:32 +00009144 else
glennrp5aa37f62011-01-02 03:07:57 +00009145 {
9146 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9147 image_matte=MagickTrue;
9148 }
9149 }
glennrp0fe50b42010-11-16 03:52:51 +00009150 }
glennrp5aa37f62011-01-02 03:07:57 +00009151
cristy3ed852e2009-09-05 21:47:34 +00009152 }
glennrp0fe50b42010-11-16 03:52:51 +00009153
cristy3ed852e2009-09-05 21:47:34 +00009154 if (logging != MagickFalse)
9155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009156 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009157
glennrp5af765f2010-03-30 11:12:18 +00009158 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009159 {
9160 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9161 ping_color_type == PNG_COLOR_TYPE_RGB ||
9162 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9163 ping_bit_depth=8;
9164 }
cristy3ed852e2009-09-05 21:47:34 +00009165
glennrpd6bf1612010-12-17 17:28:54 +00009166 old_bit_depth=ping_bit_depth;
9167
glennrp5af765f2010-03-30 11:12:18 +00009168 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009169 {
glennrp8d579662011-02-23 02:05:02 +00009170 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9171 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009172 }
glennrp8640fb52010-11-23 15:48:26 +00009173
glennrp5af765f2010-03-30 11:12:18 +00009174 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009175 {
cristy35ef8242010-06-03 16:24:13 +00009176 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009177 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009178
9179 if (image->colors == 0)
9180 {
glennrp0fe50b42010-11-16 03:52:51 +00009181 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00009182 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00009183 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009184 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009185 }
9186
cristy35ef8242010-06-03 16:24:13 +00009187 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009188 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009189 }
glennrp2b013e42010-11-24 16:55:50 +00009190
glennrpd6bf1612010-12-17 17:28:54 +00009191 if (logging != MagickFalse)
9192 {
9193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9194 " Number of colors: %.20g",(double) image_colors);
9195
9196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9197 " Tentative PNG bit depth: %d",ping_bit_depth);
9198 }
9199
9200 if (ping_bit_depth < (int) mng_info->write_png_depth)
9201 ping_bit_depth = mng_info->write_png_depth;
9202 }
glennrp2cc891a2010-12-24 13:44:32 +00009203
glennrp5af765f2010-03-30 11:12:18 +00009204 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009205
cristy3ed852e2009-09-05 21:47:34 +00009206 if (logging != MagickFalse)
9207 {
9208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009209 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009210
cristy3ed852e2009-09-05 21:47:34 +00009211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009212 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009213
cristy3ed852e2009-09-05 21:47:34 +00009214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009215 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009216
cristy3ed852e2009-09-05 21:47:34 +00009217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009218
glennrp8640fb52010-11-23 15:48:26 +00009219 " image->depth: %.20g",(double) image->depth);
9220
9221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009222 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009223 }
9224
glennrp58e01762011-01-07 15:28:54 +00009225 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009226 {
glennrp4f25bd02011-01-01 18:51:28 +00009227 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009228 {
glennrp7c4c9e62011-03-21 20:23:32 +00009229 if (mng_info->write_png_colortype == 0)
9230 {
9231 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009232
glennrp7c4c9e62011-03-21 20:23:32 +00009233 if (ping_have_color != MagickFalse)
9234 ping_color_type=PNG_COLOR_TYPE_RGBA;
9235 }
glennrp4f25bd02011-01-01 18:51:28 +00009236
9237 /*
9238 * Determine if there is any transparent color.
9239 */
9240 if (number_transparent + number_semitransparent == 0)
9241 {
9242 /*
9243 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9244 */
glennrpa6a06632011-01-19 15:15:34 +00009245
glennrp4f25bd02011-01-01 18:51:28 +00009246 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009247
9248 if (mng_info->write_png_colortype == 0)
9249 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009250 }
9251
9252 else
9253 {
9254 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009255 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009256
9257 mask=0xffff;
9258
9259 if (ping_bit_depth == 8)
9260 mask=0x00ff;
9261
9262 if (ping_bit_depth == 4)
9263 mask=0x000f;
9264
9265 if (ping_bit_depth == 2)
9266 mask=0x0003;
9267
9268 if (ping_bit_depth == 1)
9269 mask=0x0001;
9270
9271 ping_trans_color.red=(png_uint_16)
9272 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9273
9274 ping_trans_color.green=(png_uint_16)
9275 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9276
9277 ping_trans_color.blue=(png_uint_16)
9278 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9279
9280 ping_trans_color.gray=(png_uint_16)
9281 (ScaleQuantumToShort(PixelIntensityToQuantum(
9282 image->colormap)) & mask);
9283
9284 ping_trans_color.index=(png_byte) 0;
9285
9286 ping_have_tRNS=MagickTrue;
9287 }
9288
9289 if (ping_have_tRNS != MagickFalse)
9290 {
9291 /*
glennrpfd05d622011-02-25 04:10:33 +00009292 * Determine if there is one and only one transparent color
9293 * and if so if it is fully transparent.
9294 */
9295 if (ping_have_cheap_transparency == MagickFalse)
9296 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009297 }
9298
9299 if (ping_have_tRNS != MagickFalse)
9300 {
glennrp7c4c9e62011-03-21 20:23:32 +00009301 if (mng_info->write_png_colortype == 0)
9302 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009303
9304 if (image_depth == 8)
9305 {
9306 ping_trans_color.red&=0xff;
9307 ping_trans_color.green&=0xff;
9308 ping_trans_color.blue&=0xff;
9309 ping_trans_color.gray&=0xff;
9310 }
9311 }
9312 }
cristy3ed852e2009-09-05 21:47:34 +00009313 else
9314 {
cristy3ed852e2009-09-05 21:47:34 +00009315 if (image_depth == 8)
9316 {
glennrp5af765f2010-03-30 11:12:18 +00009317 ping_trans_color.red&=0xff;
9318 ping_trans_color.green&=0xff;
9319 ping_trans_color.blue&=0xff;
9320 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009321 }
9322 }
9323 }
glennrp8640fb52010-11-23 15:48:26 +00009324
cristy3ed852e2009-09-05 21:47:34 +00009325 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009326
glennrp2e09f552010-11-14 00:38:48 +00009327 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009328 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009329
glennrp39992b42010-11-14 00:03:43 +00009330 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009331 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009332 ping_have_color == MagickFalse &&
9333 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009334 {
cristy35ef8242010-06-03 16:24:13 +00009335 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009336
cristy3ed852e2009-09-05 21:47:34 +00009337 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009338 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009339
glennrp7c4c9e62011-03-21 20:23:32 +00009340 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009341 {
glennrp5af765f2010-03-30 11:12:18 +00009342 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009343
cristy3ed852e2009-09-05 21:47:34 +00009344 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009345 {
9346 if (logging != MagickFalse)
9347 {
9348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9349 " Scaling ping_trans_color (0)");
9350 }
9351 ping_trans_color.gray*=0x0101;
9352 }
cristy3ed852e2009-09-05 21:47:34 +00009353 }
glennrp0fe50b42010-11-16 03:52:51 +00009354
cristy3ed852e2009-09-05 21:47:34 +00009355 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9356 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009357
glennrp136ee3a2011-04-27 15:47:45 +00009358 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009359 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009360 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009361
cristy3ed852e2009-09-05 21:47:34 +00009362 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009363 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009364
cristy3ed852e2009-09-05 21:47:34 +00009365 else
9366 {
glennrp5af765f2010-03-30 11:12:18 +00009367 ping_bit_depth=8;
9368 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009369 {
9370 if(!mng_info->write_png_depth)
9371 {
glennrp5af765f2010-03-30 11:12:18 +00009372 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009373
cristy35ef8242010-06-03 16:24:13 +00009374 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009375 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009376 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009377 }
9378 }
glennrp2b013e42010-11-24 16:55:50 +00009379
glennrp0fe50b42010-11-16 03:52:51 +00009380 else if (ping_color_type ==
9381 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009382 mng_info->IsPalette)
9383 {
cristy3ed852e2009-09-05 21:47:34 +00009384 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009385
cristy3ed852e2009-09-05 21:47:34 +00009386 int
9387 depth_4_ok=MagickTrue,
9388 depth_2_ok=MagickTrue,
9389 depth_1_ok=MagickTrue;
9390
cristybb503372010-05-27 20:51:26 +00009391 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009392 {
9393 unsigned char
9394 intensity;
9395
9396 intensity=ScaleQuantumToChar(image->colormap[i].red);
9397
9398 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9399 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9400 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9401 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009402 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009403 depth_1_ok=MagickFalse;
9404 }
glennrp2b013e42010-11-24 16:55:50 +00009405
cristy3ed852e2009-09-05 21:47:34 +00009406 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009407 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009408
cristy3ed852e2009-09-05 21:47:34 +00009409 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009410 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009411
cristy3ed852e2009-09-05 21:47:34 +00009412 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009413 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009414 }
9415 }
glennrp2b013e42010-11-24 16:55:50 +00009416
glennrp5af765f2010-03-30 11:12:18 +00009417 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009418 }
glennrp0fe50b42010-11-16 03:52:51 +00009419
cristy3ed852e2009-09-05 21:47:34 +00009420 else
glennrp0fe50b42010-11-16 03:52:51 +00009421
cristy3ed852e2009-09-05 21:47:34 +00009422 if (mng_info->IsPalette)
9423 {
glennrp17a14852010-05-10 03:01:59 +00009424 number_colors=image_colors;
9425
cristy3ed852e2009-09-05 21:47:34 +00009426 if (image_depth <= 8)
9427 {
cristy3ed852e2009-09-05 21:47:34 +00009428 /*
9429 Set image palette.
9430 */
glennrp5af765f2010-03-30 11:12:18 +00009431 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009432
glennrp58e01762011-01-07 15:28:54 +00009433 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009434 {
glennrp9c1eb072010-06-06 22:19:15 +00009435 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009436
glennrp3b51f0e2010-11-27 18:14:08 +00009437 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9439 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009440 }
glennrp0fe50b42010-11-16 03:52:51 +00009441
cristy3ed852e2009-09-05 21:47:34 +00009442 else
9443 {
cristybb503372010-05-27 20:51:26 +00009444 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009445 {
9446 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9447 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9448 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9449 }
glennrp0fe50b42010-11-16 03:52:51 +00009450
glennrp3b51f0e2010-11-27 18:14:08 +00009451 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009453 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009454 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009455
glennrp39992b42010-11-14 00:03:43 +00009456 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009457 }
glennrp0fe50b42010-11-16 03:52:51 +00009458
cristy3ed852e2009-09-05 21:47:34 +00009459 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009460 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009461 {
cristybefe4d22010-06-07 01:18:58 +00009462 size_t
9463 one;
9464
glennrp5af765f2010-03-30 11:12:18 +00009465 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009466 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009467
glennrpd17915c2011-04-29 14:24:22 +00009468 while ((one << ping_bit_depth) < (ssize_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009469 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009470 }
glennrp0fe50b42010-11-16 03:52:51 +00009471
glennrp5af765f2010-03-30 11:12:18 +00009472 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009473
glennrp58e01762011-01-07 15:28:54 +00009474 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009475 {
glennrp0fe50b42010-11-16 03:52:51 +00009476 /*
glennrpd6bf1612010-12-17 17:28:54 +00009477 * Set up trans_colors array.
9478 */
glennrp0fe50b42010-11-16 03:52:51 +00009479 assert(number_colors <= 256);
9480
glennrpd6bf1612010-12-17 17:28:54 +00009481 ping_num_trans=(unsigned short) (number_transparent +
9482 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009483
9484 if (ping_num_trans == 0)
9485 ping_have_tRNS=MagickFalse;
9486
glennrpd6bf1612010-12-17 17:28:54 +00009487 else
glennrp0fe50b42010-11-16 03:52:51 +00009488 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009489 if (logging != MagickFalse)
9490 {
9491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9492 " Scaling ping_trans_color (1)");
9493 }
glennrpd6bf1612010-12-17 17:28:54 +00009494 ping_have_tRNS=MagickTrue;
9495
9496 for (i=0; i < ping_num_trans; i++)
9497 {
9498 ping_trans_alpha[i]= (png_byte) (255-
9499 ScaleQuantumToChar(image->colormap[i].opacity));
9500 }
glennrp0fe50b42010-11-16 03:52:51 +00009501 }
9502 }
cristy3ed852e2009-09-05 21:47:34 +00009503 }
9504 }
glennrp0fe50b42010-11-16 03:52:51 +00009505
cristy3ed852e2009-09-05 21:47:34 +00009506 else
9507 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009508
cristy3ed852e2009-09-05 21:47:34 +00009509 if (image_depth < 8)
9510 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009511
cristy3ed852e2009-09-05 21:47:34 +00009512 if ((save_image_depth == 16) && (image_depth == 8))
9513 {
glennrp4f25bd02011-01-01 18:51:28 +00009514 if (logging != MagickFalse)
9515 {
9516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9517 " Scaling ping_trans_color from (%d,%d,%d)",
9518 (int) ping_trans_color.red,
9519 (int) ping_trans_color.green,
9520 (int) ping_trans_color.blue);
9521 }
9522
glennrp5af765f2010-03-30 11:12:18 +00009523 ping_trans_color.red*=0x0101;
9524 ping_trans_color.green*=0x0101;
9525 ping_trans_color.blue*=0x0101;
9526 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009527
9528 if (logging != MagickFalse)
9529 {
9530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9531 " to (%d,%d,%d)",
9532 (int) ping_trans_color.red,
9533 (int) ping_trans_color.green,
9534 (int) ping_trans_color.blue);
9535 }
cristy3ed852e2009-09-05 21:47:34 +00009536 }
9537 }
9538
cristy4383ec82011-01-05 15:42:32 +00009539 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9540 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009541
cristy3ed852e2009-09-05 21:47:34 +00009542 /*
9543 Adjust background and transparency samples in sub-8-bit grayscale files.
9544 */
glennrp5af765f2010-03-30 11:12:18 +00009545 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009546 PNG_COLOR_TYPE_GRAY)
9547 {
9548 png_uint_16
9549 maxval;
9550
cristy35ef8242010-06-03 16:24:13 +00009551 size_t
9552 one=1;
9553
cristy22ffd972010-06-03 16:51:47 +00009554 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009555
glennrp4f25bd02011-01-01 18:51:28 +00009556 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009557 {
cristy3ed852e2009-09-05 21:47:34 +00009558
glennrpa521b2f2010-10-29 04:11:03 +00009559 ping_background.gray=(png_uint_16)
cristy3ed852e2009-09-05 21:47:34 +00009560 (QuantumScale*(maxval*(PixelIntensity(&image->background_color))));
9561
9562 if (logging != MagickFalse)
9563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009564 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9566 " background_color index is %d",
9567 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009568
glennrp991d11d2010-11-12 21:55:28 +00009569 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009570 }
cristy3ed852e2009-09-05 21:47:34 +00009571
glennrp5af765f2010-03-30 11:12:18 +00009572 ping_trans_color.gray=(png_uint_16) (QuantumScale*(maxval*
9573 ping_trans_color.gray));
cristy3ed852e2009-09-05 21:47:34 +00009574 }
glennrp17a14852010-05-10 03:01:59 +00009575
glennrp26f37912010-12-23 16:22:42 +00009576 if (ping_exclude_bKGD == MagickFalse)
9577 {
glennrp1273f7b2011-02-24 03:20:30 +00009578 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009579 {
9580 /*
9581 Identify which colormap entry is the background color.
9582 */
9583
glennrp17a14852010-05-10 03:01:59 +00009584 number_colors=image_colors;
9585
glennrpa521b2f2010-10-29 04:11:03 +00009586 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9587 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009588 break;
9589
9590 ping_background.index=(png_byte) i;
9591
glennrp3b51f0e2010-11-27 18:14:08 +00009592 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009593 {
9594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009595 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009596 }
glennrp0fe50b42010-11-16 03:52:51 +00009597
cristy13d07042010-11-21 20:56:18 +00009598 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009599 {
9600 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009601
9602 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009603 {
9604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9605 " background =(%d,%d,%d)",
9606 (int) ping_background.red,
9607 (int) ping_background.green,
9608 (int) ping_background.blue);
9609 }
9610 }
glennrpa521b2f2010-10-29 04:11:03 +00009611
glennrpd6bf1612010-12-17 17:28:54 +00009612 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009613 {
glennrp3b51f0e2010-11-27 18:14:08 +00009614 if (logging != MagickFalse)
9615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9616 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009617 ping_have_bKGD = MagickFalse;
9618 }
glennrp17a14852010-05-10 03:01:59 +00009619 }
glennrp26f37912010-12-23 16:22:42 +00009620 }
glennrp17a14852010-05-10 03:01:59 +00009621
cristy3ed852e2009-09-05 21:47:34 +00009622 if (logging != MagickFalse)
9623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009624 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009625 /*
9626 Initialize compression level and filtering.
9627 */
9628 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009629 {
9630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631 " Setting up deflate compression");
9632
9633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9634 " Compression buffer size: 32768");
9635 }
9636
cristy3ed852e2009-09-05 21:47:34 +00009637 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009638
cristy3ed852e2009-09-05 21:47:34 +00009639 if (logging != MagickFalse)
9640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9641 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009642
cristy3ed852e2009-09-05 21:47:34 +00009643 png_set_compression_mem_level(ping, 9);
glennrp0fe50b42010-11-16 03:52:51 +00009644
cristy3ed852e2009-09-05 21:47:34 +00009645 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9646 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009647
cristy3ed852e2009-09-05 21:47:34 +00009648 if (quality > 9)
9649 {
9650 int
9651 level;
9652
cristybb503372010-05-27 20:51:26 +00009653 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009654
cristy3ed852e2009-09-05 21:47:34 +00009655 if (logging != MagickFalse)
9656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9657 " Compression level: %d",level);
glennrp0fe50b42010-11-16 03:52:51 +00009658
cristy3ed852e2009-09-05 21:47:34 +00009659 png_set_compression_level(ping,level);
9660 }
glennrp0fe50b42010-11-16 03:52:51 +00009661
cristy3ed852e2009-09-05 21:47:34 +00009662 else
9663 {
9664 if (logging != MagickFalse)
9665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9666 " Compression strategy: Z_HUFFMAN_ONLY");
glennrp0fe50b42010-11-16 03:52:51 +00009667
cristy3ed852e2009-09-05 21:47:34 +00009668 png_set_compression_strategy(ping, Z_HUFFMAN_ONLY);
9669 }
glennrp0fe50b42010-11-16 03:52:51 +00009670
cristy3ed852e2009-09-05 21:47:34 +00009671 if (logging != MagickFalse)
9672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9673 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009674
glennrp2b013e42010-11-24 16:55:50 +00009675#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
cristy3ed852e2009-09-05 21:47:34 +00009676 /* This became available in libpng-1.0.9. Output must be a MNG. */
9677 if (mng_info->write_mng && ((quality % 10) == 7))
9678 {
9679 if (logging != MagickFalse)
9680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9681 " Filter_type: PNG_INTRAPIXEL_DIFFERENCING");
glennrp0fe50b42010-11-16 03:52:51 +00009682
glennrp5af765f2010-03-30 11:12:18 +00009683 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
cristy3ed852e2009-09-05 21:47:34 +00009684 }
glennrp0fe50b42010-11-16 03:52:51 +00009685
cristy3ed852e2009-09-05 21:47:34 +00009686 else
9687 if (logging != MagickFalse)
9688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9689 " Filter_type: 0");
9690#endif
glennrp2b013e42010-11-24 16:55:50 +00009691
cristy3ed852e2009-09-05 21:47:34 +00009692 {
9693 int
9694 base_filter;
9695
9696 if ((quality % 10) > 5)
glennrp8640fb52010-11-23 15:48:26 +00009697 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009698
glennrp26c990a2010-11-23 02:23:20 +00009699 else
glennrp8640fb52010-11-23 15:48:26 +00009700 if ((quality % 10) != 5)
9701 base_filter=(int) quality % 10;
9702
9703 else
9704 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9705 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9706 (quality < 50))
9707 base_filter=PNG_NO_FILTERS;
9708
9709 else
9710 base_filter=PNG_ALL_FILTERS;
glennrp0fe50b42010-11-16 03:52:51 +00009711
cristy3ed852e2009-09-05 21:47:34 +00009712 if (logging != MagickFalse)
9713 {
9714 if (base_filter == PNG_ALL_FILTERS)
9715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9716 " Base filter method: ADAPTIVE");
9717 else
9718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9719 " Base filter method: NONE");
9720 }
glennrp2b013e42010-11-24 16:55:50 +00009721
cristy3ed852e2009-09-05 21:47:34 +00009722 png_set_filter(ping,PNG_FILTER_TYPE_BASE,base_filter);
9723 }
9724
glennrp823b55c2011-03-14 18:46:46 +00009725 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9726 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009727 {
9728 ResetImageProfileIterator(image);
9729 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009730 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009731 profile=GetImageProfile(image,name);
9732
9733 if (profile != (StringInfo *) NULL)
9734 {
glennrp5af765f2010-03-30 11:12:18 +00009735#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009736 if ((LocaleCompare(name,"ICC") == 0) ||
9737 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009738 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009739
9740 if (ping_exclude_iCCP == MagickFalse)
9741 {
9742 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009743#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009744 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009745#else
9746 (png_const_bytep) GetStringInfoDatum(profile),
9747#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009748 (png_uint_32) GetStringInfoLength(profile));
9749 }
glennrp26f37912010-12-23 16:22:42 +00009750 }
glennrp0fe50b42010-11-16 03:52:51 +00009751
glennrpc8cbc5d2011-01-01 00:12:34 +00009752 else
cristy3ed852e2009-09-05 21:47:34 +00009753#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009754 if (ping_exclude_zCCP == MagickFalse)
9755 {
glennrpcf002022011-01-30 02:38:15 +00009756 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009757 (unsigned char *) name,(unsigned char *) name,
9758 GetStringInfoDatum(profile),
9759 (png_uint_32) GetStringInfoLength(profile));
9760 }
9761 }
glennrp0b206f52011-01-07 04:55:32 +00009762
glennrpc8cbc5d2011-01-01 00:12:34 +00009763 if (logging != MagickFalse)
9764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9765 " Setting up text chunk with %s profile",name);
9766
9767 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009768 }
cristy3ed852e2009-09-05 21:47:34 +00009769 }
9770
9771#if defined(PNG_WRITE_sRGB_SUPPORTED)
9772 if ((mng_info->have_write_global_srgb == 0) &&
9773 ((image->rendering_intent != UndefinedIntent) ||
9774 (image->colorspace == sRGBColorspace)))
9775 {
glennrp26f37912010-12-23 16:22:42 +00009776 if (ping_exclude_sRGB == MagickFalse)
9777 {
9778 /*
9779 Note image rendering intent.
9780 */
9781 if (logging != MagickFalse)
9782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9783 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009784
glennrp26f37912010-12-23 16:22:42 +00009785 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009786 Magick_RenderingIntent_to_PNG_RenderingIntent(
9787 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009788
glennrp26f37912010-12-23 16:22:42 +00009789 if (ping_exclude_gAMA == MagickFalse)
9790 png_set_gAMA(ping,ping_info,0.45455);
9791 }
cristy3ed852e2009-09-05 21:47:34 +00009792 }
glennrp26f37912010-12-23 16:22:42 +00009793
glennrp5af765f2010-03-30 11:12:18 +00009794 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009795#endif
9796 {
glennrp2cc891a2010-12-24 13:44:32 +00009797 if (ping_exclude_gAMA == MagickFalse &&
9798 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009799 (image->gamma < .45 || image->gamma > .46)))
9800 {
cristy3ed852e2009-09-05 21:47:34 +00009801 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9802 {
9803 /*
9804 Note image gamma.
9805 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9806 */
9807 if (logging != MagickFalse)
9808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9809 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009810
cristy3ed852e2009-09-05 21:47:34 +00009811 png_set_gAMA(ping,ping_info,image->gamma);
9812 }
glennrp26f37912010-12-23 16:22:42 +00009813 }
glennrp2b013e42010-11-24 16:55:50 +00009814
glennrp26f37912010-12-23 16:22:42 +00009815 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009816 {
glennrp26f37912010-12-23 16:22:42 +00009817 if ((mng_info->have_write_global_chrm == 0) &&
9818 (image->chromaticity.red_primary.x != 0.0))
9819 {
9820 /*
9821 Note image chromaticity.
9822 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9823 */
9824 PrimaryInfo
9825 bp,
9826 gp,
9827 rp,
9828 wp;
cristy3ed852e2009-09-05 21:47:34 +00009829
glennrp26f37912010-12-23 16:22:42 +00009830 wp=image->chromaticity.white_point;
9831 rp=image->chromaticity.red_primary;
9832 gp=image->chromaticity.green_primary;
9833 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009834
glennrp26f37912010-12-23 16:22:42 +00009835 if (logging != MagickFalse)
9836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9837 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009838
glennrp26f37912010-12-23 16:22:42 +00009839 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9840 bp.x,bp.y);
9841 }
9842 }
cristy3ed852e2009-09-05 21:47:34 +00009843 }
glennrpdfd70802010-11-14 01:23:35 +00009844
glennrp5af765f2010-03-30 11:12:18 +00009845 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009846
9847 if (mng_info->write_mng)
9848 png_set_sig_bytes(ping,8);
9849
9850 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9851
glennrpd6bf1612010-12-17 17:28:54 +00009852 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009853 {
9854 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009855 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009856 {
glennrp5af765f2010-03-30 11:12:18 +00009857 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009858
glennrp5af765f2010-03-30 11:12:18 +00009859 if (ping_bit_depth < 8)
9860 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009861 }
glennrp0fe50b42010-11-16 03:52:51 +00009862
cristy3ed852e2009-09-05 21:47:34 +00009863 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009864 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009865 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009866 }
9867
glennrp0e8ea192010-12-24 18:00:33 +00009868 if (ping_need_colortype_warning != MagickFalse ||
9869 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009870 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009871 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009872 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009873 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009874 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009875 {
9876 if (logging != MagickFalse)
9877 {
glennrp0e8ea192010-12-24 18:00:33 +00009878 if (ping_need_colortype_warning != MagickFalse)
9879 {
9880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9881 " Image has transparency but tRNS chunk was excluded");
9882 }
9883
cristy3ed852e2009-09-05 21:47:34 +00009884 if (mng_info->write_png_depth)
9885 {
9886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9887 " Defined PNG:bit-depth=%u, Computed depth=%u",
9888 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009889 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009890 }
glennrp0e8ea192010-12-24 18:00:33 +00009891
cristy3ed852e2009-09-05 21:47:34 +00009892 if (mng_info->write_png_colortype)
9893 {
9894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9895 " Defined PNG:color-type=%u, Computed color type=%u",
9896 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009897 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009898 }
9899 }
glennrp0e8ea192010-12-24 18:00:33 +00009900
glennrp3bd2e412010-08-10 13:34:52 +00009901 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009902 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9903 }
9904
glennrp58e01762011-01-07 15:28:54 +00009905 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009906 {
9907 /* Add an opaque matte channel */
9908 image->matte = MagickTrue;
9909 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009910
glennrpb4a13412010-05-05 12:47:19 +00009911 if (logging != MagickFalse)
9912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9913 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009914 }
9915
glennrp0e319732011-01-25 21:53:13 +00009916 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009917 {
glennrp991d11d2010-11-12 21:55:28 +00009918 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009919 {
glennrp991d11d2010-11-12 21:55:28 +00009920 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009921 if (logging != MagickFalse)
9922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9923 " Setting ping_have_tRNS=MagickTrue.");
9924 }
glennrpe9c26dc2010-05-30 01:56:35 +00009925 }
9926
cristy3ed852e2009-09-05 21:47:34 +00009927 if (logging != MagickFalse)
9928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9929 " Writing PNG header chunks");
9930
glennrp5af765f2010-03-30 11:12:18 +00009931 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9932 ping_bit_depth,ping_color_type,
9933 ping_interlace_method,ping_compression_method,
9934 ping_filter_method);
9935
glennrp39992b42010-11-14 00:03:43 +00009936 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9937 {
glennrpf09bded2011-01-08 01:15:59 +00009938 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009939
glennrp3b51f0e2010-11-27 18:14:08 +00009940 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009941 {
glennrp8640fb52010-11-23 15:48:26 +00009942 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009943 {
glennrpd6bf1612010-12-17 17:28:54 +00009944 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009946 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9947 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009948 (int) palette[i].red,
9949 (int) palette[i].green,
9950 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009951 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009952 (int) ping_trans_alpha[i]);
9953 else
9954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009955 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009956 (int) i,
9957 (int) palette[i].red,
9958 (int) palette[i].green,
9959 (int) palette[i].blue);
9960 }
glennrp39992b42010-11-14 00:03:43 +00009961 }
glennrp39992b42010-11-14 00:03:43 +00009962 }
9963
glennrp26f37912010-12-23 16:22:42 +00009964 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009965 {
glennrp26f37912010-12-23 16:22:42 +00009966 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +00009967 {
glennrp26f37912010-12-23 16:22:42 +00009968 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +00009969 if (logging)
9970 {
9971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9972 " Setting up bKGD chunk");
9973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9974 " background color = (%d,%d,%d)",
9975 (int) ping_background.red,
9976 (int) ping_background.green,
9977 (int) ping_background.blue);
9978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9979 " index = %d, gray=%d",
9980 (int) ping_background.index,
9981 (int) ping_background.gray);
9982 }
9983 }
glennrp26f37912010-12-23 16:22:42 +00009984 }
9985
9986 if (ping_exclude_pHYs == MagickFalse)
9987 {
9988 if (ping_have_pHYs != MagickFalse)
9989 {
9990 png_set_pHYs(ping,ping_info,
9991 ping_pHYs_x_resolution,
9992 ping_pHYs_y_resolution,
9993 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +00009994
9995 if (logging)
9996 {
9997 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9998 " Setting up pHYs chunk");
9999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10000 " x_resolution=%lu",
10001 (unsigned long) ping_pHYs_x_resolution);
10002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10003 " y_resolution=%lu",
10004 (unsigned long) ping_pHYs_y_resolution);
10005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10006 " unit_type=%lu",
10007 (unsigned long) ping_pHYs_unit_type);
10008 }
glennrp26f37912010-12-23 16:22:42 +000010009 }
glennrpdfd70802010-11-14 01:23:35 +000010010 }
10011
10012#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010013 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010014 {
glennrp26f37912010-12-23 16:22:42 +000010015 if (image->page.x || image->page.y)
10016 {
10017 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10018 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010019
glennrp26f37912010-12-23 16:22:42 +000010020 if (logging != MagickFalse)
10021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10022 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10023 (int) image->page.x, (int) image->page.y);
10024 }
glennrpdfd70802010-11-14 01:23:35 +000010025 }
10026#endif
10027
glennrpda8f3a72011-02-27 23:54:12 +000010028 if (mng_info->need_blob != MagickFalse)
10029 {
10030 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10031 MagickFalse)
10032 png_error(ping,"WriteBlob Failed");
10033
10034 ping_have_blob=MagickTrue;
10035 }
10036
cristy3ed852e2009-09-05 21:47:34 +000010037 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010038
glennrp39992b42010-11-14 00:03:43 +000010039 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010040 {
glennrp3b51f0e2010-11-27 18:14:08 +000010041 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010042 {
10043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10044 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10045 }
10046
10047 if (ping_color_type == 3)
10048 (void) png_set_tRNS(ping, ping_info,
10049 ping_trans_alpha,
10050 ping_num_trans,
10051 NULL);
10052
10053 else
10054 {
10055 (void) png_set_tRNS(ping, ping_info,
10056 NULL,
10057 0,
10058 &ping_trans_color);
10059
glennrp3b51f0e2010-11-27 18:14:08 +000010060 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010061 {
10062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010063 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010064 (int) ping_trans_color.red,
10065 (int) ping_trans_color.green,
10066 (int) ping_trans_color.blue);
10067 }
10068 }
glennrp991d11d2010-11-12 21:55:28 +000010069 }
10070
cristy3ed852e2009-09-05 21:47:34 +000010071 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010072 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010073
cristy3ed852e2009-09-05 21:47:34 +000010074 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010075
cristy3ed852e2009-09-05 21:47:34 +000010076 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010077 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010078
glennrp26f37912010-12-23 16:22:42 +000010079 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010080 {
glennrp4f25bd02011-01-01 18:51:28 +000010081 if ((image->page.width != 0 && image->page.width != image->columns) ||
10082 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010083 {
10084 unsigned char
10085 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010086
glennrp26f37912010-12-23 16:22:42 +000010087 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10088 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010089 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010090 PNGLong(chunk+4,(png_uint_32) image->page.width);
10091 PNGLong(chunk+8,(png_uint_32) image->page.height);
10092 chunk[12]=0; /* unit = pixels */
10093 (void) WriteBlob(image,13,chunk);
10094 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10095 }
cristy3ed852e2009-09-05 21:47:34 +000010096 }
10097
10098#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010099 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010100#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010101 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010102#undef PNG_HAVE_IDAT
10103#endif
10104
10105 png_set_packing(ping);
10106 /*
10107 Allocate memory.
10108 */
10109 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010110 if (image_depth > 8)
10111 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010112 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010113 {
glennrpb4a13412010-05-05 12:47:19 +000010114 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010115 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010116 break;
glennrp0fe50b42010-11-16 03:52:51 +000010117
glennrpb4a13412010-05-05 12:47:19 +000010118 case PNG_COLOR_TYPE_GRAY_ALPHA:
10119 rowbytes*=2;
10120 break;
glennrp0fe50b42010-11-16 03:52:51 +000010121
glennrpb4a13412010-05-05 12:47:19 +000010122 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010123 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010124 break;
glennrp0fe50b42010-11-16 03:52:51 +000010125
glennrpb4a13412010-05-05 12:47:19 +000010126 default:
10127 break;
cristy3ed852e2009-09-05 21:47:34 +000010128 }
glennrp3b51f0e2010-11-27 18:14:08 +000010129
10130 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010131 {
10132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10133 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010134
glennrpb4a13412010-05-05 12:47:19 +000010135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010136 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010137 }
glennrpcf002022011-01-30 02:38:15 +000010138 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10139 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010140
glennrpcf002022011-01-30 02:38:15 +000010141 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010142 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010143
cristy3ed852e2009-09-05 21:47:34 +000010144 /*
10145 Initialize image scanlines.
10146 */
glennrp5af765f2010-03-30 11:12:18 +000010147 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010148 {
10149 /*
10150 PNG write failed.
10151 */
10152#ifdef PNG_DEBUG
10153 if (image_info->verbose)
10154 (void) printf("PNG write has failed.\n");
10155#endif
10156 png_destroy_write_struct(&ping,&ping_info);
10157 if (quantum_info != (QuantumInfo *) NULL)
10158 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010159 if (ping_pixels != (unsigned char *) NULL)
10160 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010161#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010162 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010163#endif
glennrpda8f3a72011-02-27 23:54:12 +000010164 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010165 (void) CloseBlob(image);
10166 image_info=DestroyImageInfo(image_info);
10167 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010168 return(MagickFalse);
10169 }
cristyed552522009-10-16 14:04:35 +000010170 quantum_info=AcquireQuantumInfo(image_info,image);
10171 if (quantum_info == (QuantumInfo *) NULL)
10172 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010173 quantum_info->format=UndefinedQuantumFormat;
10174 quantum_info->depth=image_depth;
10175 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010176
cristy3ed852e2009-09-05 21:47:34 +000010177 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010178 !mng_info->write_png32) &&
10179 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010180 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010181 image_matte == MagickFalse &&
10182 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010183 {
glennrp8bb3a022010-12-13 20:40:04 +000010184 /* Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010185 register const PixelPacket
10186 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010187
cristy3ed852e2009-09-05 21:47:34 +000010188 quantum_info->depth=8;
10189 for (pass=0; pass < num_passes; pass++)
10190 {
10191 /*
10192 Convert PseudoClass image to a PNG monochrome image.
10193 */
cristybb503372010-05-27 20:51:26 +000010194 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010195 {
glennrpd71e86a2011-02-24 01:28:37 +000010196 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10198 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010199
cristy3ed852e2009-09-05 21:47:34 +000010200 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010201
cristy3ed852e2009-09-05 21:47:34 +000010202 if (p == (const PixelPacket *) NULL)
10203 break;
glennrp0fe50b42010-11-16 03:52:51 +000010204
cristy3ed852e2009-09-05 21:47:34 +000010205 if (mng_info->IsPalette)
10206 {
10207 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010208 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010209 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10210 mng_info->write_png_depth &&
10211 mng_info->write_png_depth != old_bit_depth)
10212 {
10213 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010214 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010215 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010216 >> (8-old_bit_depth));
10217 }
10218 }
glennrp0fe50b42010-11-16 03:52:51 +000010219
cristy3ed852e2009-09-05 21:47:34 +000010220 else
10221 {
10222 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010223 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010224 }
glennrp0fe50b42010-11-16 03:52:51 +000010225
cristy3ed852e2009-09-05 21:47:34 +000010226 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010227 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010228 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010229 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010230
glennrp3b51f0e2010-11-27 18:14:08 +000010231 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10233 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010234
glennrpcf002022011-01-30 02:38:15 +000010235 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010236 }
10237 if (image->previous == (Image *) NULL)
10238 {
10239 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10240 if (status == MagickFalse)
10241 break;
10242 }
10243 }
10244 }
glennrp0fe50b42010-11-16 03:52:51 +000010245
glennrp8bb3a022010-12-13 20:40:04 +000010246 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010247 {
glennrp0fe50b42010-11-16 03:52:51 +000010248 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010249 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010250 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010251 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010252 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010253 {
glennrp8bb3a022010-12-13 20:40:04 +000010254 register const PixelPacket
10255 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010256
glennrp8bb3a022010-12-13 20:40:04 +000010257 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010258 {
glennrp8bb3a022010-12-13 20:40:04 +000010259
cristybb503372010-05-27 20:51:26 +000010260 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010261 {
10262 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010263
cristy3ed852e2009-09-05 21:47:34 +000010264 if (p == (const PixelPacket *) NULL)
10265 break;
glennrp2cc891a2010-12-24 13:44:32 +000010266
glennrp5af765f2010-03-30 11:12:18 +000010267 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010268 {
glennrp8bb3a022010-12-13 20:40:04 +000010269 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +000010270 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010271 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010272
glennrp8bb3a022010-12-13 20:40:04 +000010273 else
10274 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010275 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010276
glennrp3b51f0e2010-11-27 18:14:08 +000010277 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010279 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010280 }
glennrp2cc891a2010-12-24 13:44:32 +000010281
glennrp8bb3a022010-12-13 20:40:04 +000010282 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10283 {
10284 if (logging != MagickFalse && y == 0)
10285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10286 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010287
glennrp8bb3a022010-12-13 20:40:04 +000010288 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010289 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010290 }
glennrp2cc891a2010-12-24 13:44:32 +000010291
glennrp3b51f0e2010-11-27 18:14:08 +000010292 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010294 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010295
glennrpcf002022011-01-30 02:38:15 +000010296 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010297 }
glennrp2cc891a2010-12-24 13:44:32 +000010298
glennrp8bb3a022010-12-13 20:40:04 +000010299 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010300 {
glennrp8bb3a022010-12-13 20:40:04 +000010301 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10302 if (status == MagickFalse)
10303 break;
cristy3ed852e2009-09-05 21:47:34 +000010304 }
cristy3ed852e2009-09-05 21:47:34 +000010305 }
10306 }
glennrp8bb3a022010-12-13 20:40:04 +000010307
10308 else
10309 {
10310 register const PixelPacket
10311 *p;
10312
10313 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010314 {
glennrp8bb3a022010-12-13 20:40:04 +000010315 if ((image_depth > 8) || (mng_info->write_png24 ||
10316 mng_info->write_png32 ||
10317 (!mng_info->write_png8 && !mng_info->IsPalette)))
10318 {
10319 for (y=0; y < (ssize_t) image->rows; y++)
10320 {
10321 p=GetVirtualPixels(image,0,y,image->columns,1,
10322 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010323
glennrp8bb3a022010-12-13 20:40:04 +000010324 if (p == (const PixelPacket *) NULL)
10325 break;
glennrp2cc891a2010-12-24 13:44:32 +000010326
glennrp8bb3a022010-12-13 20:40:04 +000010327 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10328 {
10329 if (image->storage_class == DirectClass)
10330 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010331 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010332
glennrp8bb3a022010-12-13 20:40:04 +000010333 else
10334 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010335 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010336 }
glennrp2cc891a2010-12-24 13:44:32 +000010337
glennrp8bb3a022010-12-13 20:40:04 +000010338 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10339 {
10340 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010341 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +000010342 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010343
glennrp8bb3a022010-12-13 20:40:04 +000010344 if (logging != MagickFalse && y == 0)
10345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10346 " Writing GRAY_ALPHA PNG pixels (3)");
10347 }
glennrp2cc891a2010-12-24 13:44:32 +000010348
glennrp8bb3a022010-12-13 20:40:04 +000010349 else if (image_matte != MagickFalse)
10350 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010351 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010352
glennrp8bb3a022010-12-13 20:40:04 +000010353 else
10354 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010355 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010356
glennrp8bb3a022010-12-13 20:40:04 +000010357 if (logging != MagickFalse && y == 0)
10358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10359 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010360
glennrpcf002022011-01-30 02:38:15 +000010361 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010362 }
10363 }
glennrp2cc891a2010-12-24 13:44:32 +000010364
glennrp8bb3a022010-12-13 20:40:04 +000010365 else
10366 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10367 mng_info->write_png32 ||
10368 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10369 {
10370 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10371 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10372 {
10373 if (logging != MagickFalse)
10374 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10375 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010376
glennrp8bb3a022010-12-13 20:40:04 +000010377 quantum_info->depth=8;
10378 image_depth=8;
10379 }
glennrp2cc891a2010-12-24 13:44:32 +000010380
glennrp8bb3a022010-12-13 20:40:04 +000010381 for (y=0; y < (ssize_t) image->rows; y++)
10382 {
10383 if (logging != MagickFalse && y == 0)
10384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10385 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010386
glennrp770d1932011-03-06 22:11:17 +000010387 p=GetVirtualPixels(image,0,y,image->columns,1,
10388 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010389
glennrp8bb3a022010-12-13 20:40:04 +000010390 if (p == (const PixelPacket *) NULL)
10391 break;
glennrp2cc891a2010-12-24 13:44:32 +000010392
glennrp8bb3a022010-12-13 20:40:04 +000010393 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010394 {
glennrp4bf89732011-03-21 13:48:28 +000010395 quantum_info->depth=image->depth;
10396
glennrp44757ab2011-03-17 12:57:03 +000010397 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010398 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +000010399 }
glennrp2cc891a2010-12-24 13:44:32 +000010400
glennrp8bb3a022010-12-13 20:40:04 +000010401 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10402 {
10403 if (logging != MagickFalse && y == 0)
10404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10405 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010406
glennrp8bb3a022010-12-13 20:40:04 +000010407 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010408 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +000010409 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010410 }
glennrp2cc891a2010-12-24 13:44:32 +000010411
glennrp8bb3a022010-12-13 20:40:04 +000010412 else
glennrp8bb3a022010-12-13 20:40:04 +000010413 {
glennrp179d0752011-03-17 13:02:10 +000010414 (void) ExportQuantumPixels(image,(const CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +000010415 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10416
10417 if (logging != MagickFalse && y <= 2)
10418 {
10419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010420 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010421
10422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10423 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10424 (int)ping_pixels[0],(int)ping_pixels[1]);
10425 }
glennrp8bb3a022010-12-13 20:40:04 +000010426 }
glennrpcf002022011-01-30 02:38:15 +000010427 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010428 }
10429 }
glennrp2cc891a2010-12-24 13:44:32 +000010430
glennrp8bb3a022010-12-13 20:40:04 +000010431 if (image->previous == (Image *) NULL)
10432 {
10433 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10434 if (status == MagickFalse)
10435 break;
10436 }
cristy3ed852e2009-09-05 21:47:34 +000010437 }
glennrp8bb3a022010-12-13 20:40:04 +000010438 }
10439 }
10440
cristyb32b90a2009-09-07 21:45:48 +000010441 if (quantum_info != (QuantumInfo *) NULL)
10442 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010443
10444 if (logging != MagickFalse)
10445 {
10446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010447 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010448
cristy3ed852e2009-09-05 21:47:34 +000010449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010450 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010451
cristy3ed852e2009-09-05 21:47:34 +000010452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010453 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010454
cristy3ed852e2009-09-05 21:47:34 +000010455 if (mng_info->write_png_depth)
10456 {
10457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10458 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10459 }
glennrp0fe50b42010-11-16 03:52:51 +000010460
cristy3ed852e2009-09-05 21:47:34 +000010461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010462 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010463
cristy3ed852e2009-09-05 21:47:34 +000010464 if (mng_info->write_png_colortype)
10465 {
10466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10467 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10468 }
glennrp0fe50b42010-11-16 03:52:51 +000010469
cristy3ed852e2009-09-05 21:47:34 +000010470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010471 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010472
cristy3ed852e2009-09-05 21:47:34 +000010473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010474 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010475 }
10476 /*
glennrpa0ed0092011-04-18 16:36:29 +000010477 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010478 */
glennrp823b55c2011-03-14 18:46:46 +000010479 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010480 {
glennrp26f37912010-12-23 16:22:42 +000010481 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010482 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010483 while (property != (const char *) NULL)
10484 {
10485 png_textp
10486 text;
glennrp2cc891a2010-12-24 13:44:32 +000010487
glennrp26f37912010-12-23 16:22:42 +000010488 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +000010489
10490 /* Don't write any "png:" properties; those are just for "identify" */
10491 if (LocaleNCompare(property,"png:",4) != 0 &&
10492
10493 /* Suppress density and units if we wrote a pHYs chunk */
10494 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010495 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010496 LocaleCompare(property,"units") != 0) &&
10497
10498 /* Suppress the IM-generated Date:create and Date:modify */
10499 (ping_exclude_date == MagickFalse ||
10500 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010501 {
glennrpc70af4a2011-03-07 00:08:23 +000010502 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010503 {
glennrpc70af4a2011-03-07 00:08:23 +000010504 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10505 text[0].key=(char *) property;
10506 text[0].text=(char *) value;
10507 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010508
glennrpc70af4a2011-03-07 00:08:23 +000010509 if (ping_exclude_tEXt != MagickFalse)
10510 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10511
10512 else if (ping_exclude_zTXt != MagickFalse)
10513 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10514
10515 else
glennrp26f37912010-12-23 16:22:42 +000010516 {
glennrpc70af4a2011-03-07 00:08:23 +000010517 text[0].compression=image_info->compression == NoCompression ||
10518 (image_info->compression == UndefinedCompression &&
10519 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10520 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010521 }
glennrp2cc891a2010-12-24 13:44:32 +000010522
glennrpc70af4a2011-03-07 00:08:23 +000010523 if (logging != MagickFalse)
10524 {
10525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10526 " Setting up text chunk");
10527
10528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10529 " keyword: %s",text[0].key);
10530 }
10531
10532 png_set_text(ping,ping_info,text,1);
10533 png_free(ping,text);
10534 }
glennrp26f37912010-12-23 16:22:42 +000010535 }
10536 property=GetNextImageProperty(image);
10537 }
cristy3ed852e2009-09-05 21:47:34 +000010538 }
10539
10540 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010541 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010542
10543 if (logging != MagickFalse)
10544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10545 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010546
cristy3ed852e2009-09-05 21:47:34 +000010547 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010548
cristy3ed852e2009-09-05 21:47:34 +000010549 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10550 {
10551 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010552 (ping_width != mng_info->page.width) ||
10553 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010554 {
10555 unsigned char
10556 chunk[32];
10557
10558 /*
10559 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10560 */
10561 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10562 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010563 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010564 chunk[4]=4;
10565 chunk[5]=0; /* frame name separator (no name) */
10566 chunk[6]=1; /* flag for changing delay, for next frame only */
10567 chunk[7]=0; /* flag for changing frame timeout */
10568 chunk[8]=1; /* flag for changing frame clipping for next frame */
10569 chunk[9]=0; /* flag for changing frame sync_id */
10570 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10571 chunk[14]=0; /* clipping boundaries delta type */
10572 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10573 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010574 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010575 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10576 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010577 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010578 (void) WriteBlob(image,31,chunk);
10579 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10580 mng_info->old_framing_mode=4;
10581 mng_info->framing_mode=1;
10582 }
glennrp0fe50b42010-11-16 03:52:51 +000010583
cristy3ed852e2009-09-05 21:47:34 +000010584 else
10585 mng_info->framing_mode=3;
10586 }
10587 if (mng_info->write_mng && !mng_info->need_fram &&
10588 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +000010589 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010590 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010591 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010592
cristy3ed852e2009-09-05 21:47:34 +000010593 /*
10594 Free PNG resources.
10595 */
glennrp5af765f2010-03-30 11:12:18 +000010596
cristy3ed852e2009-09-05 21:47:34 +000010597 png_destroy_write_struct(&ping,&ping_info);
10598
glennrpcf002022011-01-30 02:38:15 +000010599 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010600
10601#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010602 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010603#endif
10604
glennrpda8f3a72011-02-27 23:54:12 +000010605 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010606 (void) CloseBlob(image);
10607
10608 image_info=DestroyImageInfo(image_info);
10609 image=DestroyImage(image);
10610
10611 /* Store bit depth actually written */
10612 s[0]=(char) ping_bit_depth;
10613 s[1]='\0';
10614
10615 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10616
cristy3ed852e2009-09-05 21:47:34 +000010617 if (logging != MagickFalse)
10618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10619 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010620
cristy3ed852e2009-09-05 21:47:34 +000010621 return(MagickTrue);
10622/* End write one PNG image */
10623}
10624
10625/*
10626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10627% %
10628% %
10629% %
10630% W r i t e P N G I m a g e %
10631% %
10632% %
10633% %
10634%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10635%
10636% WritePNGImage() writes a Portable Network Graphics (PNG) or
10637% Multiple-image Network Graphics (MNG) image file.
10638%
10639% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10640%
10641% The format of the WritePNGImage method is:
10642%
10643% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10644%
10645% A description of each parameter follows:
10646%
10647% o image_info: the image info.
10648%
10649% o image: The image.
10650%
10651% Returns MagickTrue on success, MagickFalse on failure.
10652%
10653% Communicating with the PNG encoder:
10654%
10655% While the datastream written is always in PNG format and normally would
10656% be given the "png" file extension, this method also writes the following
10657% pseudo-formats which are subsets of PNG:
10658%
glennrp5a39f372011-02-25 04:52:16 +000010659% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10660% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010661% is present, the tRNS chunk must only have values 0 and 255
10662% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010663% transparent). If other values are present they will be
10664% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010665% colors are present, they will be quantized to the 4-4-4-1,
10666% 3-3-3-1, or 3-3-2-1 palette.
10667%
10668% If you want better quantization or dithering of the colors
10669% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010670% PNG encoder. The pixels contain 8-bit indices even if
10671% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010672% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010673% PNG grayscale type might be slightly more efficient. Please
10674% note that writing to the PNG8 format may result in loss
10675% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010676%
10677% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10678% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010679% one of the colors as transparent. The only loss incurred
10680% is reduction of sample depth to 8. If the image has more
10681% than one transparent color, has semitransparent pixels, or
10682% has an opaque pixel with the same RGB components as the
10683% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010684%
10685% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10686% transparency is permitted, i.e., the alpha sample for
10687% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010688% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010689% The only loss in data is the reduction of the sample depth
10690% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010691%
10692% o -define: For more precise control of the PNG output, you can use the
10693% Image options "png:bit-depth" and "png:color-type". These
10694% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010695% from the application programming interfaces. The options
10696% are case-independent and are converted to lowercase before
10697% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010698%
10699% png:color-type can be 0, 2, 3, 4, or 6.
10700%
10701% When png:color-type is 0 (Grayscale), png:bit-depth can
10702% be 1, 2, 4, 8, or 16.
10703%
10704% When png:color-type is 2 (RGB), png:bit-depth can
10705% be 8 or 16.
10706%
10707% When png:color-type is 3 (Indexed), png:bit-depth can
10708% be 1, 2, 4, or 8. This refers to the number of bits
10709% used to store the index. The color samples always have
10710% bit-depth 8 in indexed PNG files.
10711%
10712% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10713% png:bit-depth can be 8 or 16.
10714%
glennrp5a39f372011-02-25 04:52:16 +000010715% If the image cannot be written without loss with the requested bit-depth
10716% and color-type, a PNG file will not be written, and the encoder will
10717% return MagickFalse.
10718%
cristy3ed852e2009-09-05 21:47:34 +000010719% Since image encoders should not be responsible for the "heavy lifting",
10720% the user should make sure that ImageMagick has already reduced the
10721% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010722% transparency prior to attempting to write the image with depth, color,
10723% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010724%
glennrp97cefe22011-04-22 16:17:00 +000010725% To do: Enforce the previous paragraph.
cristy3ed852e2009-09-05 21:47:34 +000010726%
cristy3ed852e2009-09-05 21:47:34 +000010727% Note that another definition, "png:bit-depth-written" exists, but it
10728% is not intended for external use. It is only used internally by the
10729% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10730%
10731% It is possible to request that the PNG encoder write previously-formatted
10732% ancillary chunks in the output PNG file, using the "-profile" commandline
10733% option as shown below or by setting the profile via a programming
10734% interface:
10735%
10736% -profile PNG-chunk-x:<file>
10737%
10738% where x is a location flag and <file> is a file containing the chunk
10739% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010740% This encoder will compute the chunk length and CRC, so those must not
10741% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010742%
10743% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10744% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10745% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010746% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010747%
glennrpbb8a7332010-11-13 15:17:35 +000010748% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010749%
glennrp3241bd02010-12-12 04:36:28 +000010750% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010751%
glennrpd6afd542010-11-19 01:53:05 +000010752% o 32-bit depth is reduced to 16.
10753% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10754% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010755% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010756% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010757% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010758% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10759% this can be done without loss and a larger bit depth N was not
10760% requested via the "-define PNG:bit-depth=N" option.
10761% o If matte channel is present but only one transparent color is
10762% present, RGB+tRNS is written instead of RGBA
10763% o Opaque matte channel is removed (or added, if color-type 4 or 6
10764% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010765%
cristy3ed852e2009-09-05 21:47:34 +000010766%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10767*/
10768static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10769 Image *image)
10770{
10771 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010772 excluding,
10773 logging,
10774 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010775 status;
10776
10777 MngInfo
10778 *mng_info;
10779
10780 const char
10781 *value;
10782
10783 int
glennrp21f0e622011-01-07 16:20:57 +000010784 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010785 source;
10786
cristy3ed852e2009-09-05 21:47:34 +000010787 /*
10788 Open image file.
10789 */
10790 assert(image_info != (const ImageInfo *) NULL);
10791 assert(image_info->signature == MagickSignature);
10792 assert(image != (Image *) NULL);
10793 assert(image->signature == MagickSignature);
10794 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010795 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010796 /*
10797 Allocate a MngInfo structure.
10798 */
10799 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010800 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010801
cristy3ed852e2009-09-05 21:47:34 +000010802 if (mng_info == (MngInfo *) NULL)
10803 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010804
cristy3ed852e2009-09-05 21:47:34 +000010805 /*
10806 Initialize members of the MngInfo structure.
10807 */
10808 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10809 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010810 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010811 have_mng_structure=MagickTrue;
10812
10813 /* See if user has requested a specific PNG subformat */
10814
10815 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10816 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10817 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10818
10819 if (mng_info->write_png8)
10820 {
glennrp9c1eb072010-06-06 22:19:15 +000010821 mng_info->write_png_colortype = /* 3 */ 4;
10822 mng_info->write_png_depth = 8;
10823 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010824 }
10825
10826 if (mng_info->write_png24)
10827 {
glennrp9c1eb072010-06-06 22:19:15 +000010828 mng_info->write_png_colortype = /* 2 */ 3;
10829 mng_info->write_png_depth = 8;
10830 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010831
glennrp9c1eb072010-06-06 22:19:15 +000010832 if (image->matte == MagickTrue)
10833 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010834
glennrp9c1eb072010-06-06 22:19:15 +000010835 else
10836 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010837
glennrp9c1eb072010-06-06 22:19:15 +000010838 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010839 }
10840
10841 if (mng_info->write_png32)
10842 {
glennrp9c1eb072010-06-06 22:19:15 +000010843 mng_info->write_png_colortype = /* 6 */ 7;
10844 mng_info->write_png_depth = 8;
10845 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010846
glennrp9c1eb072010-06-06 22:19:15 +000010847 if (image->matte == MagickTrue)
10848 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010849
glennrp9c1eb072010-06-06 22:19:15 +000010850 else
10851 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010852
glennrp9c1eb072010-06-06 22:19:15 +000010853 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010854 }
10855
10856 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010857
cristy3ed852e2009-09-05 21:47:34 +000010858 if (value != (char *) NULL)
10859 {
10860 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010861 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010862
cristy3ed852e2009-09-05 21:47:34 +000010863 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010864 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010865
cristy3ed852e2009-09-05 21:47:34 +000010866 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010867 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010868
cristy3ed852e2009-09-05 21:47:34 +000010869 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010870 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010871
cristy3ed852e2009-09-05 21:47:34 +000010872 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010873 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010874
glennrpbb8a7332010-11-13 15:17:35 +000010875 else
10876 (void) ThrowMagickException(&image->exception,
10877 GetMagickModule(),CoderWarning,
10878 "ignoring invalid defined png:bit-depth",
10879 "=%s",value);
10880
cristy3ed852e2009-09-05 21:47:34 +000010881 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010883 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010884 }
glennrp0fe50b42010-11-16 03:52:51 +000010885
cristy3ed852e2009-09-05 21:47:34 +000010886 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010887
cristy3ed852e2009-09-05 21:47:34 +000010888 if (value != (char *) NULL)
10889 {
10890 /* We must store colortype+1 because 0 is a valid colortype */
10891 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010892 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010893
cristy3ed852e2009-09-05 21:47:34 +000010894 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010895 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010896
cristy3ed852e2009-09-05 21:47:34 +000010897 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010898 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010899
cristy3ed852e2009-09-05 21:47:34 +000010900 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010901 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010902
cristy3ed852e2009-09-05 21:47:34 +000010903 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010904 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010905
glennrpbb8a7332010-11-13 15:17:35 +000010906 else
10907 (void) ThrowMagickException(&image->exception,
10908 GetMagickModule(),CoderWarning,
10909 "ignoring invalid defined png:color-type",
10910 "=%s",value);
10911
cristy3ed852e2009-09-05 21:47:34 +000010912 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010914 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010915 }
10916
glennrp0e8ea192010-12-24 18:00:33 +000010917 /* Check for chunks to be excluded:
10918 *
glennrp0dff56c2011-01-29 19:10:02 +000010919 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010920 * listed in the "unused_chunks" array, above.
10921 *
10922 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10923 * define (in the image properties or in the image artifacts)
10924 * or via a mng_info member. For convenience, in addition
10925 * to or instead of a comma-separated list of chunks, the
10926 * "exclude-chunk" string can be simply "all" or "none".
10927 *
10928 * The exclude-chunk define takes priority over the mng_info.
10929 *
10930 * A "PNG:include-chunk" define takes priority over both the
10931 * mng_info and the "PNG:exclude-chunk" define. Like the
10932 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010933 * well as a comma-separated list. Chunks that are unknown to
10934 * ImageMagick are always excluded, regardless of their "copy-safe"
10935 * status according to the PNG specification, and even if they
10936 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010937 *
10938 * Finally, all chunks listed in the "unused_chunks" array are
10939 * automatically excluded, regardless of the other instructions
10940 * or lack thereof.
10941 *
10942 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10943 * will not be written and the gAMA chunk will only be written if it
10944 * is not between .45 and .46, or approximately (1.0/2.2).
10945 *
10946 * If you exclude tRNS and the image has transparency, the colortype
10947 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10948 *
10949 * The -strip option causes StripImage() to set the png:include-chunk
10950 * artifact to "none,gama".
10951 */
10952
glennrp26f37912010-12-23 16:22:42 +000010953 mng_info->ping_exclude_bKGD=MagickFalse;
10954 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010955 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010956 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10957 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010958 mng_info->ping_exclude_iCCP=MagickFalse;
10959 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10960 mng_info->ping_exclude_oFFs=MagickFalse;
10961 mng_info->ping_exclude_pHYs=MagickFalse;
10962 mng_info->ping_exclude_sRGB=MagickFalse;
10963 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010964 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010965 mng_info->ping_exclude_vpAg=MagickFalse;
10966 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10967 mng_info->ping_exclude_zTXt=MagickFalse;
10968
glennrp8d3d6e52011-04-19 04:39:51 +000010969 mng_info->ping_preserve_colormap=MagickFalse;
10970
10971 value=GetImageArtifact(image,"png:preserve-colormap");
10972 if (value == NULL)
10973 value=GetImageOption(image_info,"png:preserve-colormap");
10974 if (value != NULL)
10975 mng_info->ping_preserve_colormap=MagickTrue;
10976
glennrp03812ae2010-12-24 01:31:34 +000010977 excluding=MagickFalse;
10978
glennrp5c7cf4e2010-12-24 00:30:00 +000010979 for (source=0; source<1; source++)
10980 {
10981 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000010982 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010983 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000010984
10985 if (value == NULL)
10986 value=GetImageArtifact(image,"png:exclude-chunks");
10987 }
glennrp5c7cf4e2010-12-24 00:30:00 +000010988 else
glennrpacba0042010-12-24 14:27:26 +000010989 {
glennrp5c7cf4e2010-12-24 00:30:00 +000010990 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000010991
glennrpacba0042010-12-24 14:27:26 +000010992 if (value == NULL)
10993 value=GetImageOption(image_info,"png:exclude-chunks");
10994 }
10995
glennrp03812ae2010-12-24 01:31:34 +000010996 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000010997 {
glennrp03812ae2010-12-24 01:31:34 +000010998
10999 size_t
11000 last;
11001
11002 excluding=MagickTrue;
11003
11004 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011005 {
11006 if (source == 0)
11007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11008 " png:exclude-chunk=%s found in image artifacts.\n", value);
11009 else
11010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11011 " png:exclude-chunk=%s found in image properties.\n", value);
11012 }
glennrp03812ae2010-12-24 01:31:34 +000011013
11014 last=strlen(value);
11015
11016 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011017 {
glennrp03812ae2010-12-24 01:31:34 +000011018
11019 if (LocaleNCompare(value+i,"all",3) == 0)
11020 {
11021 mng_info->ping_exclude_bKGD=MagickTrue;
11022 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011023 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011024 mng_info->ping_exclude_EXIF=MagickTrue;
11025 mng_info->ping_exclude_gAMA=MagickTrue;
11026 mng_info->ping_exclude_iCCP=MagickTrue;
11027 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11028 mng_info->ping_exclude_oFFs=MagickTrue;
11029 mng_info->ping_exclude_pHYs=MagickTrue;
11030 mng_info->ping_exclude_sRGB=MagickTrue;
11031 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011032 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011033 mng_info->ping_exclude_vpAg=MagickTrue;
11034 mng_info->ping_exclude_zCCP=MagickTrue;
11035 mng_info->ping_exclude_zTXt=MagickTrue;
11036 i--;
11037 }
glennrp2cc891a2010-12-24 13:44:32 +000011038
glennrp03812ae2010-12-24 01:31:34 +000011039 if (LocaleNCompare(value+i,"none",4) == 0)
11040 {
11041 mng_info->ping_exclude_bKGD=MagickFalse;
11042 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011043 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011044 mng_info->ping_exclude_EXIF=MagickFalse;
11045 mng_info->ping_exclude_gAMA=MagickFalse;
11046 mng_info->ping_exclude_iCCP=MagickFalse;
11047 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11048 mng_info->ping_exclude_oFFs=MagickFalse;
11049 mng_info->ping_exclude_pHYs=MagickFalse;
11050 mng_info->ping_exclude_sRGB=MagickFalse;
11051 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011052 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011053 mng_info->ping_exclude_vpAg=MagickFalse;
11054 mng_info->ping_exclude_zCCP=MagickFalse;
11055 mng_info->ping_exclude_zTXt=MagickFalse;
11056 }
glennrp2cc891a2010-12-24 13:44:32 +000011057
glennrp03812ae2010-12-24 01:31:34 +000011058 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11059 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011060
glennrp03812ae2010-12-24 01:31:34 +000011061 if (LocaleNCompare(value+i,"chrm",4) == 0)
11062 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011063
glennrpa0ed0092011-04-18 16:36:29 +000011064 if (LocaleNCompare(value+i,"date",4) == 0)
11065 mng_info->ping_exclude_date=MagickTrue;
11066
glennrp03812ae2010-12-24 01:31:34 +000011067 if (LocaleNCompare(value+i,"exif",4) == 0)
11068 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011069
glennrp03812ae2010-12-24 01:31:34 +000011070 if (LocaleNCompare(value+i,"gama",4) == 0)
11071 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011072
glennrp03812ae2010-12-24 01:31:34 +000011073 if (LocaleNCompare(value+i,"iccp",4) == 0)
11074 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011075
glennrp03812ae2010-12-24 01:31:34 +000011076 /*
11077 if (LocaleNCompare(value+i,"itxt",4) == 0)
11078 mng_info->ping_exclude_iTXt=MagickTrue;
11079 */
glennrp2cc891a2010-12-24 13:44:32 +000011080
glennrp03812ae2010-12-24 01:31:34 +000011081 if (LocaleNCompare(value+i,"gama",4) == 0)
11082 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011083
glennrp03812ae2010-12-24 01:31:34 +000011084 if (LocaleNCompare(value+i,"offs",4) == 0)
11085 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011086
glennrp03812ae2010-12-24 01:31:34 +000011087 if (LocaleNCompare(value+i,"phys",4) == 0)
11088 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011089
glennrpa1e3b7b2010-12-24 16:37:33 +000011090 if (LocaleNCompare(value+i,"srgb",4) == 0)
11091 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011092
glennrp03812ae2010-12-24 01:31:34 +000011093 if (LocaleNCompare(value+i,"text",4) == 0)
11094 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011095
glennrpa1e3b7b2010-12-24 16:37:33 +000011096 if (LocaleNCompare(value+i,"trns",4) == 0)
11097 mng_info->ping_exclude_tRNS=MagickTrue;
11098
glennrp03812ae2010-12-24 01:31:34 +000011099 if (LocaleNCompare(value+i,"vpag",4) == 0)
11100 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011101
glennrp03812ae2010-12-24 01:31:34 +000011102 if (LocaleNCompare(value+i,"zccp",4) == 0)
11103 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011104
glennrp03812ae2010-12-24 01:31:34 +000011105 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11106 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011107
glennrp03812ae2010-12-24 01:31:34 +000011108 }
glennrpce91ed52010-12-23 22:37:49 +000011109 }
glennrp26f37912010-12-23 16:22:42 +000011110 }
11111
glennrp5c7cf4e2010-12-24 00:30:00 +000011112 for (source=0; source<1; source++)
11113 {
11114 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011115 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011116 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011117
11118 if (value == NULL)
11119 value=GetImageArtifact(image,"png:include-chunks");
11120 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011121 else
glennrpacba0042010-12-24 14:27:26 +000011122 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011123 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011124
glennrpacba0042010-12-24 14:27:26 +000011125 if (value == NULL)
11126 value=GetImageOption(image_info,"png:include-chunks");
11127 }
11128
glennrp03812ae2010-12-24 01:31:34 +000011129 if (value != NULL)
11130 {
11131 size_t
11132 last;
glennrp26f37912010-12-23 16:22:42 +000011133
glennrp03812ae2010-12-24 01:31:34 +000011134 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011135
glennrp03812ae2010-12-24 01:31:34 +000011136 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011137 {
11138 if (source == 0)
11139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11140 " png:include-chunk=%s found in image artifacts.\n", value);
11141 else
11142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11143 " png:include-chunk=%s found in image properties.\n", value);
11144 }
glennrp03812ae2010-12-24 01:31:34 +000011145
11146 last=strlen(value);
11147
11148 for (i=0; i<(int) last; i+=5)
11149 {
11150 if (LocaleNCompare(value+i,"all",3) == 0)
11151 {
11152 mng_info->ping_exclude_bKGD=MagickFalse;
11153 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011154 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011155 mng_info->ping_exclude_EXIF=MagickFalse;
11156 mng_info->ping_exclude_gAMA=MagickFalse;
11157 mng_info->ping_exclude_iCCP=MagickFalse;
11158 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11159 mng_info->ping_exclude_oFFs=MagickFalse;
11160 mng_info->ping_exclude_pHYs=MagickFalse;
11161 mng_info->ping_exclude_sRGB=MagickFalse;
11162 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011163 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011164 mng_info->ping_exclude_vpAg=MagickFalse;
11165 mng_info->ping_exclude_zCCP=MagickFalse;
11166 mng_info->ping_exclude_zTXt=MagickFalse;
11167 i--;
11168 }
glennrp2cc891a2010-12-24 13:44:32 +000011169
glennrp03812ae2010-12-24 01:31:34 +000011170 if (LocaleNCompare(value+i,"none",4) == 0)
11171 {
11172 mng_info->ping_exclude_bKGD=MagickTrue;
11173 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011174 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011175 mng_info->ping_exclude_EXIF=MagickTrue;
11176 mng_info->ping_exclude_gAMA=MagickTrue;
11177 mng_info->ping_exclude_iCCP=MagickTrue;
11178 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11179 mng_info->ping_exclude_oFFs=MagickTrue;
11180 mng_info->ping_exclude_pHYs=MagickTrue;
11181 mng_info->ping_exclude_sRGB=MagickTrue;
11182 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011183 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011184 mng_info->ping_exclude_vpAg=MagickTrue;
11185 mng_info->ping_exclude_zCCP=MagickTrue;
11186 mng_info->ping_exclude_zTXt=MagickTrue;
11187 }
glennrp2cc891a2010-12-24 13:44:32 +000011188
glennrp03812ae2010-12-24 01:31:34 +000011189 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11190 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011191
glennrp03812ae2010-12-24 01:31:34 +000011192 if (LocaleNCompare(value+i,"chrm",4) == 0)
11193 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011194
glennrpa0ed0092011-04-18 16:36:29 +000011195 if (LocaleNCompare(value+i,"date",4) == 0)
11196 mng_info->ping_exclude_date=MagickFalse;
11197
glennrp03812ae2010-12-24 01:31:34 +000011198 if (LocaleNCompare(value+i,"exif",4) == 0)
11199 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011200
glennrp03812ae2010-12-24 01:31:34 +000011201 if (LocaleNCompare(value+i,"gama",4) == 0)
11202 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011203
glennrp03812ae2010-12-24 01:31:34 +000011204 if (LocaleNCompare(value+i,"iccp",4) == 0)
11205 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011206
glennrp03812ae2010-12-24 01:31:34 +000011207 /*
11208 if (LocaleNCompare(value+i,"itxt",4) == 0)
11209 mng_info->ping_exclude_iTXt=MagickFalse;
11210 */
glennrp2cc891a2010-12-24 13:44:32 +000011211
glennrp03812ae2010-12-24 01:31:34 +000011212 if (LocaleNCompare(value+i,"gama",4) == 0)
11213 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011214
glennrp03812ae2010-12-24 01:31:34 +000011215 if (LocaleNCompare(value+i,"offs",4) == 0)
11216 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011217
glennrp03812ae2010-12-24 01:31:34 +000011218 if (LocaleNCompare(value+i,"phys",4) == 0)
11219 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011220
glennrpa1e3b7b2010-12-24 16:37:33 +000011221 if (LocaleNCompare(value+i,"srgb",4) == 0)
11222 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011223
glennrp03812ae2010-12-24 01:31:34 +000011224 if (LocaleNCompare(value+i,"text",4) == 0)
11225 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011226
glennrpa1e3b7b2010-12-24 16:37:33 +000011227 if (LocaleNCompare(value+i,"trns",4) == 0)
11228 mng_info->ping_exclude_tRNS=MagickFalse;
11229
glennrp03812ae2010-12-24 01:31:34 +000011230 if (LocaleNCompare(value+i,"vpag",4) == 0)
11231 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011232
glennrp03812ae2010-12-24 01:31:34 +000011233 if (LocaleNCompare(value+i,"zccp",4) == 0)
11234 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011235
glennrp03812ae2010-12-24 01:31:34 +000011236 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11237 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011238
glennrp03812ae2010-12-24 01:31:34 +000011239 }
glennrpce91ed52010-12-23 22:37:49 +000011240 }
glennrp26f37912010-12-23 16:22:42 +000011241 }
11242
glennrp03812ae2010-12-24 01:31:34 +000011243 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011244 {
11245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11246 " Chunks to be excluded from the output PNG:");
11247 if (mng_info->ping_exclude_bKGD != MagickFalse)
11248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11249 " bKGD");
11250 if (mng_info->ping_exclude_cHRM != MagickFalse)
11251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11252 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011253 if (mng_info->ping_exclude_date != MagickFalse)
11254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11255 " date");
glennrp26f37912010-12-23 16:22:42 +000011256 if (mng_info->ping_exclude_EXIF != MagickFalse)
11257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11258 " EXIF");
11259 if (mng_info->ping_exclude_gAMA != MagickFalse)
11260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11261 " gAMA");
11262 if (mng_info->ping_exclude_iCCP != MagickFalse)
11263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11264 " iCCP");
11265/*
11266 if (mng_info->ping_exclude_iTXt != MagickFalse)
11267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11268 " iTXt");
11269*/
11270 if (mng_info->ping_exclude_oFFs != MagickFalse)
11271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11272 " oFFs");
11273 if (mng_info->ping_exclude_pHYs != MagickFalse)
11274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11275 " pHYs");
11276 if (mng_info->ping_exclude_sRGB != MagickFalse)
11277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11278 " sRGB");
11279 if (mng_info->ping_exclude_tEXt != MagickFalse)
11280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11281 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011282 if (mng_info->ping_exclude_tRNS != MagickFalse)
11283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11284 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011285 if (mng_info->ping_exclude_vpAg != MagickFalse)
11286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11287 " vpAg");
11288 if (mng_info->ping_exclude_zCCP != MagickFalse)
11289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11290 " zCCP");
11291 if (mng_info->ping_exclude_zTXt != MagickFalse)
11292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11293 " zTXt");
11294 }
11295
glennrpb9cfe272010-12-21 15:08:06 +000011296 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011297
glennrpb9cfe272010-12-21 15:08:06 +000011298 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000011299
11300 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011301
cristy3ed852e2009-09-05 21:47:34 +000011302 if (logging != MagickFalse)
11303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011304
cristy3ed852e2009-09-05 21:47:34 +000011305 return(status);
11306}
11307
11308#if defined(JNG_SUPPORTED)
11309
11310/* Write one JNG image */
11311static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11312 const ImageInfo *image_info,Image *image)
11313{
11314 Image
11315 *jpeg_image;
11316
11317 ImageInfo
11318 *jpeg_image_info;
11319
11320 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011321 logging,
cristy3ed852e2009-09-05 21:47:34 +000011322 status;
11323
11324 size_t
11325 length;
11326
11327 unsigned char
11328 *blob,
11329 chunk[80],
11330 *p;
11331
11332 unsigned int
11333 jng_alpha_compression_method,
11334 jng_alpha_sample_depth,
11335 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011336 transparent;
11337
cristybb503372010-05-27 20:51:26 +000011338 size_t
cristy3ed852e2009-09-05 21:47:34 +000011339 jng_quality;
11340
11341 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011342 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011343
11344 blob=(unsigned char *) NULL;
11345 jpeg_image=(Image *) NULL;
11346 jpeg_image_info=(ImageInfo *) NULL;
11347
11348 status=MagickTrue;
11349 transparent=image_info->type==GrayscaleMatteType ||
11350 image_info->type==TrueColorMatteType;
11351 jng_color_type=10;
11352 jng_alpha_sample_depth=0;
11353 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11354 jng_alpha_compression_method=0;
11355
11356 if (image->matte != MagickFalse)
11357 {
11358 /* if any pixels are transparent */
11359 transparent=MagickTrue;
11360 if (image_info->compression==JPEGCompression)
11361 jng_alpha_compression_method=8;
11362 }
11363
11364 if (transparent)
11365 {
11366 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011367
cristy3ed852e2009-09-05 21:47:34 +000011368 /* Create JPEG blob, image, and image_info */
11369 if (logging != MagickFalse)
11370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11371 " Creating jpeg_image_info for opacity.");
glennrp0fe50b42010-11-16 03:52:51 +000011372
cristy3ed852e2009-09-05 21:47:34 +000011373 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011374
cristy3ed852e2009-09-05 21:47:34 +000011375 if (jpeg_image_info == (ImageInfo *) NULL)
11376 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011377
cristy3ed852e2009-09-05 21:47:34 +000011378 if (logging != MagickFalse)
11379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11380 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011381
cristy3ed852e2009-09-05 21:47:34 +000011382 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011383
cristy3ed852e2009-09-05 21:47:34 +000011384 if (jpeg_image == (Image *) NULL)
11385 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011386
cristy3ed852e2009-09-05 21:47:34 +000011387 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11388 status=SeparateImageChannel(jpeg_image,OpacityChannel);
11389 status=NegateImage(jpeg_image,MagickFalse);
11390 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011391
cristy3ed852e2009-09-05 21:47:34 +000011392 if (jng_quality >= 1000)
11393 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011394
cristy3ed852e2009-09-05 21:47:34 +000011395 else
11396 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011397
cristy3ed852e2009-09-05 21:47:34 +000011398 jpeg_image_info->type=GrayscaleType;
11399 (void) SetImageType(jpeg_image,GrayscaleType);
11400 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011401 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011402 "%s",jpeg_image->filename);
11403 }
11404
11405 /* To do: check bit depth of PNG alpha channel */
11406
11407 /* Check if image is grayscale. */
11408 if (image_info->type != TrueColorMatteType && image_info->type !=
11409 TrueColorType && ImageIsGray(image))
11410 jng_color_type-=2;
11411
11412 if (transparent)
11413 {
11414 if (jng_alpha_compression_method==0)
11415 {
11416 const char
11417 *value;
11418
11419 /* Encode opacity as a grayscale PNG blob */
11420 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11421 &image->exception);
11422 if (logging != MagickFalse)
11423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11424 " Creating PNG blob.");
11425 length=0;
11426
11427 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11428 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11429 jpeg_image_info->interlace=NoInterlace;
11430
11431 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11432 &image->exception);
11433
11434 /* Retrieve sample depth used */
11435 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11436 if (value != (char *) NULL)
11437 jng_alpha_sample_depth= (unsigned int) value[0];
11438 }
11439 else
11440 {
11441 /* Encode opacity as a grayscale JPEG blob */
11442
11443 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11444 &image->exception);
11445
11446 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11447 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11448 jpeg_image_info->interlace=NoInterlace;
11449 if (logging != MagickFalse)
11450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11451 " Creating blob.");
11452 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000011453 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000011454 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011455
cristy3ed852e2009-09-05 21:47:34 +000011456 if (logging != MagickFalse)
11457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011458 " Successfully read jpeg_image into a blob, length=%.20g.",
11459 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011460
11461 }
11462 /* Destroy JPEG image and image_info */
11463 jpeg_image=DestroyImage(jpeg_image);
11464 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11465 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11466 }
11467
11468 /* Write JHDR chunk */
11469 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11470 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011471 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011472 PNGLong(chunk+4,(png_uint_32) image->columns);
11473 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011474 chunk[12]=jng_color_type;
11475 chunk[13]=8; /* sample depth */
11476 chunk[14]=8; /*jng_image_compression_method */
11477 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11478 chunk[16]=jng_alpha_sample_depth;
11479 chunk[17]=jng_alpha_compression_method;
11480 chunk[18]=0; /*jng_alpha_filter_method */
11481 chunk[19]=0; /*jng_alpha_interlace_method */
11482 (void) WriteBlob(image,20,chunk);
11483 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11484 if (logging != MagickFalse)
11485 {
11486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011487 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011488
cristy3ed852e2009-09-05 21:47:34 +000011489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011490 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011491
cristy3ed852e2009-09-05 21:47:34 +000011492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11493 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011494
cristy3ed852e2009-09-05 21:47:34 +000011495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11496 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011497
cristy3ed852e2009-09-05 21:47:34 +000011498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11499 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011500
cristy3ed852e2009-09-05 21:47:34 +000011501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11502 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011503
cristy3ed852e2009-09-05 21:47:34 +000011504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11505 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011506
cristy3ed852e2009-09-05 21:47:34 +000011507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11508 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011509
cristy3ed852e2009-09-05 21:47:34 +000011510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11511 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011512
cristy3ed852e2009-09-05 21:47:34 +000011513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11514 " JNG alpha interlace:%5d",0);
11515 }
11516
glennrp0fe50b42010-11-16 03:52:51 +000011517 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011518 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011519
11520 /*
11521 Write leading ancillary chunks
11522 */
11523
11524 if (transparent)
11525 {
11526 /*
11527 Write JNG bKGD chunk
11528 */
11529
11530 unsigned char
11531 blue,
11532 green,
11533 red;
11534
cristybb503372010-05-27 20:51:26 +000011535 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011536 num_bytes;
11537
11538 if (jng_color_type == 8 || jng_color_type == 12)
11539 num_bytes=6L;
11540 else
11541 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011542 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011543 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011544 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011545 red=ScaleQuantumToChar(image->background_color.red);
11546 green=ScaleQuantumToChar(image->background_color.green);
11547 blue=ScaleQuantumToChar(image->background_color.blue);
11548 *(chunk+4)=0;
11549 *(chunk+5)=red;
11550 *(chunk+6)=0;
11551 *(chunk+7)=green;
11552 *(chunk+8)=0;
11553 *(chunk+9)=blue;
11554 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11555 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11556 }
11557
11558 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11559 {
11560 /*
11561 Write JNG sRGB chunk
11562 */
11563 (void) WriteBlobMSBULong(image,1L);
11564 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011565 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011566
cristy3ed852e2009-09-05 21:47:34 +000011567 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011568 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011569 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011570 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011571
cristy3ed852e2009-09-05 21:47:34 +000011572 else
glennrpe610a072010-08-05 17:08:46 +000011573 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011574 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011575 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011576
cristy3ed852e2009-09-05 21:47:34 +000011577 (void) WriteBlob(image,5,chunk);
11578 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11579 }
11580 else
11581 {
11582 if (image->gamma != 0.0)
11583 {
11584 /*
11585 Write JNG gAMA chunk
11586 */
11587 (void) WriteBlobMSBULong(image,4L);
11588 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011589 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011590 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011591 (void) WriteBlob(image,8,chunk);
11592 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11593 }
glennrp0fe50b42010-11-16 03:52:51 +000011594
cristy3ed852e2009-09-05 21:47:34 +000011595 if ((mng_info->equal_chrms == MagickFalse) &&
11596 (image->chromaticity.red_primary.x != 0.0))
11597 {
11598 PrimaryInfo
11599 primary;
11600
11601 /*
11602 Write JNG cHRM chunk
11603 */
11604 (void) WriteBlobMSBULong(image,32L);
11605 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011606 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011607 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011608 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11609 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011610 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011611 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11612 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011613 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011614 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11615 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011616 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011617 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11618 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011619 (void) WriteBlob(image,36,chunk);
11620 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11621 }
11622 }
glennrp0fe50b42010-11-16 03:52:51 +000011623
cristy3ed852e2009-09-05 21:47:34 +000011624 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11625 {
11626 /*
11627 Write JNG pHYs chunk
11628 */
11629 (void) WriteBlobMSBULong(image,9L);
11630 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011631 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011632 if (image->units == PixelsPerInchResolution)
11633 {
cristy35ef8242010-06-03 16:24:13 +000011634 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011635 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011636
cristy35ef8242010-06-03 16:24:13 +000011637 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011638 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011639
cristy3ed852e2009-09-05 21:47:34 +000011640 chunk[12]=1;
11641 }
glennrp0fe50b42010-11-16 03:52:51 +000011642
cristy3ed852e2009-09-05 21:47:34 +000011643 else
11644 {
11645 if (image->units == PixelsPerCentimeterResolution)
11646 {
cristy35ef8242010-06-03 16:24:13 +000011647 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011648 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011649
cristy35ef8242010-06-03 16:24:13 +000011650 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011651 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011652
cristy3ed852e2009-09-05 21:47:34 +000011653 chunk[12]=1;
11654 }
glennrp0fe50b42010-11-16 03:52:51 +000011655
cristy3ed852e2009-09-05 21:47:34 +000011656 else
11657 {
cristy35ef8242010-06-03 16:24:13 +000011658 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11659 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011660 chunk[12]=0;
11661 }
11662 }
11663 (void) WriteBlob(image,13,chunk);
11664 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11665 }
11666
11667 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11668 {
11669 /*
11670 Write JNG oFFs chunk
11671 */
11672 (void) WriteBlobMSBULong(image,9L);
11673 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011674 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011675 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11676 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011677 chunk[12]=0;
11678 (void) WriteBlob(image,13,chunk);
11679 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11680 }
11681 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11682 {
11683 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11684 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011685 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011686 PNGLong(chunk+4,(png_uint_32) image->page.width);
11687 PNGLong(chunk+8,(png_uint_32) image->page.height);
11688 chunk[12]=0; /* unit = pixels */
11689 (void) WriteBlob(image,13,chunk);
11690 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11691 }
11692
11693
11694 if (transparent)
11695 {
11696 if (jng_alpha_compression_method==0)
11697 {
cristybb503372010-05-27 20:51:26 +000011698 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011699 i;
11700
cristybb503372010-05-27 20:51:26 +000011701 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011702 len;
11703
11704 /* Write IDAT chunk header */
11705 if (logging != MagickFalse)
11706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011707 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011708 length);
cristy3ed852e2009-09-05 21:47:34 +000011709
11710 /* Copy IDAT chunks */
11711 len=0;
11712 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011713 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011714 {
11715 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11716 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011717
cristy3ed852e2009-09-05 21:47:34 +000011718 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11719 {
11720 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011721 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011722 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011723 (void) WriteBlob(image,(size_t) len+4,p);
11724 (void) WriteBlobMSBULong(image,
11725 crc32(0,p,(uInt) len+4));
11726 }
glennrp0fe50b42010-11-16 03:52:51 +000011727
cristy3ed852e2009-09-05 21:47:34 +000011728 else
11729 {
11730 if (logging != MagickFalse)
11731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011732 " Skipping %c%c%c%c chunk, length=%.20g.",
11733 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011734 }
11735 p+=(8+len);
11736 }
11737 }
11738 else
11739 {
11740 /* Write JDAA chunk header */
11741 if (logging != MagickFalse)
11742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011743 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011744 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011745 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011746 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011747 /* Write JDAT chunk(s) data */
11748 (void) WriteBlob(image,4,chunk);
11749 (void) WriteBlob(image,length,blob);
11750 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11751 (uInt) length));
11752 }
11753 blob=(unsigned char *) RelinquishMagickMemory(blob);
11754 }
11755
11756 /* Encode image as a JPEG blob */
11757 if (logging != MagickFalse)
11758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11759 " Creating jpeg_image_info.");
11760 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11761 if (jpeg_image_info == (ImageInfo *) NULL)
11762 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11763
11764 if (logging != MagickFalse)
11765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11766 " Creating jpeg_image.");
11767
11768 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11769 if (jpeg_image == (Image *) NULL)
11770 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11771 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11772
11773 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011774 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000011775 jpeg_image->filename);
11776
11777 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11778 &image->exception);
11779
11780 if (logging != MagickFalse)
11781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011782 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11783 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011784
11785 if (jng_color_type == 8 || jng_color_type == 12)
11786 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011787
cristy3ed852e2009-09-05 21:47:34 +000011788 jpeg_image_info->quality=jng_quality % 1000;
11789 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11790 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011791
cristy3ed852e2009-09-05 21:47:34 +000011792 if (logging != MagickFalse)
11793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11794 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011795
cristy3ed852e2009-09-05 21:47:34 +000011796 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011797
cristy3ed852e2009-09-05 21:47:34 +000011798 if (logging != MagickFalse)
11799 {
11800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011801 " Successfully read jpeg_image into a blob, length=%.20g.",
11802 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011803
11804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011805 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011806 }
glennrp0fe50b42010-11-16 03:52:51 +000011807
cristy3ed852e2009-09-05 21:47:34 +000011808 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011809 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011810 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011811 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011812 (void) WriteBlob(image,4,chunk);
11813 (void) WriteBlob(image,length,blob);
11814 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11815
11816 jpeg_image=DestroyImage(jpeg_image);
11817 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11818 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11819 blob=(unsigned char *) RelinquishMagickMemory(blob);
11820
11821 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011822 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011823
11824 /* Write IEND chunk */
11825 (void) WriteBlobMSBULong(image,0L);
11826 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011827 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011828 (void) WriteBlob(image,4,chunk);
11829 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11830
11831 if (logging != MagickFalse)
11832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11833 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011834
cristy3ed852e2009-09-05 21:47:34 +000011835 return(status);
11836}
11837
11838
11839/*
11840%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11841% %
11842% %
11843% %
11844% W r i t e J N G I m a g e %
11845% %
11846% %
11847% %
11848%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11849%
11850% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11851%
11852% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11853%
11854% The format of the WriteJNGImage method is:
11855%
11856% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11857%
11858% A description of each parameter follows:
11859%
11860% o image_info: the image info.
11861%
11862% o image: The image.
11863%
11864%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11865*/
11866static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
11867{
11868 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011869 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000011870 logging,
cristy3ed852e2009-09-05 21:47:34 +000011871 status;
11872
11873 MngInfo
11874 *mng_info;
11875
cristy3ed852e2009-09-05 21:47:34 +000011876 /*
11877 Open image file.
11878 */
11879 assert(image_info != (const ImageInfo *) NULL);
11880 assert(image_info->signature == MagickSignature);
11881 assert(image != (Image *) NULL);
11882 assert(image->signature == MagickSignature);
11883 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011884 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011885 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11886 if (status == MagickFalse)
11887 return(status);
11888
11889 /*
11890 Allocate a MngInfo structure.
11891 */
11892 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011893 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011894 if (mng_info == (MngInfo *) NULL)
11895 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11896 /*
11897 Initialize members of the MngInfo structure.
11898 */
11899 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11900 mng_info->image=image;
11901 have_mng_structure=MagickTrue;
11902
11903 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
11904
11905 status=WriteOneJNGImage(mng_info,image_info,image);
11906 (void) CloseBlob(image);
11907
11908 (void) CatchImageException(image);
11909 MngInfoFreeStruct(mng_info,&have_mng_structure);
11910 if (logging != MagickFalse)
11911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
11912 return(status);
11913}
11914#endif
11915
11916
11917
11918static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
11919{
11920 const char
11921 *option;
11922
11923 Image
11924 *next_image;
11925
11926 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011927 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011928 status;
11929
glennrp03812ae2010-12-24 01:31:34 +000011930 volatile MagickBooleanType
11931 logging;
11932
cristy3ed852e2009-09-05 21:47:34 +000011933 MngInfo
11934 *mng_info;
11935
11936 int
cristy3ed852e2009-09-05 21:47:34 +000011937 image_count,
11938 need_iterations,
11939 need_matte;
11940
11941 volatile int
11942#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
11943 defined(PNG_MNG_FEATURES_SUPPORTED)
11944 need_local_plte,
11945#endif
11946 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000011947 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000011948 use_global_plte;
11949
cristybb503372010-05-27 20:51:26 +000011950 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011951 i;
11952
11953 unsigned char
11954 chunk[800];
11955
11956 volatile unsigned int
11957 write_jng,
11958 write_mng;
11959
cristybb503372010-05-27 20:51:26 +000011960 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000011961 scene;
11962
cristybb503372010-05-27 20:51:26 +000011963 size_t
cristy3ed852e2009-09-05 21:47:34 +000011964 final_delay=0,
11965 initial_delay;
11966
glennrpd5045b42010-03-24 12:40:35 +000011967#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000011968 if (image_info->verbose)
11969 printf("Your PNG library (libpng-%s) is rather old.\n",
11970 PNG_LIBPNG_VER_STRING);
11971#endif
11972
11973 /*
11974 Open image file.
11975 */
11976 assert(image_info != (const ImageInfo *) NULL);
11977 assert(image_info->signature == MagickSignature);
11978 assert(image != (Image *) NULL);
11979 assert(image->signature == MagickSignature);
11980 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011981 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011982 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
11983 if (status == MagickFalse)
11984 return(status);
11985
11986 /*
11987 Allocate a MngInfo structure.
11988 */
11989 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011990 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000011991 if (mng_info == (MngInfo *) NULL)
11992 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11993 /*
11994 Initialize members of the MngInfo structure.
11995 */
11996 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11997 mng_info->image=image;
11998 have_mng_structure=MagickTrue;
11999 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12000
12001 /*
12002 * See if user has requested a specific PNG subformat to be used
12003 * for all of the PNGs in the MNG being written, e.g.,
12004 *
12005 * convert *.png png8:animation.mng
12006 *
12007 * To do: check -define png:bit_depth and png:color_type as well,
12008 * or perhaps use mng:bit_depth and mng:color_type instead for
12009 * global settings.
12010 */
12011
12012 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12013 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12014 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12015
12016 write_jng=MagickFalse;
12017 if (image_info->compression == JPEGCompression)
12018 write_jng=MagickTrue;
12019
12020 mng_info->adjoin=image_info->adjoin &&
12021 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12022
cristy3ed852e2009-09-05 21:47:34 +000012023 if (logging != MagickFalse)
12024 {
12025 /* Log some info about the input */
12026 Image
12027 *p;
12028
12029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12030 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012031
cristy3ed852e2009-09-05 21:47:34 +000012032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012033 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012034
cristy3ed852e2009-09-05 21:47:34 +000012035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12036 " Type: %d",image_info->type);
12037
12038 scene=0;
12039 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12040 {
12041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012042 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012043
cristy3ed852e2009-09-05 21:47:34 +000012044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012045 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012046
cristy3ed852e2009-09-05 21:47:34 +000012047 if (p->matte)
12048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12049 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012050
cristy3ed852e2009-09-05 21:47:34 +000012051 else
12052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12053 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012054
cristy3ed852e2009-09-05 21:47:34 +000012055 if (p->storage_class == PseudoClass)
12056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12057 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012058
cristy3ed852e2009-09-05 21:47:34 +000012059 else
12060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12061 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012062
cristy3ed852e2009-09-05 21:47:34 +000012063 if (p->colors)
12064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012065 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012066
cristy3ed852e2009-09-05 21:47:34 +000012067 else
12068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12069 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012070
cristy3ed852e2009-09-05 21:47:34 +000012071 if (mng_info->adjoin == MagickFalse)
12072 break;
12073 }
12074 }
12075
cristy3ed852e2009-09-05 21:47:34 +000012076 use_global_plte=MagickFalse;
12077 all_images_are_gray=MagickFalse;
12078#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12079 need_local_plte=MagickTrue;
12080#endif
12081 need_defi=MagickFalse;
12082 need_matte=MagickFalse;
12083 mng_info->framing_mode=1;
12084 mng_info->old_framing_mode=1;
12085
12086 if (write_mng)
12087 if (image_info->page != (char *) NULL)
12088 {
12089 /*
12090 Determine image bounding box.
12091 */
12092 SetGeometry(image,&mng_info->page);
12093 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12094 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12095 }
12096 if (write_mng)
12097 {
12098 unsigned int
12099 need_geom;
12100
12101 unsigned short
12102 red,
12103 green,
12104 blue;
12105
12106 mng_info->page=image->page;
12107 need_geom=MagickTrue;
12108 if (mng_info->page.width || mng_info->page.height)
12109 need_geom=MagickFalse;
12110 /*
12111 Check all the scenes.
12112 */
12113 initial_delay=image->delay;
12114 need_iterations=MagickFalse;
12115 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12116 mng_info->equal_physs=MagickTrue,
12117 mng_info->equal_gammas=MagickTrue;
12118 mng_info->equal_srgbs=MagickTrue;
12119 mng_info->equal_backgrounds=MagickTrue;
12120 image_count=0;
12121#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12122 defined(PNG_MNG_FEATURES_SUPPORTED)
12123 all_images_are_gray=MagickTrue;
12124 mng_info->equal_palettes=MagickFalse;
12125 need_local_plte=MagickFalse;
12126#endif
12127 for (next_image=image; next_image != (Image *) NULL; )
12128 {
12129 if (need_geom)
12130 {
12131 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12132 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012133
cristy3ed852e2009-09-05 21:47:34 +000012134 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12135 mng_info->page.height=next_image->rows+next_image->page.y;
12136 }
glennrp0fe50b42010-11-16 03:52:51 +000012137
cristy3ed852e2009-09-05 21:47:34 +000012138 if (next_image->page.x || next_image->page.y)
12139 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012140
cristy3ed852e2009-09-05 21:47:34 +000012141 if (next_image->matte)
12142 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012143
cristy3ed852e2009-09-05 21:47:34 +000012144 if ((int) next_image->dispose >= BackgroundDispose)
12145 if (next_image->matte || next_image->page.x || next_image->page.y ||
12146 ((next_image->columns < mng_info->page.width) &&
12147 (next_image->rows < mng_info->page.height)))
12148 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012149
cristy3ed852e2009-09-05 21:47:34 +000012150 if (next_image->iterations)
12151 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012152
cristy3ed852e2009-09-05 21:47:34 +000012153 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012154
cristy3ed852e2009-09-05 21:47:34 +000012155 if (final_delay != initial_delay || final_delay > 1UL*
12156 next_image->ticks_per_second)
12157 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012158
cristy3ed852e2009-09-05 21:47:34 +000012159#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12160 defined(PNG_MNG_FEATURES_SUPPORTED)
12161 /*
12162 check for global palette possibility.
12163 */
12164 if (image->matte != MagickFalse)
12165 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012166
cristy3ed852e2009-09-05 21:47:34 +000012167 if (need_local_plte == 0)
12168 {
12169 if (ImageIsGray(image) == MagickFalse)
12170 all_images_are_gray=MagickFalse;
12171 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12172 if (use_global_plte == 0)
12173 use_global_plte=mng_info->equal_palettes;
12174 need_local_plte=!mng_info->equal_palettes;
12175 }
12176#endif
12177 if (GetNextImageInList(next_image) != (Image *) NULL)
12178 {
12179 if (next_image->background_color.red !=
12180 next_image->next->background_color.red ||
12181 next_image->background_color.green !=
12182 next_image->next->background_color.green ||
12183 next_image->background_color.blue !=
12184 next_image->next->background_color.blue)
12185 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012186
cristy3ed852e2009-09-05 21:47:34 +000012187 if (next_image->gamma != next_image->next->gamma)
12188 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012189
cristy3ed852e2009-09-05 21:47:34 +000012190 if (next_image->rendering_intent !=
12191 next_image->next->rendering_intent)
12192 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012193
cristy3ed852e2009-09-05 21:47:34 +000012194 if ((next_image->units != next_image->next->units) ||
12195 (next_image->x_resolution != next_image->next->x_resolution) ||
12196 (next_image->y_resolution != next_image->next->y_resolution))
12197 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012198
cristy3ed852e2009-09-05 21:47:34 +000012199 if (mng_info->equal_chrms)
12200 {
12201 if (next_image->chromaticity.red_primary.x !=
12202 next_image->next->chromaticity.red_primary.x ||
12203 next_image->chromaticity.red_primary.y !=
12204 next_image->next->chromaticity.red_primary.y ||
12205 next_image->chromaticity.green_primary.x !=
12206 next_image->next->chromaticity.green_primary.x ||
12207 next_image->chromaticity.green_primary.y !=
12208 next_image->next->chromaticity.green_primary.y ||
12209 next_image->chromaticity.blue_primary.x !=
12210 next_image->next->chromaticity.blue_primary.x ||
12211 next_image->chromaticity.blue_primary.y !=
12212 next_image->next->chromaticity.blue_primary.y ||
12213 next_image->chromaticity.white_point.x !=
12214 next_image->next->chromaticity.white_point.x ||
12215 next_image->chromaticity.white_point.y !=
12216 next_image->next->chromaticity.white_point.y)
12217 mng_info->equal_chrms=MagickFalse;
12218 }
12219 }
12220 image_count++;
12221 next_image=GetNextImageInList(next_image);
12222 }
12223 if (image_count < 2)
12224 {
12225 mng_info->equal_backgrounds=MagickFalse;
12226 mng_info->equal_chrms=MagickFalse;
12227 mng_info->equal_gammas=MagickFalse;
12228 mng_info->equal_srgbs=MagickFalse;
12229 mng_info->equal_physs=MagickFalse;
12230 use_global_plte=MagickFalse;
12231#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12232 need_local_plte=MagickTrue;
12233#endif
12234 need_iterations=MagickFalse;
12235 }
glennrp0fe50b42010-11-16 03:52:51 +000012236
cristy3ed852e2009-09-05 21:47:34 +000012237 if (mng_info->need_fram == MagickFalse)
12238 {
12239 /*
12240 Only certain framing rates 100/n are exactly representable without
12241 the FRAM chunk but we'll allow some slop in VLC files
12242 */
12243 if (final_delay == 0)
12244 {
12245 if (need_iterations != MagickFalse)
12246 {
12247 /*
12248 It's probably a GIF with loop; don't run it *too* fast.
12249 */
glennrp02617122010-07-28 13:07:35 +000012250 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012251 {
12252 final_delay=10;
12253 (void) ThrowMagickException(&image->exception,
12254 GetMagickModule(),CoderWarning,
12255 "input has zero delay between all frames; assuming",
12256 " 10 cs `%s'","");
12257 }
cristy3ed852e2009-09-05 21:47:34 +000012258 }
12259 else
12260 mng_info->ticks_per_second=0;
12261 }
12262 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012263 mng_info->ticks_per_second=(png_uint_32)
12264 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012265 if (final_delay > 50)
12266 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012267
cristy3ed852e2009-09-05 21:47:34 +000012268 if (final_delay > 75)
12269 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012270
cristy3ed852e2009-09-05 21:47:34 +000012271 if (final_delay > 125)
12272 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012273
cristy3ed852e2009-09-05 21:47:34 +000012274 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12275 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12276 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12277 1UL*image->ticks_per_second))
12278 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12279 }
glennrp0fe50b42010-11-16 03:52:51 +000012280
cristy3ed852e2009-09-05 21:47:34 +000012281 if (mng_info->need_fram != MagickFalse)
12282 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12283 /*
12284 If pseudocolor, we should also check to see if all the
12285 palettes are identical and write a global PLTE if they are.
12286 ../glennrp Feb 99.
12287 */
12288 /*
12289 Write the MNG version 1.0 signature and MHDR chunk.
12290 */
12291 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12292 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12293 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012294 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012295 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12296 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012297 PNGLong(chunk+12,mng_info->ticks_per_second);
12298 PNGLong(chunk+16,0L); /* layer count=unknown */
12299 PNGLong(chunk+20,0L); /* frame count=unknown */
12300 PNGLong(chunk+24,0L); /* play time=unknown */
12301 if (write_jng)
12302 {
12303 if (need_matte)
12304 {
12305 if (need_defi || mng_info->need_fram || use_global_plte)
12306 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012307
cristy3ed852e2009-09-05 21:47:34 +000012308 else
12309 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12310 }
glennrp0fe50b42010-11-16 03:52:51 +000012311
cristy3ed852e2009-09-05 21:47:34 +000012312 else
12313 {
12314 if (need_defi || mng_info->need_fram || use_global_plte)
12315 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012316
cristy3ed852e2009-09-05 21:47:34 +000012317 else
12318 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12319 }
12320 }
glennrp0fe50b42010-11-16 03:52:51 +000012321
cristy3ed852e2009-09-05 21:47:34 +000012322 else
12323 {
12324 if (need_matte)
12325 {
12326 if (need_defi || mng_info->need_fram || use_global_plte)
12327 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012328
cristy3ed852e2009-09-05 21:47:34 +000012329 else
12330 PNGLong(chunk+28,9L); /* simplicity=VLC */
12331 }
glennrp0fe50b42010-11-16 03:52:51 +000012332
cristy3ed852e2009-09-05 21:47:34 +000012333 else
12334 {
12335 if (need_defi || mng_info->need_fram || use_global_plte)
12336 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012337
cristy3ed852e2009-09-05 21:47:34 +000012338 else
12339 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12340 }
12341 }
12342 (void) WriteBlob(image,32,chunk);
12343 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12344 option=GetImageOption(image_info,"mng:need-cacheoff");
12345 if (option != (const char *) NULL)
12346 {
12347 size_t
12348 length;
12349
12350 /*
12351 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12352 */
12353 PNGType(chunk,mng_nEED);
12354 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012355 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012356 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012357 length+=4;
12358 (void) WriteBlob(image,length,chunk);
12359 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12360 }
12361 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12362 (GetNextImageInList(image) != (Image *) NULL) &&
12363 (image->iterations != 1))
12364 {
12365 /*
12366 Write MNG TERM chunk
12367 */
12368 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12369 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012370 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012371 chunk[4]=3; /* repeat animation */
12372 chunk[5]=0; /* show last frame when done */
12373 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12374 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012375
cristy3ed852e2009-09-05 21:47:34 +000012376 if (image->iterations == 0)
12377 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012378
cristy3ed852e2009-09-05 21:47:34 +000012379 else
12380 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012381
cristy3ed852e2009-09-05 21:47:34 +000012382 if (logging != MagickFalse)
12383 {
12384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012385 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12386 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012387
cristy3ed852e2009-09-05 21:47:34 +000012388 if (image->iterations == 0)
12389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012390 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012391
cristy3ed852e2009-09-05 21:47:34 +000012392 else
12393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012394 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012395 }
12396 (void) WriteBlob(image,14,chunk);
12397 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12398 }
12399 /*
12400 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12401 */
12402 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12403 mng_info->equal_srgbs)
12404 {
12405 /*
12406 Write MNG sRGB chunk
12407 */
12408 (void) WriteBlobMSBULong(image,1L);
12409 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012410 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012411
cristy3ed852e2009-09-05 21:47:34 +000012412 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012413 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012414 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012415 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012416
cristy3ed852e2009-09-05 21:47:34 +000012417 else
glennrpe610a072010-08-05 17:08:46 +000012418 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012419 Magick_RenderingIntent_to_PNG_RenderingIntent(
12420 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012421
cristy3ed852e2009-09-05 21:47:34 +000012422 (void) WriteBlob(image,5,chunk);
12423 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12424 mng_info->have_write_global_srgb=MagickTrue;
12425 }
glennrp0fe50b42010-11-16 03:52:51 +000012426
cristy3ed852e2009-09-05 21:47:34 +000012427 else
12428 {
12429 if (image->gamma && mng_info->equal_gammas)
12430 {
12431 /*
12432 Write MNG gAMA chunk
12433 */
12434 (void) WriteBlobMSBULong(image,4L);
12435 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012436 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012437 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012438 (void) WriteBlob(image,8,chunk);
12439 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12440 mng_info->have_write_global_gama=MagickTrue;
12441 }
12442 if (mng_info->equal_chrms)
12443 {
12444 PrimaryInfo
12445 primary;
12446
12447 /*
12448 Write MNG cHRM chunk
12449 */
12450 (void) WriteBlobMSBULong(image,32L);
12451 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012452 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012453 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012454 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12455 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012456 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012457 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12458 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012459 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012460 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12461 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012462 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012463 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12464 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012465 (void) WriteBlob(image,36,chunk);
12466 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12467 mng_info->have_write_global_chrm=MagickTrue;
12468 }
12469 }
12470 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12471 {
12472 /*
12473 Write MNG pHYs chunk
12474 */
12475 (void) WriteBlobMSBULong(image,9L);
12476 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012477 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012478
cristy3ed852e2009-09-05 21:47:34 +000012479 if (image->units == PixelsPerInchResolution)
12480 {
cristy35ef8242010-06-03 16:24:13 +000012481 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012482 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012483
cristy35ef8242010-06-03 16:24:13 +000012484 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012485 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012486
cristy3ed852e2009-09-05 21:47:34 +000012487 chunk[12]=1;
12488 }
glennrp0fe50b42010-11-16 03:52:51 +000012489
cristy3ed852e2009-09-05 21:47:34 +000012490 else
12491 {
12492 if (image->units == PixelsPerCentimeterResolution)
12493 {
cristy35ef8242010-06-03 16:24:13 +000012494 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012495 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012496
cristy35ef8242010-06-03 16:24:13 +000012497 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012498 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012499
cristy3ed852e2009-09-05 21:47:34 +000012500 chunk[12]=1;
12501 }
glennrp0fe50b42010-11-16 03:52:51 +000012502
cristy3ed852e2009-09-05 21:47:34 +000012503 else
12504 {
cristy35ef8242010-06-03 16:24:13 +000012505 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12506 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012507 chunk[12]=0;
12508 }
12509 }
12510 (void) WriteBlob(image,13,chunk);
12511 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12512 }
12513 /*
12514 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12515 or does not cover the entire frame.
12516 */
12517 if (write_mng && (image->matte || image->page.x > 0 ||
12518 image->page.y > 0 || (image->page.width &&
12519 (image->page.width+image->page.x < mng_info->page.width))
12520 || (image->page.height && (image->page.height+image->page.y
12521 < mng_info->page.height))))
12522 {
12523 (void) WriteBlobMSBULong(image,6L);
12524 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012525 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012526 red=ScaleQuantumToShort(image->background_color.red);
12527 green=ScaleQuantumToShort(image->background_color.green);
12528 blue=ScaleQuantumToShort(image->background_color.blue);
12529 PNGShort(chunk+4,red);
12530 PNGShort(chunk+6,green);
12531 PNGShort(chunk+8,blue);
12532 (void) WriteBlob(image,10,chunk);
12533 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12534 if (mng_info->equal_backgrounds)
12535 {
12536 (void) WriteBlobMSBULong(image,6L);
12537 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012538 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012539 (void) WriteBlob(image,10,chunk);
12540 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12541 }
12542 }
12543
12544#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12545 if ((need_local_plte == MagickFalse) &&
12546 (image->storage_class == PseudoClass) &&
12547 (all_images_are_gray == MagickFalse))
12548 {
cristybb503372010-05-27 20:51:26 +000012549 size_t
cristy3ed852e2009-09-05 21:47:34 +000012550 data_length;
12551
12552 /*
12553 Write MNG PLTE chunk
12554 */
12555 data_length=3*image->colors;
12556 (void) WriteBlobMSBULong(image,data_length);
12557 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012558 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012559
cristybb503372010-05-27 20:51:26 +000012560 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012561 {
12562 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
12563 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
12564 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
12565 }
glennrp0fe50b42010-11-16 03:52:51 +000012566
cristy3ed852e2009-09-05 21:47:34 +000012567 (void) WriteBlob(image,data_length+4,chunk);
12568 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12569 mng_info->have_write_global_plte=MagickTrue;
12570 }
12571#endif
12572 }
12573 scene=0;
12574 mng_info->delay=0;
12575#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12576 defined(PNG_MNG_FEATURES_SUPPORTED)
12577 mng_info->equal_palettes=MagickFalse;
12578#endif
12579 do
12580 {
12581 if (mng_info->adjoin)
12582 {
12583#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12584 defined(PNG_MNG_FEATURES_SUPPORTED)
12585 /*
12586 If we aren't using a global palette for the entire MNG, check to
12587 see if we can use one for two or more consecutive images.
12588 */
12589 if (need_local_plte && use_global_plte && !all_images_are_gray)
12590 {
12591 if (mng_info->IsPalette)
12592 {
12593 /*
12594 When equal_palettes is true, this image has the same palette
12595 as the previous PseudoClass image
12596 */
12597 mng_info->have_write_global_plte=mng_info->equal_palettes;
12598 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12599 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12600 {
12601 /*
12602 Write MNG PLTE chunk
12603 */
cristybb503372010-05-27 20:51:26 +000012604 size_t
cristy3ed852e2009-09-05 21:47:34 +000012605 data_length;
12606
12607 data_length=3*image->colors;
12608 (void) WriteBlobMSBULong(image,data_length);
12609 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012610 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012611
cristybb503372010-05-27 20:51:26 +000012612 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012613 {
12614 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12615 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12616 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12617 }
glennrp0fe50b42010-11-16 03:52:51 +000012618
cristy3ed852e2009-09-05 21:47:34 +000012619 (void) WriteBlob(image,data_length+4,chunk);
12620 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12621 (uInt) (data_length+4)));
12622 mng_info->have_write_global_plte=MagickTrue;
12623 }
12624 }
12625 else
12626 mng_info->have_write_global_plte=MagickFalse;
12627 }
12628#endif
12629 if (need_defi)
12630 {
cristybb503372010-05-27 20:51:26 +000012631 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012632 previous_x,
12633 previous_y;
12634
12635 if (scene)
12636 {
12637 previous_x=mng_info->page.x;
12638 previous_y=mng_info->page.y;
12639 }
12640 else
12641 {
12642 previous_x=0;
12643 previous_y=0;
12644 }
12645 mng_info->page=image->page;
12646 if ((mng_info->page.x != previous_x) ||
12647 (mng_info->page.y != previous_y))
12648 {
12649 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12650 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012651 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012652 chunk[4]=0; /* object 0 MSB */
12653 chunk[5]=0; /* object 0 LSB */
12654 chunk[6]=0; /* visible */
12655 chunk[7]=0; /* abstract */
12656 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12657 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12658 (void) WriteBlob(image,16,chunk);
12659 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12660 }
12661 }
12662 }
12663
12664 mng_info->write_mng=write_mng;
12665
12666 if ((int) image->dispose >= 3)
12667 mng_info->framing_mode=3;
12668
12669 if (mng_info->need_fram && mng_info->adjoin &&
12670 ((image->delay != mng_info->delay) ||
12671 (mng_info->framing_mode != mng_info->old_framing_mode)))
12672 {
12673 if (image->delay == mng_info->delay)
12674 {
12675 /*
12676 Write a MNG FRAM chunk with the new framing mode.
12677 */
12678 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12679 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012680 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012681 chunk[4]=(unsigned char) mng_info->framing_mode;
12682 (void) WriteBlob(image,5,chunk);
12683 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12684 }
12685 else
12686 {
12687 /*
12688 Write a MNG FRAM chunk with the delay.
12689 */
12690 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12691 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012692 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012693 chunk[4]=(unsigned char) mng_info->framing_mode;
12694 chunk[5]=0; /* frame name separator (no name) */
12695 chunk[6]=2; /* flag for changing default delay */
12696 chunk[7]=0; /* flag for changing frame timeout */
12697 chunk[8]=0; /* flag for changing frame clipping */
12698 chunk[9]=0; /* flag for changing frame sync_id */
12699 PNGLong(chunk+10,(png_uint_32)
12700 ((mng_info->ticks_per_second*
12701 image->delay)/MagickMax(image->ticks_per_second,1)));
12702 (void) WriteBlob(image,14,chunk);
12703 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012704 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012705 }
12706 mng_info->old_framing_mode=mng_info->framing_mode;
12707 }
12708
12709#if defined(JNG_SUPPORTED)
12710 if (image_info->compression == JPEGCompression)
12711 {
12712 ImageInfo
12713 *write_info;
12714
12715 if (logging != MagickFalse)
12716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12717 " Writing JNG object.");
12718 /* To do: specify the desired alpha compression method. */
12719 write_info=CloneImageInfo(image_info);
12720 write_info->compression=UndefinedCompression;
12721 status=WriteOneJNGImage(mng_info,write_info,image);
12722 write_info=DestroyImageInfo(write_info);
12723 }
12724 else
12725#endif
12726 {
12727 if (logging != MagickFalse)
12728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12729 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012730
glennrpb9cfe272010-12-21 15:08:06 +000012731 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012732 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012733
12734 /* We don't want any ancillary chunks written */
12735 mng_info->ping_exclude_bKGD=MagickTrue;
12736 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012737 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012738 mng_info->ping_exclude_EXIF=MagickTrue;
12739 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012740 mng_info->ping_exclude_iCCP=MagickTrue;
12741 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12742 mng_info->ping_exclude_oFFs=MagickTrue;
12743 mng_info->ping_exclude_pHYs=MagickTrue;
12744 mng_info->ping_exclude_sRGB=MagickTrue;
12745 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012746 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012747 mng_info->ping_exclude_vpAg=MagickTrue;
12748 mng_info->ping_exclude_zCCP=MagickTrue;
12749 mng_info->ping_exclude_zTXt=MagickTrue;
12750
cristy3ed852e2009-09-05 21:47:34 +000012751 status=WriteOnePNGImage(mng_info,image_info,image);
12752 }
12753
12754 if (status == MagickFalse)
12755 {
12756 MngInfoFreeStruct(mng_info,&have_mng_structure);
12757 (void) CloseBlob(image);
12758 return(MagickFalse);
12759 }
12760 (void) CatchImageException(image);
12761 if (GetNextImageInList(image) == (Image *) NULL)
12762 break;
12763 image=SyncNextImageInList(image);
12764 status=SetImageProgress(image,SaveImagesTag,scene++,
12765 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012766
cristy3ed852e2009-09-05 21:47:34 +000012767 if (status == MagickFalse)
12768 break;
glennrp0fe50b42010-11-16 03:52:51 +000012769
cristy3ed852e2009-09-05 21:47:34 +000012770 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012771
cristy3ed852e2009-09-05 21:47:34 +000012772 if (write_mng)
12773 {
12774 while (GetPreviousImageInList(image) != (Image *) NULL)
12775 image=GetPreviousImageInList(image);
12776 /*
12777 Write the MEND chunk.
12778 */
12779 (void) WriteBlobMSBULong(image,0x00000000L);
12780 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012781 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012782 (void) WriteBlob(image,4,chunk);
12783 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12784 }
12785 /*
12786 Relinquish resources.
12787 */
12788 (void) CloseBlob(image);
12789 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012790
cristy3ed852e2009-09-05 21:47:34 +000012791 if (logging != MagickFalse)
12792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012793
cristy3ed852e2009-09-05 21:47:34 +000012794 return(MagickTrue);
12795}
glennrpd5045b42010-03-24 12:40:35 +000012796#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012797
cristy3ed852e2009-09-05 21:47:34 +000012798static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12799{
12800 image=image;
12801 printf("Your PNG library is too old: You have libpng-%s\n",
12802 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012803
cristy3ed852e2009-09-05 21:47:34 +000012804 ThrowBinaryException(CoderError,"PNG library is too old",
12805 image_info->filename);
12806}
glennrp39992b42010-11-14 00:03:43 +000012807
cristy3ed852e2009-09-05 21:47:34 +000012808static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12809{
12810 return(WritePNGImage(image_info,image));
12811}
glennrpd5045b42010-03-24 12:40:35 +000012812#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012813#endif