blob: 2be6db9a3e7233e13c49cb6628fd98138e1c60e8 [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% %
cristy45ef08f2012-12-07 13:13:34 +000021% Copyright 1999-2013 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*/
cristy16ea1392012-03-21 20:38:41 +000044#include "MagickCore/studio.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
cristyaa2c16c2012-03-25 22:21:35 +000050#include "MagickCore/channel.h"
cristy16ea1392012-03-21 20:38:41 +000051#include "MagickCore/color.h"
52#include "MagickCore/color-private.h"
53#include "MagickCore/colormap.h"
54#include "MagickCore/colorspace.h"
55#include "MagickCore/colorspace-private.h"
56#include "MagickCore/constitute.h"
57#include "MagickCore/enhance.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/geometry.h"
61#include "MagickCore/histogram.h"
62#include "MagickCore/image.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/layer.h"
65#include "MagickCore/list.h"
66#include "MagickCore/log.h"
67#include "MagickCore/MagickCore.h"
68#include "MagickCore/memory_.h"
69#include "MagickCore/module.h"
70#include "MagickCore/monitor.h"
71#include "MagickCore/monitor-private.h"
72#include "MagickCore/option.h"
73#include "MagickCore/pixel.h"
74#include "MagickCore/pixel-accessor.h"
75#include "MagickCore/profile.h"
76#include "MagickCore/property.h"
77#include "MagickCore/quantum-private.h"
78#include "MagickCore/resource_.h"
79#include "MagickCore/semaphore.h"
80#include "MagickCore/quantum-private.h"
81#include "MagickCore/static.h"
82#include "MagickCore/statistic.h"
83#include "MagickCore/string_.h"
84#include "MagickCore/string-private.h"
85#include "MagickCore/transform.h"
86#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000087#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000088
glennrp7ef138c2009-11-10 13:50:20 +000089/* Suppress libpng pedantic warnings that were added in
90 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000091 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000092 * fix any code that generates warnings.
93 */
glennrp991e92a2010-01-28 03:09:00 +000094/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000095/* #define PNG_USE_RESULT The result of this function must be checked */
96/* #define PNG_NORETURN This function does not return */
97/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000098/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000099
100/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000101#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000102
cristy3ed852e2009-09-05 21:47:34 +0000103#include "png.h"
104#include "zlib.h"
105
106/* ImageMagick differences */
107#define first_scene scene
108
glennrpd5045b42010-03-24 12:40:35 +0000109#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000110/*
111 Optional declarations. Define or undefine them as you like.
112*/
113/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
114
115/*
116 Features under construction. Define these to work on them.
117*/
118#undef MNG_OBJECT_BUFFERS
119#undef MNG_BASI_SUPPORTED
120#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000122#if defined(MAGICKCORE_JPEG_DELEGATE)
123# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
124#endif
125#if !defined(RGBColorMatchExact)
126#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000127 (((color).red == (target).red) && \
128 ((color).green == (target).green) && \
129 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000130#endif
131
glennrpecab7d72013-05-14 22:50:32 +0000132/* Table of recognized sRGB ICC profiles */
133struct sRGB_info_struct
134{
135 png_uint_32 len;
136 png_uint_32 crc;
137 png_byte intent;
138};
139
140const struct sRGB_info_struct sRGB_info[] =
141{
glennrpc241d3c2013-05-15 04:24:40 +0000142 /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
glennrpecab7d72013-05-14 22:50:32 +0000143 { 3048, 0x3b8772b9UL, 0},
144
glennrpc241d3c2013-05-15 04:24:40 +0000145 /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
glennrpecab7d72013-05-14 22:50:32 +0000146 { 3052, 0x427ebb21UL, 1},
147
glennrpc241d3c2013-05-15 04:24:40 +0000148 /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
glennrpecab7d72013-05-14 22:50:32 +0000149 {60988, 0x306fd8aeUL, 0},
150
glennrpc241d3c2013-05-15 04:24:40 +0000151 /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
glennrpecab7d72013-05-14 22:50:32 +0000152 {60960, 0xbbef7812UL, 0},
153
glennrpc241d3c2013-05-15 04:24:40 +0000154 /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
glennrpecab7d72013-05-14 22:50:32 +0000155 { 3024, 0x5d5129ceUL, 1},
156
157 /* HP-Microsoft sRGB v2 perceptual */
158 { 3144, 0x182ea552UL, 0},
159
160 /* HP-Microsoft sRGB v2 media-relative */
161 { 3144, 0xf29e526dUL, 1},
162
glennrpc241d3c2013-05-15 04:24:40 +0000163 /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
164 { 524, 0xd4938c39UL, 0},
165
166 /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
167 { 3212, 0x034af5a1UL, 0},
168
glennrpecab7d72013-05-14 22:50:32 +0000169 /* Not recognized */
170 { 0, 0x00000000UL, 0},
171};
172
glennrp8e58efd2011-05-20 12:16:29 +0000173/* Macros for left-bit-replication to ensure that pixels
cristy16ea1392012-03-21 20:38:41 +0000174 * and PixelInfos all have the same image->depth, and for use
glennrp8e58efd2011-05-20 12:16:29 +0000175 * in PNG8 quantization.
176 */
177
glennrp8e58efd2011-05-20 12:16:29 +0000178/* LBR01: Replicate top bit */
179
glennrp05001c32011-08-06 13:04:16 +0000180#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000181 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
182 0 : QuantumRange);
183
glennrp91d99252011-06-25 14:30:13 +0000184#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000185 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
186 0 : QuantumRange);
187
glennrp91d99252011-06-25 14:30:13 +0000188#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000189 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
190 0 : QuantumRange);
191
cristy16ea1392012-03-21 20:38:41 +0000192#define LBR01PacketAlpha(pixelpacket) \
193 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000194 0 : QuantumRange);
195
glennrp91d99252011-06-25 14:30:13 +0000196#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000197 { \
glennrp05001c32011-08-06 13:04:16 +0000198 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000199 LBR01PacketGreen((pixelpacket)); \
200 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000201 }
glennrp8e58efd2011-05-20 12:16:29 +0000202
glennrp91d99252011-06-25 14:30:13 +0000203#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000204 { \
glennrp91d99252011-06-25 14:30:13 +0000205 LBR01PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000206 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000207 }
glennrp8e58efd2011-05-20 12:16:29 +0000208
cristyef618312011-06-25 12:26:44 +0000209#define LBR01PixelRed(pixel) \
glennrp360c1542013-01-06 01:21:46 +0000210 (SetPixelRed(image, \
211 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrpc7375f92013-01-06 01:32:16 +0000212 0 : QuantumRange,(pixel)));
glennrp8e58efd2011-05-20 12:16:29 +0000213
glennrp54cf7972011-08-06 14:28:09 +0000214#define LBR01PixelGreen(pixel) \
glennrp360c1542013-01-06 01:21:46 +0000215 (SetPixelGreen(image, \
216 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrpc7375f92013-01-06 01:32:16 +0000217 0 : QuantumRange,(pixel)));
glennrp8e58efd2011-05-20 12:16:29 +0000218
glennrp54cf7972011-08-06 14:28:09 +0000219#define LBR01PixelBlue(pixel) \
glennrp360c1542013-01-06 01:21:46 +0000220 (SetPixelBlue(image, \
221 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrpc7375f92013-01-06 01:32:16 +0000222 0 : QuantumRange,(pixel)));
glennrp8e58efd2011-05-20 12:16:29 +0000223
cristy16ea1392012-03-21 20:38:41 +0000224#define LBR01PixelAlpha(pixel) \
glennrp360c1542013-01-06 01:21:46 +0000225 (SetPixelAlpha(image, \
226 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrpc7375f92013-01-06 01:32:16 +0000227 0 : QuantumRange,(pixel)));
glennrp8e58efd2011-05-20 12:16:29 +0000228
glennrp54cf7972011-08-06 14:28:09 +0000229#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000230 { \
cristyef618312011-06-25 12:26:44 +0000231 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000232 LBR01PixelGreen((pixel)); \
233 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000234 }
glennrp8e58efd2011-05-20 12:16:29 +0000235
cristy16ea1392012-03-21 20:38:41 +0000236#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000237 { \
glennrp54cf7972011-08-06 14:28:09 +0000238 LBR01PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000239 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000240 }
glennrp8e58efd2011-05-20 12:16:29 +0000241
242/* LBR02: Replicate top 2 bits */
243
glennrp05001c32011-08-06 13:04:16 +0000244#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000245 { \
246 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
247 (pixelpacket).red=ScaleCharToQuantum( \
248 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
249 }
glennrp91d99252011-06-25 14:30:13 +0000250#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000251 { \
252 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
253 (pixelpacket).green=ScaleCharToQuantum( \
254 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
255 }
glennrp91d99252011-06-25 14:30:13 +0000256#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000257 { \
258 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
259 (pixelpacket).blue=ScaleCharToQuantum( \
260 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
261 }
cristy16ea1392012-03-21 20:38:41 +0000262#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000263 { \
cristy16ea1392012-03-21 20:38:41 +0000264 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
265 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
267 }
268
glennrp91d99252011-06-25 14:30:13 +0000269#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000270 { \
glennrp05001c32011-08-06 13:04:16 +0000271 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000272 LBR02PacketGreen((pixelpacket)); \
273 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000274 }
glennrp8e58efd2011-05-20 12:16:29 +0000275
glennrp91d99252011-06-25 14:30:13 +0000276#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000277 { \
glennrp91d99252011-06-25 14:30:13 +0000278 LBR02PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000279 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000280 }
glennrp8e58efd2011-05-20 12:16:29 +0000281
cristyef618312011-06-25 12:26:44 +0000282#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000283 { \
cristy16ea1392012-03-21 20:38:41 +0000284 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000285 & 0xc0; \
cristy16ea1392012-03-21 20:38:41 +0000286 SetPixelRed(image, ScaleCharToQuantum( \
287 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
288 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000289 }
glennrp54cf7972011-08-06 14:28:09 +0000290#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000291 { \
cristy16ea1392012-03-21 20:38:41 +0000292 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000293 & 0xc0; \
cristy16ea1392012-03-21 20:38:41 +0000294 SetPixelGreen(image, ScaleCharToQuantum( \
295 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
296 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000297 }
glennrp54cf7972011-08-06 14:28:09 +0000298#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000299 { \
300 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000301 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
302 SetPixelBlue(image, ScaleCharToQuantum( \
303 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
304 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000305 }
cristy16ea1392012-03-21 20:38:41 +0000306#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000307 { \
308 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000309 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
310 SetPixelAlpha(image, ScaleCharToQuantum( \
311 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
312 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000313 }
314
glennrp54cf7972011-08-06 14:28:09 +0000315#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000316 { \
cristyef618312011-06-25 12:26:44 +0000317 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000318 LBR02PixelGreen((pixel)); \
319 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000320 }
glennrp8e58efd2011-05-20 12:16:29 +0000321
cristy16ea1392012-03-21 20:38:41 +0000322#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000323 { \
glennrp54cf7972011-08-06 14:28:09 +0000324 LBR02PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000325 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000326 }
glennrp8e58efd2011-05-20 12:16:29 +0000327
328/* LBR03: Replicate top 3 bits (only used with opaque pixels during
329 PNG8 quantization) */
330
glennrp05001c32011-08-06 13:04:16 +0000331#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000332 { \
333 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
334 (pixelpacket).red=ScaleCharToQuantum( \
335 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
336 }
glennrp91d99252011-06-25 14:30:13 +0000337#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000338 { \
339 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
340 (pixelpacket).green=ScaleCharToQuantum( \
341 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
342 }
glennrp91d99252011-06-25 14:30:13 +0000343#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
346 (pixelpacket).blue=ScaleCharToQuantum( \
347 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
348 }
349
glennrp91d99252011-06-25 14:30:13 +0000350#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000351 { \
glennrp05001c32011-08-06 13:04:16 +0000352 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000353 LBR03PacketGreen((pixelpacket)); \
354 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000355 }
glennrp8e58efd2011-05-20 12:16:29 +0000356
cristyef618312011-06-25 12:26:44 +0000357#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000358 { \
cristy16ea1392012-03-21 20:38:41 +0000359 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000360 & 0xe0; \
cristy16ea1392012-03-21 20:38:41 +0000361 SetPixelRed(image, ScaleCharToQuantum( \
362 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000363 }
cristy16ea1392012-03-21 20:38:41 +0000364#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000365 { \
cristy16ea1392012-03-21 20:38:41 +0000366 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000367 & 0xe0; \
cristy16ea1392012-03-21 20:38:41 +0000368 SetPixelGreen(image, ScaleCharToQuantum( \
369 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000370 }
cristy16ea1392012-03-21 20:38:41 +0000371#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000372 { \
cristy16ea1392012-03-21 20:38:41 +0000373 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000374 & 0xe0; \
cristy16ea1392012-03-21 20:38:41 +0000375 SetPixelBlue(image, ScaleCharToQuantum( \
376 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000377 }
378
cristy16ea1392012-03-21 20:38:41 +0000379#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000380 { \
cristyef618312011-06-25 12:26:44 +0000381 LBR03PixelRed((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000382 LBR03Green((pixel)); \
383 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000384 }
glennrp8e58efd2011-05-20 12:16:29 +0000385
386/* LBR04: Replicate top 4 bits */
387
glennrp05001c32011-08-06 13:04:16 +0000388#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000389 { \
390 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
391 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
392 }
glennrp91d99252011-06-25 14:30:13 +0000393#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000394 { \
395 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
396 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
397 }
glennrp91d99252011-06-25 14:30:13 +0000398#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000399 { \
400 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
401 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
402 }
cristy16ea1392012-03-21 20:38:41 +0000403#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000404 { \
cristy16ea1392012-03-21 20:38:41 +0000405 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
406 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000407 }
408
glennrp91d99252011-06-25 14:30:13 +0000409#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000410 { \
glennrp05001c32011-08-06 13:04:16 +0000411 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000412 LBR04PacketGreen((pixelpacket)); \
413 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000414 }
glennrp8e58efd2011-05-20 12:16:29 +0000415
glennrp91d99252011-06-25 14:30:13 +0000416#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000417 { \
glennrp91d99252011-06-25 14:30:13 +0000418 LBR04PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000419 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000420 }
glennrp8e58efd2011-05-20 12:16:29 +0000421
cristyef618312011-06-25 12:26:44 +0000422#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
cristy16ea1392012-03-21 20:38:41 +0000424 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000425 & 0xf0; \
cristy16ea1392012-03-21 20:38:41 +0000426 SetPixelRed(image,\
427 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000428 }
glennrp54cf7972011-08-06 14:28:09 +0000429#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000430 { \
cristy16ea1392012-03-21 20:38:41 +0000431 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000432 & 0xf0; \
cristy16ea1392012-03-21 20:38:41 +0000433 SetPixelGreen(image,\
434 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000435 }
glennrp54cf7972011-08-06 14:28:09 +0000436#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000437 { \
438 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000439 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
440 SetPixelBlue(image,\
441 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000442 }
cristy16ea1392012-03-21 20:38:41 +0000443#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000444 { \
445 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000446 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
447 SetPixelAlpha(image,\
448 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000449 }
450
glennrp54cf7972011-08-06 14:28:09 +0000451#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000452 { \
cristyef618312011-06-25 12:26:44 +0000453 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000454 LBR04PixelGreen((pixel)); \
455 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000456 }
glennrp8e58efd2011-05-20 12:16:29 +0000457
cristy16ea1392012-03-21 20:38:41 +0000458#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000459 { \
glennrp54cf7972011-08-06 14:28:09 +0000460 LBR04PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000461 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000462 }
glennrp8e58efd2011-05-20 12:16:29 +0000463
464
glennrp11458992013-01-08 18:12:52 +0000465#if MAGICKCORE_QUANTUM_DEPTH > 8
glennrp8e58efd2011-05-20 12:16:29 +0000466/* LBR08: Replicate top 8 bits */
467
glennrp05001c32011-08-06 13:04:16 +0000468#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000469 { \
470 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
471 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
472 }
glennrp91d99252011-06-25 14:30:13 +0000473#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000474 { \
475 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
476 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
477 }
glennrp91d99252011-06-25 14:30:13 +0000478#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000479 { \
480 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
481 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
482 }
cristy16ea1392012-03-21 20:38:41 +0000483#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000484 { \
cristy16ea1392012-03-21 20:38:41 +0000485 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
486 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000487 }
488
glennrp91d99252011-06-25 14:30:13 +0000489#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000490 { \
glennrp05001c32011-08-06 13:04:16 +0000491 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000492 LBR08PacketGreen((pixelpacket)); \
493 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000494 }
glennrp8e58efd2011-05-20 12:16:29 +0000495
glennrp91d99252011-06-25 14:30:13 +0000496#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000497 { \
glennrp91d99252011-06-25 14:30:13 +0000498 LBR08PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000499 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000500 }
glennrp8e58efd2011-05-20 12:16:29 +0000501
cristyef618312011-06-25 12:26:44 +0000502#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000503 { \
504 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000505 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
506 SetPixelRed(image,\
507 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000508 }
glennrp54cf7972011-08-06 14:28:09 +0000509#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000510 { \
511 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000512 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
513 SetPixelGreen(image,\
514 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000515 }
glennrp54cf7972011-08-06 14:28:09 +0000516#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000517 { \
518 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000519 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
520 SetPixelBlue(image,\
521 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000522 }
cristy16ea1392012-03-21 20:38:41 +0000523#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000524 { \
525 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000526 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
527 SetPixelAlpha(image,\
528 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000529 }
530
glennrp54cf7972011-08-06 14:28:09 +0000531#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000532 { \
cristyef618312011-06-25 12:26:44 +0000533 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000534 LBR08PixelGreen((pixel)); \
535 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000536 }
glennrp8e58efd2011-05-20 12:16:29 +0000537
cristy16ea1392012-03-21 20:38:41 +0000538#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000539 { \
glennrp54cf7972011-08-06 14:28:09 +0000540 LBR08PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000541 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000542 }
glennrp11458992013-01-08 18:12:52 +0000543#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
glennrp8e58efd2011-05-20 12:16:29 +0000544
545
glennrp15f07662013-01-08 18:08:13 +0000546#if MAGICKCORE_QUANTUM_DEPTH > 16
glennrp8e58efd2011-05-20 12:16:29 +0000547/* LBR16: Replicate top 16 bits */
548
glennrp05001c32011-08-06 13:04:16 +0000549#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000550 { \
551 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
552 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
553 }
glennrp91d99252011-06-25 14:30:13 +0000554#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000555 { \
556 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
557 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
558 }
glennrp91d99252011-06-25 14:30:13 +0000559#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000560 { \
561 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
562 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
563 }
cristy16ea1392012-03-21 20:38:41 +0000564#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000565 { \
cristy16ea1392012-03-21 20:38:41 +0000566 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
567 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000568 }
569
glennrp91d99252011-06-25 14:30:13 +0000570#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000571 { \
glennrp05001c32011-08-06 13:04:16 +0000572 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000573 LBR16PacketGreen((pixelpacket)); \
574 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000575 }
glennrp8e58efd2011-05-20 12:16:29 +0000576
glennrp91d99252011-06-25 14:30:13 +0000577#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000578 { \
glennrp91d99252011-06-25 14:30:13 +0000579 LBR16PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000580 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000581 }
glennrp8e58efd2011-05-20 12:16:29 +0000582
cristyef618312011-06-25 12:26:44 +0000583#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000584 { \
585 unsigned short lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000586 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
587 SetPixelRed(image,\
588 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000589 }
glennrp54cf7972011-08-06 14:28:09 +0000590#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000591 { \
592 unsigned short lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000593 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
594 SetPixelGreen(image,\
595 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000596 }
glennrp54cf7972011-08-06 14:28:09 +0000597#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000598 { \
599 unsigned short lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000600 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
601 SetPixelBlue(image,\
602 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000603 }
cristy16ea1392012-03-21 20:38:41 +0000604#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000605 { \
606 unsigned short lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000607 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
608 SetPixelAlpha(image,\
609 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000610 }
611
glennrp54cf7972011-08-06 14:28:09 +0000612#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000613 { \
cristyef618312011-06-25 12:26:44 +0000614 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000615 LBR16PixelGreen((pixel)); \
616 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000617 }
glennrp8e58efd2011-05-20 12:16:29 +0000618
cristy16ea1392012-03-21 20:38:41 +0000619#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000620 { \
glennrp54cf7972011-08-06 14:28:09 +0000621 LBR16PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000622 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000623 }
glennrp15f07662013-01-08 18:08:13 +0000624#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
glennrp8e58efd2011-05-20 12:16:29 +0000625
cristy3ed852e2009-09-05 21:47:34 +0000626/*
627 Establish thread safety.
628 setjmp/longjmp is claimed to be safe on these platforms:
629 setjmp/longjmp is alleged to be unsafe on these platforms:
630*/
631#ifndef SETJMP_IS_THREAD_SAFE
632#define PNG_SETJMP_NOT_THREAD_SAFE
633#endif
634
glennrpedaa0382012-04-12 14:16:21 +0000635#ifdef PNG_SETJMP_NOT_THREAD_SAFE
cristy3ed852e2009-09-05 21:47:34 +0000636static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000637 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000638#endif
639
640/*
641 This temporary until I set up malloc'ed object attributes array.
642 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
643 waste more memory.
644*/
645#define MNG_MAX_OBJECTS 256
646
647/*
648 If this not defined, spec is interpreted strictly. If it is
649 defined, an attempt will be made to recover from some errors,
650 including
651 o global PLTE too short
652*/
653#undef MNG_LOOSE
654
655/*
656 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
657 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
658 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
659 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
660 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
661 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
662 will be enabled by default in libpng-1.2.0.
663*/
cristy3ed852e2009-09-05 21:47:34 +0000664#ifdef PNG_MNG_FEATURES_SUPPORTED
665# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
666# define PNG_READ_EMPTY_PLTE_SUPPORTED
667# endif
668# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
669# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
670# endif
671#endif
672
673/*
cristybb503372010-05-27 20:51:26 +0000674 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000675 This macro is only defined in libpng-1.0.3 and later.
676 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
677*/
678#ifndef PNG_UINT_31_MAX
679#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
680#endif
681
682/*
683 Constant strings for known chunk types. If you need to add a chunk,
684 add a string holding the name here. To make the code more
685 portable, we use ASCII numbers like this, not characters.
686*/
687
glennrp85dcf872011-12-07 02:51:47 +0000688static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
689static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
690static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
691static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
692static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
693static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
694static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
695static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
696static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
697static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
698static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
699static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
700static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
701static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
702static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
703static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
704static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
705static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
706static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
707static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
708static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
709static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
710static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
711static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
712static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
713static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
714static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
715static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
716static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
717static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
718static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
719static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
720static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
721static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000722
723#if defined(JNG_SUPPORTED)
glennrp85dcf872011-12-07 02:51:47 +0000724static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
725static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
726static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
727static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
728static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
729static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000730#endif
731
732/*
733Other known chunks that are not yet supported by ImageMagick:
glennrp85dcf872011-12-07 02:51:47 +0000734static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
735static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
736static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
737static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
738static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
739static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
740static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
741static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000742*/
743
744typedef struct _MngBox
745{
cristy8182b072010-05-30 20:10:53 +0000746 long
cristy3ed852e2009-09-05 21:47:34 +0000747 left,
748 right,
749 top,
750 bottom;
751} MngBox;
752
753typedef struct _MngPair
754{
cristy8182b072010-05-30 20:10:53 +0000755 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000756 a,
757 b;
758} MngPair;
759
760#ifdef MNG_OBJECT_BUFFERS
761typedef struct _MngBuffer
762{
763
cristybb503372010-05-27 20:51:26 +0000764 size_t
cristy3ed852e2009-09-05 21:47:34 +0000765 height,
766 width;
767
768 Image
769 *image;
770
771 png_color
772 plte[256];
773
774 int
775 reference_count;
776
777 unsigned char
778 alpha_sample_depth,
779 compression_method,
780 color_type,
781 concrete,
782 filter_method,
783 frozen,
784 image_type,
785 interlace_method,
786 pixel_sample_depth,
787 plte_length,
788 sample_depth,
789 viewable;
790} MngBuffer;
791#endif
792
793typedef struct _MngInfo
794{
795
796#ifdef MNG_OBJECT_BUFFERS
797 MngBuffer
798 *ob[MNG_MAX_OBJECTS];
799#endif
800
801 Image *
802 image;
803
804 RectangleInfo
805 page;
806
807 int
808 adjoin,
809#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
810 bytes_in_read_buffer,
811 found_empty_plte,
812#endif
813 equal_backgrounds,
814 equal_chrms,
815 equal_gammas,
816#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
817 defined(PNG_MNG_FEATURES_SUPPORTED)
818 equal_palettes,
819#endif
820 equal_physs,
821 equal_srgbs,
822 framing_mode,
823 have_global_bkgd,
824 have_global_chrm,
825 have_global_gama,
826 have_global_phys,
827 have_global_sbit,
828 have_global_srgb,
829 have_saved_bkgd_index,
830 have_write_global_chrm,
831 have_write_global_gama,
832 have_write_global_plte,
833 have_write_global_srgb,
834 need_fram,
835 object_id,
836 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000837 saved_bkgd_index;
838
839 int
840 new_number_colors;
841
cristybb503372010-05-27 20:51:26 +0000842 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000843 image_found,
844 loop_count[256],
845 loop_iteration[256],
846 scenes_found,
847 x_off[MNG_MAX_OBJECTS],
848 y_off[MNG_MAX_OBJECTS];
849
850 MngBox
851 clip,
852 frame,
853 image_box,
854 object_clip[MNG_MAX_OBJECTS];
855
856 unsigned char
857 /* These flags could be combined into one byte */
858 exists[MNG_MAX_OBJECTS],
859 frozen[MNG_MAX_OBJECTS],
860 loop_active[256],
861 invisible[MNG_MAX_OBJECTS],
862 viewable[MNG_MAX_OBJECTS];
863
864 MagickOffsetType
865 loop_jump[256];
866
867 png_colorp
868 global_plte;
869
870 png_color_8
871 global_sbit;
872
873 png_byte
874#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
875 read_buffer[8],
876#endif
877 global_trns[256];
878
879 float
880 global_gamma;
881
882 ChromaticityInfo
883 global_chrm;
884
885 RenderingIntent
886 global_srgb_intent;
887
cristy35ef8242010-06-03 16:24:13 +0000888 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000889 delay,
890 global_plte_length,
891 global_trns_length,
892 global_x_pixels_per_unit,
893 global_y_pixels_per_unit,
894 mng_width,
895 mng_height,
896 ticks_per_second;
897
glennrpb9cfe272010-12-21 15:08:06 +0000898 MagickBooleanType
899 need_blob;
900
cristy3ed852e2009-09-05 21:47:34 +0000901 unsigned int
902 IsPalette,
903 global_phys_unit_type,
904 basi_warning,
905 clon_warning,
906 dhdr_warning,
907 jhdr_warning,
908 magn_warning,
909 past_warning,
910 phyg_warning,
911 phys_warning,
912 sbit_warning,
913 show_warning,
914 mng_type,
915 write_mng,
916 write_png_colortype,
917 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000918 write_png_compression_level,
919 write_png_compression_strategy,
920 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000921 write_png8,
922 write_png24,
glennrpfd164d22013-01-26 21:10:22 +0000923 write_png32,
924 write_png48,
925 write_png64;
cristy3ed852e2009-09-05 21:47:34 +0000926
927#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000928 size_t
cristy3ed852e2009-09-05 21:47:34 +0000929 basi_width,
930 basi_height;
931
932 unsigned int
933 basi_depth,
934 basi_color_type,
935 basi_compression_method,
936 basi_filter_type,
937 basi_interlace_method,
938 basi_red,
939 basi_green,
940 basi_blue,
941 basi_alpha,
942 basi_viewable;
943#endif
944
945 png_uint_16
946 magn_first,
947 magn_last,
948 magn_mb,
949 magn_ml,
950 magn_mr,
951 magn_mt,
952 magn_mx,
953 magn_my,
954 magn_methx,
955 magn_methy;
956
cristy16ea1392012-03-21 20:38:41 +0000957 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000958 mng_global_bkgd;
959
glennrp26f37912010-12-23 16:22:42 +0000960 /* Added at version 6.6.6-7 */
961 MagickBooleanType
962 ping_exclude_bKGD,
963 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000964 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000965 ping_exclude_EXIF,
966 ping_exclude_gAMA,
967 ping_exclude_iCCP,
968 /* ping_exclude_iTXt, */
969 ping_exclude_oFFs,
970 ping_exclude_pHYs,
971 ping_exclude_sRGB,
972 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000973 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000974 ping_exclude_vpAg,
975 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000976 ping_exclude_zTXt,
glennrpecab7d72013-05-14 22:50:32 +0000977 ping_preserve_colormap,
978 /* Added at version 6.8.5-7 */
979 ping_preserve_iCCP;
glennrp26f37912010-12-23 16:22:42 +0000980
cristy3ed852e2009-09-05 21:47:34 +0000981} MngInfo;
982#endif /* VER */
983
984/*
985 Forward declarations.
986*/
987static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000988 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000989
cristy3ed852e2009-09-05 21:47:34 +0000990static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000991 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000992
cristy3ed852e2009-09-05 21:47:34 +0000993#if defined(JNG_SUPPORTED)
994static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000995 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000996#endif
997
glennrp0c3e06b2010-11-19 13:45:02 +0000998#if PNG_LIBPNG_VER > 10011
999
glennrpfd05d622011-02-25 04:10:33 +00001000
glennrp0c3e06b2010-11-19 13:45:02 +00001001#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
1002static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +00001003LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
glennrp0c3e06b2010-11-19 13:45:02 +00001004{
glennrp67b9c1a2011-04-22 18:47:36 +00001005 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
1006 *
1007 * This is true if the high byte and the next highest byte of
1008 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +00001009 * are equal to each other. We check this by seeing if the samples
1010 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +00001011 *
1012 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +00001013 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +00001014 */
1015
glennrp3faa9a32011-04-23 14:00:25 +00001016#define QuantumToCharToQuantumEqQuantum(quantum) \
1017 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
1018
glennrp0c3e06b2010-11-19 13:45:02 +00001019 MagickBooleanType
1020 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001021
glennrp03e11f62011-04-22 13:30:16 +00001022 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +00001023 {
1024
cristy16ea1392012-03-21 20:38:41 +00001025 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +00001026 *p;
1027
1028 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +00001029 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
1030 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
1031 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
1032 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001033
1034 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
1035 {
1036 int indx;
1037
1038 for (indx=0; indx < (ssize_t) image->colors; indx++)
1039 {
glennrp3faa9a32011-04-23 14:00:25 +00001040 ok_to_reduce=(
1041 QuantumToCharToQuantumEqQuantum(
1042 image->colormap[indx].red) &&
1043 QuantumToCharToQuantumEqQuantum(
1044 image->colormap[indx].green) &&
1045 QuantumToCharToQuantumEqQuantum(
1046 image->colormap[indx].blue)) ?
1047 MagickTrue : MagickFalse;
1048
glennrp0c3e06b2010-11-19 13:45:02 +00001049 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00001050 break;
glennrp0c3e06b2010-11-19 13:45:02 +00001051 }
1052 }
1053
1054 if ((ok_to_reduce != MagickFalse) &&
1055 (image->storage_class != PseudoClass))
1056 {
1057 ssize_t
1058 y;
1059
1060 register ssize_t
1061 x;
1062
1063 for (y=0; y < (ssize_t) image->rows; y++)
1064 {
cristy16ea1392012-03-21 20:38:41 +00001065 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0c3e06b2010-11-19 13:45:02 +00001066
cristy16ea1392012-03-21 20:38:41 +00001067 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001068 {
1069 ok_to_reduce = MagickFalse;
1070 break;
1071 }
1072
1073 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1074 {
glennrp3faa9a32011-04-23 14:00:25 +00001075 ok_to_reduce=
cristy16ea1392012-03-21 20:38:41 +00001076 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1077 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1078 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001079 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001080
1081 if (ok_to_reduce == MagickFalse)
1082 break;
1083
cristy16ea1392012-03-21 20:38:41 +00001084 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001085 }
glennrp8640fb52010-11-23 15:48:26 +00001086 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001087 break;
1088 }
1089 }
1090
1091 if (ok_to_reduce != MagickFalse)
1092 {
glennrp0c3e06b2010-11-19 13:45:02 +00001093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001094 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001095 }
glennrpa6a06632011-01-19 15:15:34 +00001096 else
1097 {
1098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001099 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001100 }
glennrp0c3e06b2010-11-19 13:45:02 +00001101 }
1102
1103 return ok_to_reduce;
1104}
1105#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1106
glennrp1a56e9c2012-04-25 17:06:57 +00001107static const char* PngColorTypeToString(const unsigned int color_type)
1108{
1109 const char
1110 *result = "Unknown";
1111
1112 switch (color_type)
1113 {
1114 case PNG_COLOR_TYPE_GRAY:
1115 result = "Gray";
1116 break;
1117 case PNG_COLOR_TYPE_GRAY_ALPHA:
1118 result = "Gray+Alpha";
1119 break;
1120 case PNG_COLOR_TYPE_PALETTE:
1121 result = "Palette";
1122 break;
1123 case PNG_COLOR_TYPE_RGB:
1124 result = "RGB";
1125 break;
1126 case PNG_COLOR_TYPE_RGB_ALPHA:
1127 result = "RGB+Alpha";
1128 break;
1129 }
1130
1131 return result;
1132}
1133
glennrpe610a072010-08-05 17:08:46 +00001134static int
glennrpcf002022011-01-30 02:38:15 +00001135Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001136{
glennrpe610a072010-08-05 17:08:46 +00001137 switch (intent)
1138 {
1139 case PerceptualIntent:
1140 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001141
glennrpe610a072010-08-05 17:08:46 +00001142 case RelativeIntent:
1143 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001144
glennrpe610a072010-08-05 17:08:46 +00001145 case SaturationIntent:
1146 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001147
glennrpe610a072010-08-05 17:08:46 +00001148 case AbsoluteIntent:
1149 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001150
glennrpe610a072010-08-05 17:08:46 +00001151 default:
1152 return -1;
1153 }
1154}
1155
1156static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001157Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001158{
glennrpcf002022011-01-30 02:38:15 +00001159 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001160 {
1161 case 0:
1162 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001163
glennrpe610a072010-08-05 17:08:46 +00001164 case 1:
1165 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001166
glennrpe610a072010-08-05 17:08:46 +00001167 case 2:
1168 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001169
glennrpe610a072010-08-05 17:08:46 +00001170 case 3:
1171 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001172
glennrpe610a072010-08-05 17:08:46 +00001173 default:
1174 return UndefinedIntent;
1175 }
1176}
1177
cristy9d8c1222012-08-10 12:34:19 +00001178static const char *
glennrp98b83d42012-07-23 02:50:31 +00001179Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1180{
1181 switch (ping_intent)
1182 {
1183 case 0:
1184 return "Perceptual Intent";
1185
1186 case 1:
1187 return "Relative Intent";
1188
1189 case 2:
1190 return "Saturation Intent";
1191
1192 case 3:
1193 return "Absolute Intent";
1194
1195 default:
1196 return "Undefined Intent";
1197 }
1198}
1199
cristybb503372010-05-27 20:51:26 +00001200static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001201{
1202 if (x > y)
1203 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001204
cristy3ed852e2009-09-05 21:47:34 +00001205 return(y);
1206}
glennrp0c3e06b2010-11-19 13:45:02 +00001207
cristyd9ecd042012-06-17 18:26:12 +00001208static const char *
glennrp5dff4352012-06-06 22:12:04 +00001209Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1210{
1211 switch (ping_colortype)
1212 {
1213 case 0:
1214 return "Grayscale";
1215
1216 case 2:
1217 return "Truecolor";
1218
1219 case 3:
1220 return "Indexed";
1221
1222 case 4:
1223 return "GrayAlpha";
1224
1225 case 6:
1226 return "RGBA";
1227
1228 default:
1229 return "UndefinedColorType";
1230 }
1231}
1232
1233
cristybb503372010-05-27 20:51:26 +00001234static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001235{
1236 if (x < y)
1237 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001238
cristy3ed852e2009-09-05 21:47:34 +00001239 return(y);
1240}
glennrpd5045b42010-03-24 12:40:35 +00001241#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001242#endif /* MAGICKCORE_PNG_DELEGATE */
1243
1244/*
1245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1246% %
1247% %
1248% %
1249% I s M N G %
1250% %
1251% %
1252% %
1253%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254%
1255% IsMNG() returns MagickTrue if the image format type, identified by the
1256% magick string, is MNG.
1257%
1258% The format of the IsMNG method is:
1259%
1260% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1261%
1262% A description of each parameter follows:
1263%
1264% o magick: compare image format pattern against these bytes.
1265%
1266% o length: Specifies the length of the magick string.
1267%
1268%
1269*/
1270static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1271{
1272 if (length < 8)
1273 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001274
cristy3ed852e2009-09-05 21:47:34 +00001275 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1276 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001277
cristy3ed852e2009-09-05 21:47:34 +00001278 return(MagickFalse);
1279}
1280
1281/*
1282%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1283% %
1284% %
1285% %
1286% I s J N G %
1287% %
1288% %
1289% %
1290%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1291%
1292% IsJNG() returns MagickTrue if the image format type, identified by the
1293% magick string, is JNG.
1294%
1295% The format of the IsJNG method is:
1296%
1297% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1298%
1299% A description of each parameter follows:
1300%
1301% o magick: compare image format pattern against these bytes.
1302%
1303% o length: Specifies the length of the magick string.
1304%
1305%
1306*/
1307static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1308{
1309 if (length < 8)
1310 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001311
cristy3ed852e2009-09-05 21:47:34 +00001312 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1313 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001314
cristy3ed852e2009-09-05 21:47:34 +00001315 return(MagickFalse);
1316}
1317
1318/*
1319%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1320% %
1321% %
1322% %
1323% I s P N G %
1324% %
1325% %
1326% %
1327%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1328%
1329% IsPNG() returns MagickTrue if the image format type, identified by the
1330% magick string, is PNG.
1331%
1332% The format of the IsPNG method is:
1333%
1334% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1335%
1336% A description of each parameter follows:
1337%
1338% o magick: compare image format pattern against these bytes.
1339%
1340% o length: Specifies the length of the magick string.
1341%
1342*/
1343static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1344{
1345 if (length < 8)
1346 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001347
cristy3ed852e2009-09-05 21:47:34 +00001348 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1349 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001350
cristy3ed852e2009-09-05 21:47:34 +00001351 return(MagickFalse);
1352}
1353
1354#if defined(MAGICKCORE_PNG_DELEGATE)
1355#if defined(__cplusplus) || defined(c_plusplus)
1356extern "C" {
1357#endif
1358
glennrpd5045b42010-03-24 12:40:35 +00001359#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001360static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001361{
1362 unsigned char
1363 buffer[4];
1364
1365 assert(image != (Image *) NULL);
1366 assert(image->signature == MagickSignature);
1367 buffer[0]=(unsigned char) (value >> 24);
1368 buffer[1]=(unsigned char) (value >> 16);
1369 buffer[2]=(unsigned char) (value >> 8);
1370 buffer[3]=(unsigned char) value;
1371 return((size_t) WriteBlob(image,4,buffer));
1372}
1373
1374static void PNGLong(png_bytep p,png_uint_32 value)
1375{
1376 *p++=(png_byte) ((value >> 24) & 0xff);
1377 *p++=(png_byte) ((value >> 16) & 0xff);
1378 *p++=(png_byte) ((value >> 8) & 0xff);
1379 *p++=(png_byte) (value & 0xff);
1380}
1381
glennrpa521b2f2010-10-29 04:11:03 +00001382#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001383static void PNGsLong(png_bytep p,png_int_32 value)
1384{
1385 *p++=(png_byte) ((value >> 24) & 0xff);
1386 *p++=(png_byte) ((value >> 16) & 0xff);
1387 *p++=(png_byte) ((value >> 8) & 0xff);
1388 *p++=(png_byte) (value & 0xff);
1389}
glennrpa521b2f2010-10-29 04:11:03 +00001390#endif
cristy3ed852e2009-09-05 21:47:34 +00001391
1392static void PNGShort(png_bytep p,png_uint_16 value)
1393{
1394 *p++=(png_byte) ((value >> 8) & 0xff);
1395 *p++=(png_byte) (value & 0xff);
1396}
1397
1398static void PNGType(png_bytep p,png_bytep type)
1399{
1400 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1401}
1402
glennrp03812ae2010-12-24 01:31:34 +00001403static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1404 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001405{
1406 if (logging != MagickFalse)
1407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001408 " Writing %c%c%c%c chunk, length: %.20g",
1409 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001410}
glennrpd5045b42010-03-24 12:40:35 +00001411#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001412
1413#if defined(__cplusplus) || defined(c_plusplus)
1414}
1415#endif
1416
glennrpd5045b42010-03-24 12:40:35 +00001417#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001418/*
1419%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1420% %
1421% %
1422% %
1423% R e a d P N G I m a g e %
1424% %
1425% %
1426% %
1427%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1428%
1429% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1430% Multiple-image Network Graphics (MNG) image file and returns it. It
1431% allocates the memory necessary for the new Image structure and returns a
1432% pointer to the new image or set of images.
1433%
1434% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1435%
1436% The format of the ReadPNGImage method is:
1437%
1438% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1439%
1440% A description of each parameter follows:
1441%
1442% o image_info: the image info.
1443%
1444% o exception: return any errors or warnings in this structure.
1445%
1446% To do, more or less in chronological order (as of version 5.5.2,
1447% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1448%
1449% Get 16-bit cheap transparency working.
1450%
1451% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1452%
1453% Preserve all unknown and not-yet-handled known chunks found in input
1454% PNG file and copy them into output PNG files according to the PNG
1455% copying rules.
1456%
1457% (At this point, PNG encoding should be in full MNG compliance)
1458%
1459% Provide options for choice of background to use when the MNG BACK
1460% chunk is not present or is not mandatory (i.e., leave transparent,
1461% user specified, MNG BACK, PNG bKGD)
1462%
1463% Implement LOOP/ENDL [done, but could do discretionary loops more
1464% efficiently by linking in the duplicate frames.].
1465%
1466% Decode and act on the MHDR simplicity profile (offer option to reject
1467% files or attempt to process them anyway when the profile isn't LC or VLC).
1468%
1469% Upgrade to full MNG without Delta-PNG.
1470%
1471% o BACK [done a while ago except for background image ID]
1472% o MOVE [done 15 May 1999]
1473% o CLIP [done 15 May 1999]
1474% o DISC [done 19 May 1999]
1475% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1476% o SEEK [partially done 19 May 1999 (discard function only)]
1477% o SHOW
1478% o PAST
1479% o BASI
1480% o MNG-level tEXt/iTXt/zTXt
1481% o pHYg
1482% o pHYs
1483% o sBIT
1484% o bKGD
1485% o iTXt (wait for libpng implementation).
1486%
1487% Use the scene signature to discover when an identical scene is
1488% being reused, and just point to the original image->exception instead
1489% of storing another set of pixels. This not specific to MNG
1490% but could be applied generally.
1491%
1492% Upgrade to full MNG with Delta-PNG.
1493%
1494% JNG tEXt/iTXt/zTXt
1495%
1496% We will not attempt to read files containing the CgBI chunk.
1497% They are really Xcode files meant for display on the iPhone.
1498% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001499% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001500% since irretrievable loss of color data has occurred due to the
1501% use of premultiplied alpha.
1502*/
1503
1504#if defined(__cplusplus) || defined(c_plusplus)
1505extern "C" {
1506#endif
1507
1508/*
1509 This the function that does the actual reading of data. It is
1510 the same as the one supplied in libpng, except that it receives the
1511 datastream from the ReadBlob() function instead of standard input.
1512*/
1513static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1514{
1515 Image
1516 *image;
1517
1518 image=(Image *) png_get_io_ptr(png_ptr);
1519 if (length)
1520 {
1521 png_size_t
1522 check;
1523
1524 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1525 if (check != length)
1526 {
1527 char
1528 msg[MaxTextExtent];
1529
cristy3b6fd2e2011-05-20 12:53:50 +00001530 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001531 "Expected %.20g bytes; found %.20g bytes",(double) length,
1532 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001533 png_warning(png_ptr,msg);
1534 png_error(png_ptr,"Read Exception");
1535 }
1536 }
1537}
1538
1539#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1540 !defined(PNG_MNG_FEATURES_SUPPORTED)
1541/* We use mng_get_data() instead of png_get_data() if we have a libpng
1542 * older than libpng-1.0.3a, which was the first to allow the empty
1543 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1544 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1545 * encountered after an empty PLTE, so we have to look ahead for bKGD
1546 * chunks and remove them from the datastream that is passed to libpng,
1547 * and store their contents for later use.
1548 */
1549static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1550{
1551 MngInfo
1552 *mng_info;
1553
1554 Image
1555 *image;
1556
1557 png_size_t
1558 check;
1559
cristybb503372010-05-27 20:51:26 +00001560 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001561 i;
1562
1563 i=0;
1564 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1565 image=(Image *) mng_info->image;
1566 while (mng_info->bytes_in_read_buffer && length)
1567 {
1568 data[i]=mng_info->read_buffer[i];
1569 mng_info->bytes_in_read_buffer--;
1570 length--;
1571 i++;
1572 }
1573 if (length)
1574 {
1575 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001576
cristy3ed852e2009-09-05 21:47:34 +00001577 if (check != length)
1578 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001579
cristy3ed852e2009-09-05 21:47:34 +00001580 if (length == 4)
1581 {
1582 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1583 (data[3] == 0))
1584 {
1585 check=(png_size_t) ReadBlob(image,(size_t) length,
1586 (char *) mng_info->read_buffer);
1587 mng_info->read_buffer[4]=0;
1588 mng_info->bytes_in_read_buffer=4;
1589 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1590 mng_info->found_empty_plte=MagickTrue;
1591 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1592 {
1593 mng_info->found_empty_plte=MagickFalse;
1594 mng_info->have_saved_bkgd_index=MagickFalse;
1595 }
1596 }
glennrp0fe50b42010-11-16 03:52:51 +00001597
cristy3ed852e2009-09-05 21:47:34 +00001598 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1599 (data[3] == 1))
1600 {
1601 check=(png_size_t) ReadBlob(image,(size_t) length,
1602 (char *) mng_info->read_buffer);
1603 mng_info->read_buffer[4]=0;
1604 mng_info->bytes_in_read_buffer=4;
1605 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1606 if (mng_info->found_empty_plte)
1607 {
1608 /*
1609 Skip the bKGD data byte and CRC.
1610 */
1611 check=(png_size_t)
1612 ReadBlob(image,5,(char *) mng_info->read_buffer);
1613 check=(png_size_t) ReadBlob(image,(size_t) length,
1614 (char *) mng_info->read_buffer);
1615 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1616 mng_info->have_saved_bkgd_index=MagickTrue;
1617 mng_info->bytes_in_read_buffer=0;
1618 }
1619 }
1620 }
1621 }
1622}
1623#endif
1624
1625static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1626{
1627 Image
1628 *image;
1629
1630 image=(Image *) png_get_io_ptr(png_ptr);
1631 if (length)
1632 {
1633 png_size_t
1634 check;
1635
cristybb503372010-05-27 20:51:26 +00001636 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001637
cristy3ed852e2009-09-05 21:47:34 +00001638 if (check != length)
1639 png_error(png_ptr,"WriteBlob Failed");
1640 }
1641}
1642
1643static void png_flush_data(png_structp png_ptr)
1644{
1645 (void) png_ptr;
1646}
1647
1648#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1649static int PalettesAreEqual(Image *a,Image *b)
1650{
cristybb503372010-05-27 20:51:26 +00001651 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001652 i;
1653
1654 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1655 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001656
cristy3ed852e2009-09-05 21:47:34 +00001657 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1658 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001659
cristy3ed852e2009-09-05 21:47:34 +00001660 if (a->colors != b->colors)
1661 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001662
cristybb503372010-05-27 20:51:26 +00001663 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001664 {
1665 if ((a->colormap[i].red != b->colormap[i].red) ||
1666 (a->colormap[i].green != b->colormap[i].green) ||
1667 (a->colormap[i].blue != b->colormap[i].blue))
1668 return((int) MagickFalse);
1669 }
glennrp0fe50b42010-11-16 03:52:51 +00001670
cristy3ed852e2009-09-05 21:47:34 +00001671 return((int) MagickTrue);
1672}
1673#endif
1674
1675static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1676{
1677 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1678 mng_info->exists[i] && !mng_info->frozen[i])
1679 {
1680#ifdef MNG_OBJECT_BUFFERS
1681 if (mng_info->ob[i] != (MngBuffer *) NULL)
1682 {
1683 if (mng_info->ob[i]->reference_count > 0)
1684 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001685
cristy3ed852e2009-09-05 21:47:34 +00001686 if (mng_info->ob[i]->reference_count == 0)
1687 {
1688 if (mng_info->ob[i]->image != (Image *) NULL)
1689 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001690
cristy3ed852e2009-09-05 21:47:34 +00001691 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1692 }
1693 }
1694 mng_info->ob[i]=(MngBuffer *) NULL;
1695#endif
1696 mng_info->exists[i]=MagickFalse;
1697 mng_info->invisible[i]=MagickFalse;
1698 mng_info->viewable[i]=MagickFalse;
1699 mng_info->frozen[i]=MagickFalse;
1700 mng_info->x_off[i]=0;
1701 mng_info->y_off[i]=0;
1702 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001703 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001704 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001705 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001706 }
1707}
1708
glennrp21f0e622011-01-07 16:20:57 +00001709static void MngInfoFreeStruct(MngInfo *mng_info,
1710 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001711{
glennrp21f0e622011-01-07 16:20:57 +00001712 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001713 {
cristybb503372010-05-27 20:51:26 +00001714 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001715 i;
1716
1717 for (i=1; i < MNG_MAX_OBJECTS; i++)
1718 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001719
cristy3ed852e2009-09-05 21:47:34 +00001720 if (mng_info->global_plte != (png_colorp) NULL)
1721 mng_info->global_plte=(png_colorp)
1722 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001723
cristy3ed852e2009-09-05 21:47:34 +00001724 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1725 *have_mng_structure=MagickFalse;
1726 }
1727}
1728
1729static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1730{
1731 MngBox
1732 box;
1733
1734 box=box1;
1735 if (box.left < box2.left)
1736 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001737
cristy3ed852e2009-09-05 21:47:34 +00001738 if (box.top < box2.top)
1739 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001740
cristy3ed852e2009-09-05 21:47:34 +00001741 if (box.right > box2.right)
1742 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001743
cristy3ed852e2009-09-05 21:47:34 +00001744 if (box.bottom > box2.bottom)
1745 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001746
cristy3ed852e2009-09-05 21:47:34 +00001747 return box;
1748}
1749
1750static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1751{
1752 MngBox
1753 box;
1754
1755 /*
1756 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1757 */
cristybb503372010-05-27 20:51:26 +00001758 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1759 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1760 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1761 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001762 if (delta_type != 0)
1763 {
1764 box.left+=previous_box.left;
1765 box.right+=previous_box.right;
1766 box.top+=previous_box.top;
1767 box.bottom+=previous_box.bottom;
1768 }
glennrp0fe50b42010-11-16 03:52:51 +00001769
cristy3ed852e2009-09-05 21:47:34 +00001770 return(box);
1771}
1772
1773static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1774 unsigned char *p)
1775{
1776 MngPair
1777 pair;
1778 /*
cristybb503372010-05-27 20:51:26 +00001779 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001780 */
cristy8182b072010-05-30 20:10:53 +00001781 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1782 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001783
cristy3ed852e2009-09-05 21:47:34 +00001784 if (delta_type != 0)
1785 {
1786 pair.a+=previous_pair.a;
1787 pair.b+=previous_pair.b;
1788 }
glennrp0fe50b42010-11-16 03:52:51 +00001789
cristy3ed852e2009-09-05 21:47:34 +00001790 return(pair);
1791}
1792
cristy8182b072010-05-30 20:10:53 +00001793static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001794{
cristy8182b072010-05-30 20:10:53 +00001795 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001796}
1797
cristy16ea1392012-03-21 20:38:41 +00001798typedef struct _PNGErrorInfo
cristyc82a27b2011-10-21 01:07:16 +00001799{
cristyc82a27b2011-10-21 01:07:16 +00001800 Image
1801 *image;
1802
cristy16ea1392012-03-21 20:38:41 +00001803 ExceptionInfo
1804 *exception;
1805} PNGErrorInfo;
cristyc82a27b2011-10-21 01:07:16 +00001806
cristy16ea1392012-03-21 20:38:41 +00001807static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1808{
1809 ExceptionInfo
1810 *exception;
cristyc82a27b2011-10-21 01:07:16 +00001811
cristy16ea1392012-03-21 20:38:41 +00001812 Image
1813 *image;
1814
1815 PNGErrorInfo
1816 *error_info;
1817
1818 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1819 image=error_info->image;
1820 exception=error_info->exception;
1821
1822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1823 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1824
1825 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1826 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001827
glennrpe4017e32011-01-08 17:16:09 +00001828#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001829 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1830 * are building with libpng-1.4.x and can be ignored.
1831 */
cristy3ed852e2009-09-05 21:47:34 +00001832 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001833#else
1834 png_longjmp(ping,1);
1835#endif
cristy3ed852e2009-09-05 21:47:34 +00001836}
1837
glennrpcf002022011-01-30 02:38:15 +00001838static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001839{
cristy16ea1392012-03-21 20:38:41 +00001840 ExceptionInfo
1841 *exception;
1842
cristy3ed852e2009-09-05 21:47:34 +00001843 Image
1844 *image;
1845
cristy16ea1392012-03-21 20:38:41 +00001846 PNGErrorInfo
1847 *error_info;
1848
cristy3ed852e2009-09-05 21:47:34 +00001849 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1850 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001851
cristy16ea1392012-03-21 20:38:41 +00001852 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1853 image=error_info->image;
1854 exception=error_info->exception;
1855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1856 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001857
cristy16ea1392012-03-21 20:38:41 +00001858 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001859 message,"`%s'",image->filename);
1860}
1861
1862#ifdef PNG_USER_MEM_SUPPORTED
glennrp943b7d32013-04-21 00:40:38 +00001863#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +00001864static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1865#else
1866static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1867#endif
cristy3ed852e2009-09-05 21:47:34 +00001868{
cristydf0d90e2011-12-12 01:03:55 +00001869 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001870 return((png_voidp) AcquireMagickMemory((size_t) size));
cristy3ed852e2009-09-05 21:47:34 +00001871}
1872
1873/*
1874 Free a pointer. It is removed from the list at the same time.
1875*/
glennrpcf002022011-01-30 02:38:15 +00001876static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001877{
glennrp3bd393f2011-12-21 18:54:53 +00001878 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001879 ptr=RelinquishMagickMemory(ptr);
1880 return((png_free_ptr) NULL);
1881}
1882#endif
1883
1884#if defined(__cplusplus) || defined(c_plusplus)
1885}
1886#endif
1887
1888static int
glennrpedaa0382012-04-12 14:16:21 +00001889Magick_png_read_raw_profile(png_struct *ping,Image *image,
1890 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001891{
cristybb503372010-05-27 20:51:26 +00001892 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001893 i;
1894
1895 register unsigned char
1896 *dp;
1897
1898 register png_charp
1899 sp;
1900
1901 png_uint_32
1902 length,
1903 nibbles;
1904
1905 StringInfo
1906 *profile;
1907
glennrp0c3e06b2010-11-19 13:45:02 +00001908 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001909 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1910 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1911 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1912 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1913 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1914 13,14,15};
1915
1916 sp=text[ii].text+1;
1917 /* look for newline */
1918 while (*sp != '\n')
1919 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001920
cristy3ed852e2009-09-05 21:47:34 +00001921 /* look for length */
1922 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1923 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001924
cristyf2f27272009-12-17 14:48:46 +00001925 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001926
glennrp97f90e22011-02-22 05:47:58 +00001927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1928 " length: %lu",(unsigned long) length);
1929
cristy3ed852e2009-09-05 21:47:34 +00001930 while (*sp != ' ' && *sp != '\n')
1931 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001932
cristy3ed852e2009-09-05 21:47:34 +00001933 /* allocate space */
1934 if (length == 0)
1935 {
glennrpedaa0382012-04-12 14:16:21 +00001936 png_warning(ping,"invalid profile length");
cristy3ed852e2009-09-05 21:47:34 +00001937 return(MagickFalse);
1938 }
glennrp0fe50b42010-11-16 03:52:51 +00001939
cristy8723e4b2011-09-01 13:11:19 +00001940 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001941
cristy3ed852e2009-09-05 21:47:34 +00001942 if (profile == (StringInfo *) NULL)
1943 {
glennrpedaa0382012-04-12 14:16:21 +00001944 png_warning(ping, "unable to copy profile");
cristy3ed852e2009-09-05 21:47:34 +00001945 return(MagickFalse);
1946 }
glennrp0fe50b42010-11-16 03:52:51 +00001947
cristy3ed852e2009-09-05 21:47:34 +00001948 /* copy profile, skipping white space and column 1 "=" signs */
1949 dp=GetStringInfoDatum(profile);
1950 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001951
cristybb503372010-05-27 20:51:26 +00001952 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001953 {
1954 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1955 {
1956 if (*sp == '\0')
1957 {
glennrpedaa0382012-04-12 14:16:21 +00001958 png_warning(ping, "ran out of profile data");
cristy3ed852e2009-09-05 21:47:34 +00001959 profile=DestroyStringInfo(profile);
1960 return(MagickFalse);
1961 }
1962 sp++;
1963 }
glennrp0fe50b42010-11-16 03:52:51 +00001964
cristy3ed852e2009-09-05 21:47:34 +00001965 if (i%2 == 0)
1966 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001967
cristy3ed852e2009-09-05 21:47:34 +00001968 else
1969 (*dp++)+=unhex[(int) *sp++];
1970 }
1971 /*
1972 We have already read "Raw profile type.
1973 */
cristy16ea1392012-03-21 20:38:41 +00001974 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001975 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001976
cristy3ed852e2009-09-05 21:47:34 +00001977 if (image_info->verbose)
1978 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001979
cristy3ed852e2009-09-05 21:47:34 +00001980 return MagickTrue;
1981}
1982
1983#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1984static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1985{
1986 Image
1987 *image;
1988
1989
1990 /* The unknown chunk structure contains the chunk data:
1991 png_byte name[5];
1992 png_byte *data;
1993 png_size_t size;
1994
1995 Note that libpng has already taken care of the CRC handling.
1996 */
1997
glennrp2ad70152013-03-03 00:44:57 +00001998 LogMagickEvent(CoderEvent,GetMagickModule(),
1999 " read_vpag_chunk: found %c%c%c%c chunk",
2000 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
cristy3ed852e2009-09-05 21:47:34 +00002001
2002 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
2003 chunk->name[2] != 65 ||chunk-> name[3] != 103)
2004 return(0); /* Did not recognize */
2005
2006 /* recognized vpAg */
2007
2008 if (chunk->size != 9)
2009 return(-1); /* Error return */
2010
2011 if (chunk->data[8] != 0)
2012 return(0); /* ImageMagick requires pixel units */
2013
2014 image=(Image *) png_get_user_chunk_ptr(ping);
2015
cristybb503372010-05-27 20:51:26 +00002016 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00002017 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00002018
cristybb503372010-05-27 20:51:26 +00002019 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00002020 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2021
2022 /* Return one of the following: */
2023 /* return(-n); chunk had an error */
2024 /* return(0); did not recognize */
2025 /* return(n); success */
2026
2027 return(1);
2028
2029}
2030#endif
2031
2032/*
2033%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2034% %
2035% %
2036% %
2037% R e a d O n e P N G I m a g e %
2038% %
2039% %
2040% %
2041%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2042%
2043% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2044% (minus the 8-byte signature) and returns it. It allocates the memory
2045% necessary for the new Image structure and returns a pointer to the new
2046% image.
2047%
2048% The format of the ReadOnePNGImage method is:
2049%
2050% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2051% ExceptionInfo *exception)
2052%
2053% A description of each parameter follows:
2054%
2055% o mng_info: Specifies a pointer to a MngInfo structure.
2056%
2057% o image_info: the image info.
2058%
2059% o exception: return any errors or warnings in this structure.
2060%
2061*/
2062static Image *ReadOnePNGImage(MngInfo *mng_info,
2063 const ImageInfo *image_info, ExceptionInfo *exception)
2064{
2065 /* Read one PNG image */
2066
glennrpcc95c3f2011-04-18 16:46:48 +00002067 /* To do: Read the tIME chunk into the date:modify property */
2068 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2069
cristy3ed852e2009-09-05 21:47:34 +00002070 Image
2071 *image;
2072
glennrpd0cae252013-03-15 22:30:41 +00002073 char
glennrpec0ddbc2013-03-16 12:40:12 +00002074 im_vers[32],
2075 libpng_runv[32],
2076 libpng_vers[32],
2077 zlib_runv[32],
2078 zlib_vers[32];
glennrpd0cae252013-03-15 22:30:41 +00002079
cristy3ed852e2009-09-05 21:47:34 +00002080 int
glennrp98b83d42012-07-23 02:50:31 +00002081 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
glennrpcb395ac2011-03-30 19:50:23 +00002082 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002083 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002084 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002085 num_passes,
glennrp913f9612012-06-27 17:50:00 +00002086 number_colors,
glennrpfaa852b2010-03-30 12:17:00 +00002087 pass,
2088 ping_bit_depth,
2089 ping_color_type,
glennrpfcf06162012-11-05 14:57:08 +00002090 ping_file_depth,
glennrpfaa852b2010-03-30 12:17:00 +00002091 ping_interlace_method,
2092 ping_compression_method,
2093 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002094 ping_num_trans,
2095 unit_type;
2096
2097 double
2098 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002099
2100 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002101 logging,
glennrp98b83d42012-07-23 02:50:31 +00002102 ping_found_cHRM,
2103 ping_found_gAMA,
2104 ping_found_iCCP,
2105 ping_found_sRGB,
glennrp3d627862013-02-26 00:19:34 +00002106 ping_found_sRGB_cHRM,
glennrpecab7d72013-05-14 22:50:32 +00002107 ping_preserve_iCCP,
cristy3ed852e2009-09-05 21:47:34 +00002108 status;
2109
cristy09973322013-06-30 21:06:30 +00002110 MemoryInfo
2111 *volatile pixel_info;
2112
cristy16ea1392012-03-21 20:38:41 +00002113 PixelInfo
2114 transparent_color;
2115
2116 PNGErrorInfo
2117 error_info;
2118
glennrpfaa852b2010-03-30 12:17:00 +00002119 png_bytep
2120 ping_trans_alpha;
2121
2122 png_color_16p
2123 ping_background,
2124 ping_trans_color;
2125
cristy3ed852e2009-09-05 21:47:34 +00002126 png_info
2127 *end_info,
2128 *ping_info;
2129
2130 png_struct
2131 *ping;
2132
2133 png_textp
2134 text;
2135
glennrpfaa852b2010-03-30 12:17:00 +00002136 png_uint_32
2137 ping_height,
2138 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002139 x_resolution,
2140 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002141
cristy16ea1392012-03-21 20:38:41 +00002142 QuantumInfo
2143 *quantum_info;
2144
cristybb503372010-05-27 20:51:26 +00002145 ssize_t
cristy756ae432011-11-19 02:18:25 +00002146 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002147 y;
2148
2149 register unsigned char
2150 *p;
2151
cristybb503372010-05-27 20:51:26 +00002152 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002153 i,
2154 x;
2155
cristy16ea1392012-03-21 20:38:41 +00002156 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002157 *q;
2158
2159 size_t
glennrp39992b42010-11-14 00:03:43 +00002160 length,
cristy3ed852e2009-09-05 21:47:34 +00002161 row_offset;
2162
cristyeb3b22a2011-03-31 20:16:11 +00002163 ssize_t
2164 j;
2165
cristy75fc68f2012-10-08 16:26:00 +00002166 unsigned char
cristy09973322013-06-30 21:06:30 +00002167 *ping_pixels;
cristy55b78b52012-10-08 14:01:27 +00002168
glennrp629960f2012-05-29 19:13:52 +00002169#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002170 png_byte unused_chunks[]=
2171 {
2172 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2173 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2174 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2175 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2176 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2177 116, 73, 77, 69, (png_byte) '\0', /* tIME */
glennrp629960f2012-05-29 19:13:52 +00002178#ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2179 /* ignore the APNG chunks */
2180 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2181 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2182 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2183#endif
cristy3ed852e2009-09-05 21:47:34 +00002184 };
2185#endif
2186
2187 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002188 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002189
glennrpd0cae252013-03-15 22:30:41 +00002190 /* Define these outside of the following "if logging()" block so they will
2191 * show in debuggers.
2192 */
2193 *im_vers='\0';
2194 (void) ConcatenateMagickString(im_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002195 MagickLibVersionText,32);
glennrpd0cae252013-03-15 22:30:41 +00002196 (void) ConcatenateMagickString(im_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002197 MagickLibAddendum,32);
2198
glennrpd0cae252013-03-15 22:30:41 +00002199 *libpng_vers='\0';
2200 (void) ConcatenateMagickString(libpng_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002201 PNG_LIBPNG_VER_STRING,32);
2202 *libpng_runv='\0';
2203 (void) ConcatenateMagickString(libpng_runv,
2204 png_get_libpng_ver(NULL),32);
2205
glennrpd0cae252013-03-15 22:30:41 +00002206 *zlib_vers='\0';
2207 (void) ConcatenateMagickString(zlib_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002208 ZLIB_VERSION,32);
2209 *zlib_runv='\0';
2210 (void) ConcatenateMagickString(zlib_runv,
2211 zlib_version,32);
2212
glennrpd0cae252013-03-15 22:30:41 +00002213 if (logging)
2214 {
2215 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
2216 im_vers);
2217 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
2218 libpng_vers);
glennrpec0ddbc2013-03-16 12:40:12 +00002219 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2220 {
2221 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2222 libpng_runv);
2223 }
glennrpd0cae252013-03-15 22:30:41 +00002224 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
2225 zlib_vers);
glennrpec0ddbc2013-03-16 12:40:12 +00002226 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2227 {
2228 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2229 zlib_runv);
2230 }
glennrpd0cae252013-03-15 22:30:41 +00002231 }
2232
glennrp25c1e2b2010-03-25 01:39:56 +00002233#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002234 if (image_info->verbose)
2235 printf("Your PNG library (libpng-%s) is rather old.\n",
2236 PNG_LIBPNG_VER_STRING);
2237#endif
2238
glennrp61b4c952009-11-10 20:40:41 +00002239#if (PNG_LIBPNG_VER >= 10400)
2240# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2241 if (image_info->verbose)
2242 {
2243 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2244 PNG_LIBPNG_VER_STRING);
2245 printf("Please update it.\n");
2246 }
2247# endif
2248#endif
2249
cristy16ea1392012-03-21 20:38:41 +00002250
2251 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002252 image=mng_info->image;
2253
glennrpa6a06632011-01-19 15:15:34 +00002254 if (logging != MagickFalse)
glennrp98b83d42012-07-23 02:50:31 +00002255 {
glennrpa6a06632011-01-19 15:15:34 +00002256 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpec0ddbc2013-03-16 12:40:12 +00002257 " Before reading:");
glennrp3d627862013-02-26 00:19:34 +00002258
2259 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpec0ddbc2013-03-16 12:40:12 +00002260 " image->alpha_trait=%d",(int) image->alpha_trait);
glennrpa6a06632011-01-19 15:15:34 +00002261
glennrp98b83d42012-07-23 02:50:31 +00002262 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpec0ddbc2013-03-16 12:40:12 +00002263 " image->rendering_intent=%d",(int) image->rendering_intent);
glennrpe88af772012-08-22 13:59:50 +00002264
2265 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpec0ddbc2013-03-16 12:40:12 +00002266 " image->colorspace=%d",(int) image->colorspace);
glennrp3d627862013-02-26 00:19:34 +00002267
2268 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpec0ddbc2013-03-16 12:40:12 +00002269 " image->gamma=%f", image->gamma);
glennrp98b83d42012-07-23 02:50:31 +00002270 }
2271 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2272
glennrp0e319732011-01-25 21:53:13 +00002273 /* Set to an out-of-range color unless tRNS chunk is present */
2274 transparent_color.red=65537;
2275 transparent_color.green=65537;
2276 transparent_color.blue=65537;
cristy16ea1392012-03-21 20:38:41 +00002277 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002278
glennrp913f9612012-06-27 17:50:00 +00002279 number_colors=0;
glennrpcb395ac2011-03-30 19:50:23 +00002280 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002281 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002282 num_raw_profiles = 0;
2283
glennrp98b83d42012-07-23 02:50:31 +00002284 ping_found_cHRM = MagickFalse;
2285 ping_found_gAMA = MagickFalse;
2286 ping_found_iCCP = MagickFalse;
2287 ping_found_sRGB = MagickFalse;
glennrp4b917592013-06-20 19:53:59 +00002288 ping_found_sRGB_cHRM = MagickFalse;
glennrpecab7d72013-05-14 22:50:32 +00002289 ping_preserve_iCCP = MagickFalse;
2290
2291 {
2292 const char
2293 *value;
2294
2295 value=GetImageOption(image_info,"png:preserve-iCCP");
2296
2297 if (value == NULL)
2298 value=GetImageArtifact(image,"png:preserve-iCCP");
2299
2300 if (value != NULL)
2301 ping_preserve_iCCP=MagickTrue;
2302 }
glennrp98b83d42012-07-23 02:50:31 +00002303
cristy3ed852e2009-09-05 21:47:34 +00002304 /*
2305 Allocate the PNG structures
2306 */
2307#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00002308 error_info.image=image;
2309 error_info.exception=exception;
2310 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002311 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2312 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002313#else
cristy16ea1392012-03-21 20:38:41 +00002314 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002315 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002316#endif
2317 if (ping == (png_struct *) NULL)
2318 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002319
cristy3ed852e2009-09-05 21:47:34 +00002320 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002321
cristy3ed852e2009-09-05 21:47:34 +00002322 if (ping_info == (png_info *) NULL)
2323 {
2324 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2325 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2326 }
glennrp0fe50b42010-11-16 03:52:51 +00002327
cristy3ed852e2009-09-05 21:47:34 +00002328 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002329
cristy3ed852e2009-09-05 21:47:34 +00002330 if (end_info == (png_info *) NULL)
2331 {
2332 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2333 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2334 }
glennrp0fe50b42010-11-16 03:52:51 +00002335
cristy09973322013-06-30 21:06:30 +00002336 pixel_info=(MemoryInfo *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002337
glennrpfaa852b2010-03-30 12:17:00 +00002338 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002339 {
2340 /*
2341 PNG image is corrupt.
2342 */
2343 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002344
2345#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002346 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002347#endif
glennrpedaa0382012-04-12 14:16:21 +00002348
cristy09973322013-06-30 21:06:30 +00002349 if (pixel_info != (MemoryInfo *) NULL)
2350 pixel_info=RelinquishVirtualMemory(pixel_info);
glennrpedaa0382012-04-12 14:16:21 +00002351
cristy3ed852e2009-09-05 21:47:34 +00002352 if (logging != MagickFalse)
2353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2354 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002355
cristy3ed852e2009-09-05 21:47:34 +00002356 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002357 {
cristy16ea1392012-03-21 20:38:41 +00002358 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002359 image->columns=0;
2360 }
glennrp0fe50b42010-11-16 03:52:51 +00002361
cristy3ed852e2009-09-05 21:47:34 +00002362 return(GetFirstImageInList(image));
2363 }
glennrpedaa0382012-04-12 14:16:21 +00002364
2365 /* { For navigation to end of SETJMP-protected block. Within this
2366 * block, use png_error() instead of Throwing an Exception, to ensure
2367 * that libpng is able to clean up, and that the semaphore is unlocked.
2368 */
2369
2370#ifdef PNG_SETJMP_NOT_THREAD_SAFE
2371 LockSemaphoreInfo(ping_semaphore);
2372#endif
2373
glennrp943b7d32013-04-21 00:40:38 +00002374#ifdef PNG_BENIGN_ERRORS_SUPPORTED
glennrpa3a5f952013-04-17 12:58:15 +00002375 /* Allow benign errors */
2376 png_set_benign_errors(ping, 1);
2377#endif
2378
cristy3ed852e2009-09-05 21:47:34 +00002379 /*
2380 Prepare PNG for reading.
2381 */
glennrpfaa852b2010-03-30 12:17:00 +00002382
cristy3ed852e2009-09-05 21:47:34 +00002383 mng_info->image_found++;
2384 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002385
cristy3ed852e2009-09-05 21:47:34 +00002386 if (LocaleCompare(image_info->magick,"MNG") == 0)
2387 {
2388#if defined(PNG_MNG_FEATURES_SUPPORTED)
2389 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2390 png_set_read_fn(ping,image,png_get_data);
2391#else
2392#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2393 png_permit_empty_plte(ping,MagickTrue);
2394 png_set_read_fn(ping,image,png_get_data);
2395#else
2396 mng_info->image=image;
2397 mng_info->bytes_in_read_buffer=0;
2398 mng_info->found_empty_plte=MagickFalse;
2399 mng_info->have_saved_bkgd_index=MagickFalse;
2400 png_set_read_fn(ping,mng_info,mng_get_data);
2401#endif
2402#endif
2403 }
glennrp0fe50b42010-11-16 03:52:51 +00002404
cristy3ed852e2009-09-05 21:47:34 +00002405 else
2406 png_set_read_fn(ping,image,png_get_data);
2407
2408#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2409 /* Ignore unused chunks and all unknown chunks except for vpAg */
glennrp2ad70152013-03-03 00:44:57 +00002410#if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2411 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2412#else
cristy3ed852e2009-09-05 21:47:34 +00002413 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
glennrp2ad70152013-03-03 00:44:57 +00002414#endif
cristy3ed852e2009-09-05 21:47:34 +00002415 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2416 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2417 (int)sizeof(unused_chunks)/5);
2418 /* Callback for other unknown chunks */
2419 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2420#endif
2421
glennrp2feb1412013-01-22 15:02:16 +00002422#ifdef PNG_SET_USER_LIMITS_SUPPORTED
glennrp09cd9622013-01-22 15:17:38 +00002423# if (PNG_LIBPNG_VER >= 10400)
glennrp2feb1412013-01-22 15:02:16 +00002424 /* Limit the size of the chunk storage cache used for sPLT, text,
glennrp68736192013-01-24 06:32:08 +00002425 * and unknown chunks.
glennrp2feb1412013-01-22 15:02:16 +00002426 */
glennrp68736192013-01-24 06:32:08 +00002427 png_set_chunk_cache_max(ping, 32767);
glennrp09cd9622013-01-22 15:17:38 +00002428# endif
glennrp2feb1412013-01-22 15:02:16 +00002429#endif
2430
glennrp9bf97b62012-06-06 21:03:14 +00002431#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2432 /* Disable new libpng-1.5.10 feature */
2433 png_set_check_for_invalid_index (ping, 0);
2434#endif
2435
glennrp991e92a2010-01-28 03:09:00 +00002436#if (PNG_LIBPNG_VER < 10400)
2437# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2438 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002439 /* Disable thread-unsafe features of pnggccrd */
2440 if (png_access_version_number() >= 10200)
2441 {
2442 png_uint_32 mmx_disable_mask=0;
2443 png_uint_32 asm_flags;
2444
2445 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2446 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2447 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2448 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2449 asm_flags=png_get_asm_flags(ping);
2450 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2451 }
glennrp991e92a2010-01-28 03:09:00 +00002452# endif
cristy3ed852e2009-09-05 21:47:34 +00002453#endif
2454
2455 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002456
2457 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2458 &ping_bit_depth,&ping_color_type,
2459 &ping_interlace_method,&ping_compression_method,
2460 &ping_filter_method);
2461
glennrpfcf06162012-11-05 14:57:08 +00002462 ping_file_depth = ping_bit_depth;
2463
glennrp5830fbc2013-01-27 06:11:45 +00002464 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2465 {
2466 char
2467 msg[MaxTextExtent];
2468
2469 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
glennrp3398b5b2013-05-03 23:10:31 +00002470 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
glennrp5830fbc2013-01-27 06:11:45 +00002471
2472 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
glennrp3398b5b2013-05-03 23:10:31 +00002473 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
glennrp5830fbc2013-01-27 06:11:45 +00002474 }
2475
glennrpfaa852b2010-03-30 12:17:00 +00002476 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2477 &ping_trans_color);
2478
2479 (void) png_get_bKGD(ping, ping_info, &ping_background);
2480
2481 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002482 {
glennrpfcf06162012-11-05 14:57:08 +00002483 png_set_packing(ping);
2484 ping_bit_depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00002485 }
glennrpfaa852b2010-03-30 12:17:00 +00002486
2487 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002488 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002489 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
glennrp98b83d42012-07-23 02:50:31 +00002490
cristy176b29a2012-06-21 13:35:15 +00002491 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2492 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2493 {
2494 image->rendering_intent=UndefinedIntent;
glennrp98b83d42012-07-23 02:50:31 +00002495 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
cristy176b29a2012-06-21 13:35:15 +00002496 image->gamma=1.000;
2497 (void) ResetMagickMemory(&image->chromaticity,0,
2498 sizeof(image->chromaticity));
2499 }
glennrp98b83d42012-07-23 02:50:31 +00002500
cristy3ed852e2009-09-05 21:47:34 +00002501 if (logging != MagickFalse)
2502 {
2503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002504 " PNG width: %.20g, height: %.20g",
2505 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002506
cristy3ed852e2009-09-05 21:47:34 +00002507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2508 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002509 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002510
cristy3ed852e2009-09-05 21:47:34 +00002511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2512 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002513 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002514
cristy3ed852e2009-09-05 21:47:34 +00002515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2516 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002517 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002518 }
2519
glennrpecab7d72013-05-14 22:50:32 +00002520 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2521 {
2522 ping_found_iCCP=MagickTrue;
2523 if (logging != MagickFalse)
2524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2525 " Found PNG iCCP chunk.");
2526 }
2527
glennrp98b83d42012-07-23 02:50:31 +00002528 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2529 {
2530 ping_found_gAMA=MagickTrue;
2531 if (logging != MagickFalse)
2532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2533 " Found PNG gAMA chunk.");
2534 }
2535
2536 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2537 {
2538 ping_found_cHRM=MagickTrue;
2539 if (logging != MagickFalse)
2540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2541 " Found PNG cHRM chunk.");
2542 }
2543
glennrpecab7d72013-05-14 22:50:32 +00002544 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2545 PNG_INFO_sRGB))
glennrp98b83d42012-07-23 02:50:31 +00002546 {
2547 ping_found_sRGB=MagickTrue;
2548 if (logging != MagickFalse)
2549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2550 " Found PNG sRGB chunk.");
2551 }
2552
glennrpfaa852b2010-03-30 12:17:00 +00002553#ifdef PNG_READ_iCCP_SUPPORTED
glennrpecab7d72013-05-14 22:50:32 +00002554 if (ping_found_iCCP !=MagickTrue &&
2555 ping_found_sRGB != MagickTrue &&
2556 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2557 {
2558 ping_found_iCCP=MagickTrue;
2559 if (logging != MagickFalse)
2560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2561 " Found PNG iCCP chunk.");
2562 }
2563
glennrpfaa852b2010-03-30 12:17:00 +00002564 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002565 {
2566 int
2567 compression;
2568
glennrpe4017e32011-01-08 17:16:09 +00002569#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002570 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002571 info;
2572#else
2573 png_bytep
2574 info;
2575#endif
2576
2577 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002578 name;
2579
2580 png_uint_32
2581 profile_length;
2582
2583 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2584 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002585
cristy3ed852e2009-09-05 21:47:34 +00002586 if (profile_length != 0)
2587 {
2588 StringInfo
2589 *profile;
2590
2591 if (logging != MagickFalse)
2592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2593 " Reading PNG iCCP chunk.");
glennrpecab7d72013-05-14 22:50:32 +00002594
cristye8f8f382011-09-01 13:32:37 +00002595 profile=BlobToStringInfo(info,profile_length);
glennrpecab7d72013-05-14 22:50:32 +00002596
cristye8f8f382011-09-01 13:32:37 +00002597 if (profile == (StringInfo *) NULL)
glennrpecab7d72013-05-14 22:50:32 +00002598 {
2599 png_warning(ping, "ICC profile is NULL");
2600 profile=DestroyStringInfo(profile);
2601 }
glennrpedaa0382012-04-12 14:16:21 +00002602 else
glennrpecab7d72013-05-14 22:50:32 +00002603 {
2604 if (ping_preserve_iCCP == MagickFalse)
glennrpedaa0382012-04-12 14:16:21 +00002605 {
glennrpecab7d72013-05-14 22:50:32 +00002606 int
2607 icheck,
2608 got_crc=0;
2609
2610
2611 png_uint_32
2612 length,
2613 profile_crc=0;
2614
2615 unsigned char
2616 *data;
2617
2618 length=(png_uint_32) GetStringInfoLength(profile);
2619
2620 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2621 {
2622 if (length == sRGB_info[icheck].len)
2623 {
2624 if (got_crc == 0)
2625 {
2626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2627 " Got a %lu-byte ICC profile (potentially sRGB)",
2628 (unsigned long) length);
2629
2630 data=GetStringInfoDatum(profile);
2631 profile_crc=crc32(0,data,length);
2632
2633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2634 " with crc=%8x",(unsigned int) profile_crc);
2635 got_crc++;
2636 }
2637
2638 if (profile_crc == sRGB_info[icheck].crc)
2639 {
2640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2641 " It is sRGB with rendering intent = %s",
2642 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2643 sRGB_info[icheck].intent));
2644 if (image->rendering_intent==UndefinedIntent)
2645 {
2646 image->rendering_intent=
2647 Magick_RenderingIntent_from_PNG_RenderingIntent(
2648 sRGB_info[icheck].intent);
2649 }
2650 break;
2651 }
2652 }
2653 }
2654 if (sRGB_info[icheck].len == 0)
2655 {
2656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2657 " Got a %lu-byte ICC profile not recognized as sRGB",
2658 (unsigned long) length);
2659 (void) SetImageProfile(image,"icc",profile,exception);
2660 }
glennrpedaa0382012-04-12 14:16:21 +00002661 }
glennrpecab7d72013-05-14 22:50:32 +00002662 else /* Preserve-iCCP */
2663 {
2664 (void) SetImageProfile(image,"icc",profile,exception);
2665 }
2666
2667 profile=DestroyStringInfo(profile);
2668 }
cristy3ed852e2009-09-05 21:47:34 +00002669 }
2670 }
2671#endif
glennrpecab7d72013-05-14 22:50:32 +00002672
cristy3ed852e2009-09-05 21:47:34 +00002673#if defined(PNG_READ_sRGB_SUPPORTED)
2674 {
glennrpecab7d72013-05-14 22:50:32 +00002675 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2676 PNG_INFO_sRGB))
2677 {
2678 if (png_get_sRGB(ping,ping_info,&intent))
cristy2ea7a8e2012-02-08 01:04:50 +00002679 {
glennrpecab7d72013-05-14 22:50:32 +00002680 if (image->rendering_intent == UndefinedIntent)
2681 image->rendering_intent=
2682 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002683
cristy3ed852e2009-09-05 21:47:34 +00002684 if (logging != MagickFalse)
2685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002686 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002687 }
glennrpecab7d72013-05-14 22:50:32 +00002688 }
2689
2690 else if (mng_info->have_global_srgb)
2691 {
2692 if (image->rendering_intent == UndefinedIntent)
2693 image->rendering_intent=
2694 Magick_RenderingIntent_from_PNG_RenderingIntent
2695 (mng_info->global_srgb_intent);
2696 }
cristy3ed852e2009-09-05 21:47:34 +00002697 }
2698#endif
glennrpecab7d72013-05-14 22:50:32 +00002699
2700
cristy3ed852e2009-09-05 21:47:34 +00002701 {
glennrpfaa852b2010-03-30 12:17:00 +00002702 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2703 if (mng_info->have_global_gama)
2704 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002705
cristy3ed852e2009-09-05 21:47:34 +00002706 if (png_get_gAMA(ping,ping_info,&file_gamma))
2707 {
2708 image->gamma=(float) file_gamma;
2709 if (logging != MagickFalse)
2710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2711 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2712 }
2713 }
glennrp98b83d42012-07-23 02:50:31 +00002714
glennrpfaa852b2010-03-30 12:17:00 +00002715 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2716 {
2717 if (mng_info->have_global_chrm != MagickFalse)
2718 {
2719 (void) png_set_cHRM(ping,ping_info,
2720 mng_info->global_chrm.white_point.x,
2721 mng_info->global_chrm.white_point.y,
2722 mng_info->global_chrm.red_primary.x,
2723 mng_info->global_chrm.red_primary.y,
2724 mng_info->global_chrm.green_primary.x,
2725 mng_info->global_chrm.green_primary.y,
2726 mng_info->global_chrm.blue_primary.x,
2727 mng_info->global_chrm.blue_primary.y);
2728 }
2729 }
glennrp0fe50b42010-11-16 03:52:51 +00002730
glennrpfaa852b2010-03-30 12:17:00 +00002731 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002732 {
2733 (void) png_get_cHRM(ping,ping_info,
2734 &image->chromaticity.white_point.x,
2735 &image->chromaticity.white_point.y,
2736 &image->chromaticity.red_primary.x,
2737 &image->chromaticity.red_primary.y,
2738 &image->chromaticity.green_primary.x,
2739 &image->chromaticity.green_primary.y,
2740 &image->chromaticity.blue_primary.x,
2741 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002742
glennrpecab7d72013-05-14 22:50:32 +00002743 ping_found_cHRM=MagickTrue;
2744
glennrp3d627862013-02-26 00:19:34 +00002745 if (image->chromaticity.red_primary.x>0.6399f &&
2746 image->chromaticity.red_primary.x<0.6401f &&
2747 image->chromaticity.red_primary.y>0.3299f &&
2748 image->chromaticity.red_primary.y<0.3301f &&
2749 image->chromaticity.green_primary.x>0.2999f &&
2750 image->chromaticity.green_primary.x<0.3001f &&
2751 image->chromaticity.green_primary.y>0.5999f &&
2752 image->chromaticity.green_primary.y<0.6001f &&
2753 image->chromaticity.blue_primary.x>0.1499f &&
2754 image->chromaticity.blue_primary.x<0.1501f &&
2755 image->chromaticity.blue_primary.y>0.0599f &&
2756 image->chromaticity.blue_primary.y<0.0601f &&
2757 image->chromaticity.white_point.x>0.3126f &&
2758 image->chromaticity.white_point.x<0.3128f &&
2759 image->chromaticity.white_point.y>0.3289f &&
2760 image->chromaticity.white_point.y<0.3291f)
2761 ping_found_sRGB_cHRM=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002762 }
glennrp0fe50b42010-11-16 03:52:51 +00002763
glennrpe610a072010-08-05 17:08:46 +00002764 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002765 {
glennrp3d627862013-02-26 00:19:34 +00002766 if (ping_found_sRGB != MagickTrue &&
2767 (ping_found_gAMA != MagickTrue ||
glennrp84288232013-03-02 15:52:36 +00002768 (image->gamma > .45 && image->gamma < .46)) &&
glennrp3d627862013-02-26 00:19:34 +00002769 (ping_found_cHRM != MagickTrue ||
2770 ping_found_sRGB_cHRM == MagickTrue) &&
2771 ping_found_iCCP != MagickTrue)
glennrp5cf1bff2013-01-16 02:44:03 +00002772 {
2773 png_set_sRGB(ping,ping_info,
2774 Magick_RenderingIntent_to_PNG_RenderingIntent
2775 (image->rendering_intent));
glennrp5cf1bff2013-01-16 02:44:03 +00002776 file_gamma=1.000f/2.200f;
2777 ping_found_sRGB=MagickTrue;
glennrp84288232013-03-02 15:52:36 +00002778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp918b9dc2013-04-03 13:41:41 +00002779 " Setting sRGB as if in input");
glennrp5cf1bff2013-01-16 02:44:03 +00002780 }
cristy3ed852e2009-09-05 21:47:34 +00002781 }
glennrp5cf1bff2013-01-16 02:44:03 +00002782
cristy3ed852e2009-09-05 21:47:34 +00002783#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002784 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002785 {
cristy905ef802011-02-23 00:29:18 +00002786 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2787 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002788
cristy3ed852e2009-09-05 21:47:34 +00002789 if (logging != MagickFalse)
2790 if (image->page.x || image->page.y)
2791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002792 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2793 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002794 }
2795#endif
2796#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002797 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2798 {
2799 if (mng_info->have_global_phys)
2800 {
2801 png_set_pHYs(ping,ping_info,
2802 mng_info->global_x_pixels_per_unit,
2803 mng_info->global_y_pixels_per_unit,
2804 mng_info->global_phys_unit_type);
2805 }
2806 }
2807
2808 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002809 {
cristy3ed852e2009-09-05 21:47:34 +00002810 /*
2811 Set image resolution.
2812 */
2813 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002814 &unit_type);
cristy16ea1392012-03-21 20:38:41 +00002815 image->resolution.x=(double) x_resolution;
2816 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002817
cristy3ed852e2009-09-05 21:47:34 +00002818 if (unit_type == PNG_RESOLUTION_METER)
2819 {
2820 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00002821 image->resolution.x=(double) x_resolution/100.0;
2822 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002823 }
glennrp0fe50b42010-11-16 03:52:51 +00002824
cristy3ed852e2009-09-05 21:47:34 +00002825 if (logging != MagickFalse)
2826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002827 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2828 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002829 }
cristy3ed852e2009-09-05 21:47:34 +00002830#endif
glennrp823b55c2011-03-14 18:46:46 +00002831
glennrpfaa852b2010-03-30 12:17:00 +00002832 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002833 {
cristy3ed852e2009-09-05 21:47:34 +00002834 png_colorp
2835 palette;
2836
2837 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002838
cristy3ed852e2009-09-05 21:47:34 +00002839 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002840 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002841 {
2842 if (mng_info->global_plte_length)
2843 {
2844 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2845 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002846
glennrpfaa852b2010-03-30 12:17:00 +00002847 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrpedaa0382012-04-12 14:16:21 +00002848 {
cristy3ed852e2009-09-05 21:47:34 +00002849 if (mng_info->global_trns_length)
2850 {
glennrpedaa0382012-04-12 14:16:21 +00002851 png_warning(ping,
2852 "global tRNS has more entries than global PLTE");
cristy3ed852e2009-09-05 21:47:34 +00002853 }
glennrpedaa0382012-04-12 14:16:21 +00002854 else
2855 {
2856 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2857 (int) mng_info->global_trns_length,NULL);
2858 }
2859 }
glennrpbfd9e612011-04-22 14:02:20 +00002860#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002861 if (
2862#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2863 mng_info->have_saved_bkgd_index ||
2864#endif
glennrpfaa852b2010-03-30 12:17:00 +00002865 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002866 {
2867 png_color_16
2868 background;
2869
2870#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2871 if (mng_info->have_saved_bkgd_index)
2872 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002873#endif
glennrpfaa852b2010-03-30 12:17:00 +00002874 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2875 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002876
cristy3ed852e2009-09-05 21:47:34 +00002877 background.red=(png_uint_16)
2878 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002879
cristy3ed852e2009-09-05 21:47:34 +00002880 background.green=(png_uint_16)
2881 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002882
cristy3ed852e2009-09-05 21:47:34 +00002883 background.blue=(png_uint_16)
2884 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002885
glennrpc6c391a2011-04-27 02:23:56 +00002886 background.gray=(png_uint_16)
2887 mng_info->global_plte[background.index].green;
2888
cristy3ed852e2009-09-05 21:47:34 +00002889 png_set_bKGD(ping,ping_info,&background);
2890 }
2891#endif
2892 }
2893 else
glennrpedaa0382012-04-12 14:16:21 +00002894 png_error(ping,"No global PLTE in file");
cristy3ed852e2009-09-05 21:47:34 +00002895 }
2896 }
2897
glennrpbfd9e612011-04-22 14:02:20 +00002898#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002899 if (mng_info->have_global_bkgd &&
2900 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002901 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002902
glennrpfaa852b2010-03-30 12:17:00 +00002903 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002904 {
glennrpbfd9e612011-04-22 14:02:20 +00002905 unsigned int
2906 bkgd_scale;
2907
cristy3ed852e2009-09-05 21:47:34 +00002908 /*
2909 Set image background color.
2910 */
2911 if (logging != MagickFalse)
2912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2913 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002914
glennrpbfd9e612011-04-22 14:02:20 +00002915 /* Scale background components to 16-bit, then scale
2916 * to quantum depth
2917 */
2918 if (logging != MagickFalse)
2919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2920 " raw ping_background=(%d,%d,%d).",ping_background->red,
2921 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002922
glennrpbfd9e612011-04-22 14:02:20 +00002923 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002924
glennrpfcf06162012-11-05 14:57:08 +00002925 if (ping_file_depth == 1)
glennrpbfd9e612011-04-22 14:02:20 +00002926 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002927
glennrpfcf06162012-11-05 14:57:08 +00002928 else if (ping_file_depth == 2)
glennrpbfd9e612011-04-22 14:02:20 +00002929 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002930
glennrpfcf06162012-11-05 14:57:08 +00002931 else if (ping_file_depth == 4)
glennrpbfd9e612011-04-22 14:02:20 +00002932 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002933
glennrpfcf06162012-11-05 14:57:08 +00002934 if (ping_file_depth <= 8)
glennrpbfd9e612011-04-22 14:02:20 +00002935 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002936
glennrpbfd9e612011-04-22 14:02:20 +00002937 ping_background->red *= bkgd_scale;
2938 ping_background->green *= bkgd_scale;
2939 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002940
glennrpbfd9e612011-04-22 14:02:20 +00002941 if (logging != MagickFalse)
2942 {
glennrp2cbb4482010-06-02 04:37:24 +00002943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2944 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002945
glennrp2cbb4482010-06-02 04:37:24 +00002946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2947 " ping_background=(%d,%d,%d).",ping_background->red,
2948 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002949 }
glennrp2cbb4482010-06-02 04:37:24 +00002950
glennrpbfd9e612011-04-22 14:02:20 +00002951 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002952 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002953
glennrpbfd9e612011-04-22 14:02:20 +00002954 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002955 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002956
glennrpbfd9e612011-04-22 14:02:20 +00002957 image->background_color.blue=
2958 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002959
cristy16ea1392012-03-21 20:38:41 +00002960 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002961
glennrpbfd9e612011-04-22 14:02:20 +00002962 if (logging != MagickFalse)
2963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2964 " image->background_color=(%.20g,%.20g,%.20g).",
2965 (double) image->background_color.red,
2966 (double) image->background_color.green,
2967 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002968 }
glennrpbfd9e612011-04-22 14:02:20 +00002969#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002970
glennrpfaa852b2010-03-30 12:17:00 +00002971 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002972 {
2973 /*
glennrpa6a06632011-01-19 15:15:34 +00002974 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002975 */
2976 int
2977 max_sample;
2978
cristy35ef8242010-06-03 16:24:13 +00002979 size_t
2980 one=1;
2981
cristy3ed852e2009-09-05 21:47:34 +00002982 if (logging != MagickFalse)
2983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2984 " Reading PNG tRNS chunk.");
2985
glennrpfcf06162012-11-05 14:57:08 +00002986 max_sample = (int) ((one << ping_file_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002987
glennrpfaa852b2010-03-30 12:17:00 +00002988 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2989 (int)ping_trans_color->gray > max_sample) ||
2990 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2991 ((int)ping_trans_color->red > max_sample ||
2992 (int)ping_trans_color->green > max_sample ||
2993 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002994 {
2995 if (logging != MagickFalse)
2996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2997 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002998 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002999 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy8a46d822012-08-28 23:32:39 +00003000 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00003001 }
3002 else
3003 {
glennrpa6a06632011-01-19 15:15:34 +00003004 int
3005 scale_to_short;
3006
glennrpfcf06162012-11-05 14:57:08 +00003007 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
glennrpa6a06632011-01-19 15:15:34 +00003008
3009 /* Scale transparent_color to short */
3010 transparent_color.red= scale_to_short*ping_trans_color->red;
3011 transparent_color.green= scale_to_short*ping_trans_color->green;
3012 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy16ea1392012-03-21 20:38:41 +00003013 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00003014
glennrpfaa852b2010-03-30 12:17:00 +00003015 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00003016 {
glennrp0f111982010-07-07 20:18:33 +00003017 if (logging != MagickFalse)
3018 {
3019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3020 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00003021
glennrp0f111982010-07-07 20:18:33 +00003022 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00003023 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00003024 }
cristy16ea1392012-03-21 20:38:41 +00003025 transparent_color.red=transparent_color.alpha;
3026 transparent_color.green=transparent_color.alpha;
3027 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00003028 }
3029 }
3030 }
3031#if defined(PNG_READ_sBIT_SUPPORTED)
3032 if (mng_info->have_global_sbit)
3033 {
glennrpfaa852b2010-03-30 12:17:00 +00003034 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00003035 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3036 }
3037#endif
3038 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00003039
cristy3ed852e2009-09-05 21:47:34 +00003040 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00003041
3042 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3043
cristy3ed852e2009-09-05 21:47:34 +00003044 /*
3045 Initialize image structure.
3046 */
3047 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00003048 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00003049 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00003050 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00003051 if (mng_info->mng_type == 0)
3052 {
glennrpfaa852b2010-03-30 12:17:00 +00003053 mng_info->mng_width=ping_width;
3054 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00003055 mng_info->frame=mng_info->image_box;
3056 mng_info->clip=mng_info->image_box;
3057 }
glennrp0fe50b42010-11-16 03:52:51 +00003058
cristy3ed852e2009-09-05 21:47:34 +00003059 else
3060 {
3061 image->page.y=mng_info->y_off[mng_info->object_id];
3062 }
glennrp0fe50b42010-11-16 03:52:51 +00003063
cristy3ed852e2009-09-05 21:47:34 +00003064 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00003065 image->columns=ping_width;
3066 image->rows=ping_height;
glennrpa6c5d342012-05-14 13:21:17 +00003067
cristy16ea1392012-03-21 20:38:41 +00003068 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3069 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
glennrp8d0bca52012-05-17 02:33:23 +00003070 {
glennrpe88af772012-08-22 13:59:50 +00003071 if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
glennrp3d627862013-02-26 00:19:34 +00003072 image->gamma == 1.0) && ping_found_sRGB != MagickTrue)
glennrp8d0bca52012-05-17 02:33:23 +00003073 {
3074 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
glennrpe88af772012-08-22 13:59:50 +00003075 * image->colorspace to GRAY, and reset image->chromaticity.
glennrp8d0bca52012-05-17 02:33:23 +00003076 */
3077 SetImageColorspace(image,GRAYColorspace,exception);
3078 }
glennrp8d0bca52012-05-17 02:33:23 +00003079 }
glennrpe88af772012-08-22 13:59:50 +00003080
3081 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3082 " image->colorspace=%d",(int) image->colorspace);
glennrpa6c5d342012-05-14 13:21:17 +00003083
glennrpfaa852b2010-03-30 12:17:00 +00003084 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrp32340ff2012-08-25 17:36:19 +00003085 ((int) ping_bit_depth < 16 &&
3086 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00003087 {
cristybefe4d22010-06-07 01:18:58 +00003088 size_t
3089 one;
3090
cristy3ed852e2009-09-05 21:47:34 +00003091 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00003092 one=1;
glennrpfcf06162012-11-05 14:57:08 +00003093 image->colors=one << ping_file_depth;
cristy3ed852e2009-09-05 21:47:34 +00003094#if (MAGICKCORE_QUANTUM_DEPTH == 8)
3095 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00003096 image->colors=256;
3097#else
3098 if (image->colors > 65536L)
3099 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00003100#endif
glennrpfaa852b2010-03-30 12:17:00 +00003101 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00003102 {
cristy3ed852e2009-09-05 21:47:34 +00003103 png_colorp
3104 palette;
3105
3106 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00003107 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00003108
cristy3ed852e2009-09-05 21:47:34 +00003109 if (logging != MagickFalse)
3110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3111 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3112 }
3113 }
3114
3115 if (image->storage_class == PseudoClass)
3116 {
3117 /*
3118 Initialize image colormap.
3119 */
cristy16ea1392012-03-21 20:38:41 +00003120 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
glennrpedaa0382012-04-12 14:16:21 +00003121 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003122
glennrpfaa852b2010-03-30 12:17:00 +00003123 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00003124 {
cristy3ed852e2009-09-05 21:47:34 +00003125 png_colorp
3126 palette;
3127
3128 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00003129
glennrp6af6cf12011-04-22 13:05:16 +00003130 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00003131 {
3132 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3133 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3134 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3135 }
glennrp6af6cf12011-04-22 13:05:16 +00003136
glennrp67b9c1a2011-04-22 18:47:36 +00003137 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00003138 {
3139 image->colormap[i].red=0;
3140 image->colormap[i].green=0;
3141 image->colormap[i].blue=0;
3142 }
cristy3ed852e2009-09-05 21:47:34 +00003143 }
glennrp0fe50b42010-11-16 03:52:51 +00003144
cristy3ed852e2009-09-05 21:47:34 +00003145 else
3146 {
cristybb503372010-05-27 20:51:26 +00003147 size_t
cristy3ed852e2009-09-05 21:47:34 +00003148 scale;
3149
glennrpfcf06162012-11-05 14:57:08 +00003150 scale=(QuantumRange/((1UL << ping_file_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00003151
cristy3ed852e2009-09-05 21:47:34 +00003152 if (scale < 1)
3153 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00003154
cristybb503372010-05-27 20:51:26 +00003155 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00003156 {
3157 image->colormap[i].red=(Quantum) (i*scale);
3158 image->colormap[i].green=(Quantum) (i*scale);
3159 image->colormap[i].blue=(Quantum) (i*scale);
3160 }
3161 }
3162 }
glennrp147bc912011-03-30 18:47:21 +00003163
glennrpcb395ac2011-03-30 19:50:23 +00003164 /* Set some properties for reporting by "identify" */
3165 {
glennrp147bc912011-03-30 18:47:21 +00003166 char
3167 msg[MaxTextExtent];
3168
glennrpfcf06162012-11-05 14:57:08 +00003169 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
glennrp147bc912011-03-30 18:47:21 +00003170 ping_interlace_method in value */
3171
cristy3b6fd2e2011-05-20 12:53:50 +00003172 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00003173 "%d, %d",(int) ping_width, (int) ping_height);
glennrp3398b5b2013-05-03 23:10:31 +00003174 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00003175
glennrpfcf06162012-11-05 14:57:08 +00003176 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
glennrp3398b5b2013-05-03 23:10:31 +00003177 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00003178
glennrp5dff4352012-06-06 22:12:04 +00003179 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3180 (int) ping_color_type,
3181 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
glennrp3398b5b2013-05-03 23:10:31 +00003182 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00003183
glennrp913f9612012-06-27 17:50:00 +00003184 if (ping_interlace_method == 0)
3185 {
3186 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3187 (int) ping_interlace_method);
3188 }
3189 else if (ping_interlace_method == 1)
3190 {
3191 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3192 (int) ping_interlace_method);
3193 }
3194 else
3195 {
3196 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3197 (int) ping_interlace_method);
3198 }
3199 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3200
3201 if (number_colors != 0)
3202 {
3203 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3204 (int) number_colors);
glennrp3398b5b2013-05-03 23:10:31 +00003205 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
glennrp913f9612012-06-27 17:50:00 +00003206 exception);
3207 }
glennrpcb395ac2011-03-30 19:50:23 +00003208 }
glennrp147bc912011-03-30 18:47:21 +00003209
cristy3ed852e2009-09-05 21:47:34 +00003210 /*
3211 Read image scanlines.
3212 */
3213 if (image->delay != 0)
3214 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00003215
glennrp0ca69b12010-07-26 01:57:52 +00003216 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00003217 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3218 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00003219 {
glennrp1b888c42012-03-02 15:18:14 +00003220 /* This happens later in non-ping decodes */
3221 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3222 image->storage_class=DirectClass;
3223
cristy3ed852e2009-09-05 21:47:34 +00003224 if (logging != MagickFalse)
3225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003226 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003227 mng_info->scenes_found-1);
3228 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00003229
3230#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00003231 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003232#endif
glennrpedaa0382012-04-12 14:16:21 +00003233
cristy3ed852e2009-09-05 21:47:34 +00003234 if (logging != MagickFalse)
3235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3236 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00003237
cristy3ed852e2009-09-05 21:47:34 +00003238 return(image);
3239 }
glennrp0fe50b42010-11-16 03:52:51 +00003240
cristy3ed852e2009-09-05 21:47:34 +00003241 if (logging != MagickFalse)
3242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3243 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00003244
cristy3ed852e2009-09-05 21:47:34 +00003245 if (num_passes > 1)
cristy09973322013-06-30 21:06:30 +00003246 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
glennrpcf002022011-01-30 02:38:15 +00003247 sizeof(*ping_pixels));
cristy09973322013-06-30 21:06:30 +00003248 else
3249 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00003250
cristy09973322013-06-30 21:06:30 +00003251 if (pixel_info == (MemoryInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003252 png_error(ping,"Memory allocation failed");
cristy09973322013-06-30 21:06:30 +00003253 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
glennrp0fe50b42010-11-16 03:52:51 +00003254
cristy3ed852e2009-09-05 21:47:34 +00003255 if (logging != MagickFalse)
3256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3257 " Converting PNG pixels to pixel packets");
3258 /*
3259 Convert PNG pixels to pixel packets.
3260 */
cristy16ea1392012-03-21 20:38:41 +00003261 quantum_info=AcquireQuantumInfo(image_info,image);
3262
3263 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003264 png_error(ping,"Failed to allocate quantum_info");
glennrp0fe50b42010-11-16 03:52:51 +00003265
glennrp4b840d72012-11-22 16:01:16 +00003266 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3267
glennrpc8cbc5d2011-01-01 00:12:34 +00003268 {
3269
3270 MagickBooleanType
3271 found_transparent_pixel;
3272
3273 found_transparent_pixel=MagickFalse;
3274
cristy3ed852e2009-09-05 21:47:34 +00003275 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00003276 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003277 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00003278 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003279 /*
3280 Convert image to DirectClass pixel packets.
3281 */
cristy8a46d822012-08-28 23:32:39 +00003282 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
glennrpc8cbc5d2011-01-01 00:12:34 +00003283 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3284 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
cristyb0a657e2012-08-29 00:45:37 +00003285 BlendPixelTrait : UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00003286
glennrpc8cbc5d2011-01-01 00:12:34 +00003287 for (y=0; y < (ssize_t) image->rows; y++)
3288 {
3289 if (num_passes > 1)
3290 row_offset=ping_rowbytes*y;
3291
3292 else
3293 row_offset=0;
3294
glennrpcf002022011-01-30 02:38:15 +00003295 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc5c31be2012-11-07 18:42:18 +00003296
3297 if (pass < num_passes-1)
3298 continue;
3299
cristy862a33c2012-05-17 22:49:37 +00003300 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003301
cristy16ea1392012-03-21 20:38:41 +00003302 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00003303 break;
3304
cristy16ea1392012-03-21 20:38:41 +00003305 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3306 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3307 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003308
cristy16ea1392012-03-21 20:38:41 +00003309 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3310 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3311 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003312
cristy16ea1392012-03-21 20:38:41 +00003313 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3314 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3315 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003316
cristy16ea1392012-03-21 20:38:41 +00003317 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3318 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3319 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003320
cristy16ea1392012-03-21 20:38:41 +00003321 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3322 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3323 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00003324
glennrpc8cbc5d2011-01-01 00:12:34 +00003325 if (found_transparent_pixel == MagickFalse)
3326 {
3327 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00003328 if (y== 0 && logging != MagickFalse)
3329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3330 " Looking for cheap transparent pixel");
3331
glennrpc8cbc5d2011-01-01 00:12:34 +00003332 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3333 {
glennrp5aa37f62011-01-02 03:07:57 +00003334 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3335 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy16ea1392012-03-21 20:38:41 +00003336 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00003337 {
glennrpa6a06632011-01-19 15:15:34 +00003338 if (logging != MagickFalse)
3339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3340 " ...got one.");
3341
glennrpc8cbc5d2011-01-01 00:12:34 +00003342 found_transparent_pixel = MagickTrue;
3343 break;
3344 }
glennrp4f25bd02011-01-01 18:51:28 +00003345 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3346 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
cristy16ea1392012-03-21 20:38:41 +00003347 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3348 transparent_color.red &&
3349 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3350 transparent_color.green &&
3351 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3352 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00003353 {
glennrpa6a06632011-01-19 15:15:34 +00003354 if (logging != MagickFalse)
3355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3356 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00003357 found_transparent_pixel = MagickTrue;
3358 break;
3359 }
cristy16ea1392012-03-21 20:38:41 +00003360 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00003361 }
3362 }
3363
3364 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3365 {
3366 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3367 image->rows);
3368
3369 if (status == MagickFalse)
3370 break;
3371 }
3372 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3373 break;
3374 }
3375
3376 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3377 {
3378 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003379 if (status == MagickFalse)
3380 break;
3381 }
cristy3ed852e2009-09-05 21:47:34 +00003382 }
cristy3ed852e2009-09-05 21:47:34 +00003383 }
glennrp0fe50b42010-11-16 03:52:51 +00003384
cristy3ed852e2009-09-05 21:47:34 +00003385 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003386
cristy3ed852e2009-09-05 21:47:34 +00003387 for (pass=0; pass < num_passes; pass++)
3388 {
3389 Quantum
3390 *quantum_scanline;
3391
3392 register Quantum
3393 *r;
3394
3395 /*
3396 Convert grayscale image to PseudoClass pixel packets.
3397 */
glennrpc17d96f2011-06-27 01:20:11 +00003398 if (logging != MagickFalse)
3399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3400 " Converting grayscale pixels to pixel packets");
cristy16ea1392012-03-21 20:38:41 +00003401
cristy8a46d822012-08-28 23:32:39 +00003402 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristyb0a657e2012-08-29 00:45:37 +00003403 BlendPixelTrait : UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00003404
cristy3ed852e2009-09-05 21:47:34 +00003405 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
cristyb0a657e2012-08-29 00:45:37 +00003406 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3407 sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003408
cristy3ed852e2009-09-05 21:47:34 +00003409 if (quantum_scanline == (Quantum *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003410 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003411
cristybb503372010-05-27 20:51:26 +00003412 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003413 {
glennrp4f996392013-01-26 22:56:50 +00003414 Quantum
3415 alpha;
3416
cristy3ed852e2009-09-05 21:47:34 +00003417 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003418 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003419
cristy3ed852e2009-09-05 21:47:34 +00003420 else
3421 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003422
glennrpcf002022011-01-30 02:38:15 +00003423 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc5c31be2012-11-07 18:42:18 +00003424
3425 if (pass < num_passes-1)
3426 continue;
3427
glennrp4f996392013-01-26 22:56:50 +00003428 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003429
cristy16ea1392012-03-21 20:38:41 +00003430 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003431 break;
glennrp0fe50b42010-11-16 03:52:51 +00003432
glennrpcf002022011-01-30 02:38:15 +00003433 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003434 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003435
glennrpfaa852b2010-03-30 12:17:00 +00003436 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003437 {
cristy3ed852e2009-09-05 21:47:34 +00003438 case 8:
3439 {
glennrp4f996392013-01-26 22:56:50 +00003440
glennrpfaa852b2010-03-30 12:17:00 +00003441 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003442 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003443 {
glennrpa18d5bc2011-04-23 14:51:34 +00003444 *r++=*p++;
glennrp4f996392013-01-26 22:56:50 +00003445
3446 alpha=ScaleCharToQuantum((unsigned char)*p++);
3447
3448 SetPixelAlpha(image,alpha,q);
3449
3450 if (alpha != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003451 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003452
cristy16ea1392012-03-21 20:38:41 +00003453 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003454 }
glennrp0fe50b42010-11-16 03:52:51 +00003455
cristy3ed852e2009-09-05 21:47:34 +00003456 else
cristybb503372010-05-27 20:51:26 +00003457 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003458 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003459
cristy3ed852e2009-09-05 21:47:34 +00003460 break;
3461 }
glennrp47b9dd52010-11-24 18:12:06 +00003462
cristy3ed852e2009-09-05 21:47:34 +00003463 case 16:
3464 {
cristybb503372010-05-27 20:51:26 +00003465 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003466 {
glennrpc17d96f2011-06-27 01:20:11 +00003467#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
cristy87281ec2013-05-08 11:50:14 +00003468 unsigned short
glennrp58f77c72011-04-23 14:09:09 +00003469 quantum;
3470
3471 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003472 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003473
3474 else
glennrpc17d96f2011-06-27 01:20:11 +00003475 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003476
glennrp58f77c72011-04-23 14:09:09 +00003477 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003478 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003479 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003480
3481 if (ping_color_type == 4)
3482 {
glennrpc17d96f2011-06-27 01:20:11 +00003483 if (image->colors > 256)
3484 quantum=((*p++) << 8);
3485 else
3486 quantum=0;
3487
glennrp58f77c72011-04-23 14:09:09 +00003488 quantum|=(*p++);
glennrp4f996392013-01-26 22:56:50 +00003489
3490 alpha=ScaleShortToQuantum(quantum);
3491 SetPixelAlpha(image,alpha,q);
3492
3493 if (alpha != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003494 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003495
cristy16ea1392012-03-21 20:38:41 +00003496 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003497 }
glennrp58f77c72011-04-23 14:09:09 +00003498
3499#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3500 *r++=(*p++);
3501 p++; /* strip low byte */
3502
3503 if (ping_color_type == 4)
3504 {
cristy16ea1392012-03-21 20:38:41 +00003505 SetPixelAlpha(image,*p++,q);
glennrp4f996392013-01-26 22:56:50 +00003506
cristy16ea1392012-03-21 20:38:41 +00003507 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003508 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003509
glennrp9d0ea4d2011-04-22 18:35:57 +00003510 p++;
cristy16ea1392012-03-21 20:38:41 +00003511 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003512 }
cristy3ed852e2009-09-05 21:47:34 +00003513#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003514 }
glennrp47b9dd52010-11-24 18:12:06 +00003515
cristy3ed852e2009-09-05 21:47:34 +00003516 break;
3517 }
glennrp47b9dd52010-11-24 18:12:06 +00003518
cristy3ed852e2009-09-05 21:47:34 +00003519 default:
3520 break;
3521 }
glennrp3faa9a32011-04-23 14:00:25 +00003522
cristy3ed852e2009-09-05 21:47:34 +00003523 /*
3524 Transfer image scanline.
3525 */
3526 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003527
cristy16ea1392012-03-21 20:38:41 +00003528 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3529
3530 if (q == (Quantum *) NULL)
3531 break;
cristybb503372010-05-27 20:51:26 +00003532 for (x=0; x < (ssize_t) image->columns; x++)
cristy16ea1392012-03-21 20:38:41 +00003533 {
3534 SetPixelIndex(image,*r++,q);
3535 q+=GetPixelChannels(image);
3536 }
glennrp0fe50b42010-11-16 03:52:51 +00003537
cristy3ed852e2009-09-05 21:47:34 +00003538 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3539 break;
glennrp0fe50b42010-11-16 03:52:51 +00003540
cristy7a287bf2010-02-14 02:18:09 +00003541 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3542 {
cristycee97112010-05-28 00:44:52 +00003543 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003544 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003545
cristy7a287bf2010-02-14 02:18:09 +00003546 if (status == MagickFalse)
3547 break;
3548 }
cristy3ed852e2009-09-05 21:47:34 +00003549 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003550
cristy7a287bf2010-02-14 02:18:09 +00003551 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003552 {
3553 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003554
cristy3ed852e2009-09-05 21:47:34 +00003555 if (status == MagickFalse)
3556 break;
3557 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003558
cristy3ed852e2009-09-05 21:47:34 +00003559 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3560 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003561
cristyb0a657e2012-08-29 00:45:37 +00003562 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3563 UndefinedPixelTrait;
glennrpc8cbc5d2011-01-01 00:12:34 +00003564
3565 if (logging != MagickFalse)
3566 {
3567 if (found_transparent_pixel != MagickFalse)
3568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3569 " Found transparent pixel");
3570 else
glennrp5aa37f62011-01-02 03:07:57 +00003571 {
3572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3573 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003574
glennrp5aa37f62011-01-02 03:07:57 +00003575 ping_color_type&=0x03;
3576 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003577 }
3578 }
3579
cristy16ea1392012-03-21 20:38:41 +00003580 if (quantum_info != (QuantumInfo *) NULL)
3581 quantum_info=DestroyQuantumInfo(quantum_info);
3582
cristy5c6f7892010-05-05 22:53:29 +00003583 if (image->storage_class == PseudoClass)
3584 {
cristyb0a657e2012-08-29 00:45:37 +00003585 PixelTrait
3586 alpha_trait;
cristy5c6f7892010-05-05 22:53:29 +00003587
cristyb0a657e2012-08-29 00:45:37 +00003588 alpha_trait=image->alpha_trait;
cristy8a46d822012-08-28 23:32:39 +00003589 image->alpha_trait=UndefinedPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003590 (void) SyncImage(image,exception);
cristyb0a657e2012-08-29 00:45:37 +00003591 image->alpha_trait=alpha_trait;
cristy5c6f7892010-05-05 22:53:29 +00003592 }
glennrp47b9dd52010-11-24 18:12:06 +00003593
glennrp4eb39312011-03-30 21:34:55 +00003594 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003595
3596 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003597 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003598 {
3599 png_destroy_read_struct(&ping,&ping_info,&end_info);
cristy09973322013-06-30 21:06:30 +00003600 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00003601 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00003602 (void) SetImageBackgroundColor(image,exception);
glennrpedaa0382012-04-12 14:16:21 +00003603#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00003604 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003605#endif
3606 if (logging != MagickFalse)
3607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3608 " exit ReadOnePNGImage() early.");
3609 return(image);
3610 }
glennrp47b9dd52010-11-24 18:12:06 +00003611
glennrpfaa852b2010-03-30 12:17:00 +00003612 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003613 {
3614 ClassType
3615 storage_class;
3616
3617 /*
3618 Image has a transparent background.
3619 */
3620 storage_class=image->storage_class;
cristy8a46d822012-08-28 23:32:39 +00003621 image->alpha_trait=BlendPixelTrait;
glennrpc11cf6a2010-03-20 16:46:19 +00003622
glennrp3c218112010-11-27 15:31:26 +00003623/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003624
glennrp0fe50b42010-11-16 03:52:51 +00003625 if (storage_class == PseudoClass)
3626 {
3627 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003628 {
glennrp0fe50b42010-11-16 03:52:51 +00003629 for (x=0; x < ping_num_trans; x++)
3630 {
cristy8a46d822012-08-28 23:32:39 +00003631 image->colormap[x].alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003632 image->colormap[x].alpha =
3633 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003634 }
glennrpc11cf6a2010-03-20 16:46:19 +00003635 }
glennrp47b9dd52010-11-24 18:12:06 +00003636
glennrp0fe50b42010-11-16 03:52:51 +00003637 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3638 {
3639 for (x=0; x < (int) image->colors; x++)
3640 {
3641 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy16ea1392012-03-21 20:38:41 +00003642 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003643 {
cristy8a46d822012-08-28 23:32:39 +00003644 image->colormap[x].alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003645 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003646 }
3647 }
3648 }
cristy16ea1392012-03-21 20:38:41 +00003649 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003650 }
glennrp47b9dd52010-11-24 18:12:06 +00003651
glennrpa6a06632011-01-19 15:15:34 +00003652#if 1 /* Should have already been done above, but glennrp problem P10
3653 * needs this.
3654 */
glennrp0fe50b42010-11-16 03:52:51 +00003655 else
3656 {
3657 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003658 {
glennrp0fe50b42010-11-16 03:52:51 +00003659 image->storage_class=storage_class;
3660 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3661
cristy16ea1392012-03-21 20:38:41 +00003662 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003663 break;
3664
glennrp0fe50b42010-11-16 03:52:51 +00003665
glennrpa6a06632011-01-19 15:15:34 +00003666 /* Caution: on a Q8 build, this does not distinguish between
3667 * 16-bit colors that differ only in the low byte
3668 */
glennrp0fe50b42010-11-16 03:52:51 +00003669 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3670 {
cristy16ea1392012-03-21 20:38:41 +00003671 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3672 transparent_color.red &&
3673 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3674 transparent_color.green &&
3675 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3676 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003677 {
cristy16ea1392012-03-21 20:38:41 +00003678 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003679 }
glennrp0fe50b42010-11-16 03:52:51 +00003680
glennrp67b9c1a2011-04-22 18:47:36 +00003681#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003682 else
glennrp4f25bd02011-01-01 18:51:28 +00003683 {
cristy16ea1392012-03-21 20:38:41 +00003684 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003685 }
glennrpa6a06632011-01-19 15:15:34 +00003686#endif
glennrp0fe50b42010-11-16 03:52:51 +00003687
cristy16ea1392012-03-21 20:38:41 +00003688 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003689 }
3690
3691 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3692 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003693 }
glennrp0fe50b42010-11-16 03:52:51 +00003694 }
glennrpa6a06632011-01-19 15:15:34 +00003695#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003696
cristy3ed852e2009-09-05 21:47:34 +00003697 image->storage_class=DirectClass;
3698 }
glennrp3c218112010-11-27 15:31:26 +00003699
cristyeb3b22a2011-03-31 20:16:11 +00003700 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003701 {
3702 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003703 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3704 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003705 else
glennrpa0ed0092011-04-18 16:36:29 +00003706 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3707 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003708
glennrp4eb39312011-03-30 21:34:55 +00003709 if (status != MagickFalse)
3710 for (i=0; i < (ssize_t) num_text; i++)
3711 {
3712 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003713
glennrp4eb39312011-03-30 21:34:55 +00003714 if (logging != MagickFalse)
3715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3716 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003717
glennrp4eb39312011-03-30 21:34:55 +00003718 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003719 {
glennrpedaa0382012-04-12 14:16:21 +00003720 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3721 (int) i,exception);
glennrp4eb39312011-03-30 21:34:55 +00003722 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003723 }
glennrp0fe50b42010-11-16 03:52:51 +00003724
glennrp4eb39312011-03-30 21:34:55 +00003725 else
3726 {
3727 char
3728 *value;
3729
3730 length=text[i].text_length;
3731 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3732 sizeof(*value));
3733 if (value == (char *) NULL)
3734 {
glennrpedaa0382012-04-12 14:16:21 +00003735 png_error(ping,"Memory allocation failed");
glennrp4eb39312011-03-30 21:34:55 +00003736 break;
3737 }
3738 *value='\0';
3739 (void) ConcatenateMagickString(value,text[i].text,length+2);
3740
3741 /* Don't save "density" or "units" property if we have a pHYs
3742 * chunk
3743 */
3744 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3745 (LocaleCompare(text[i].key,"density") != 0 &&
3746 LocaleCompare(text[i].key,"units") != 0))
cristy16ea1392012-03-21 20:38:41 +00003747 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003748
3749 if (logging != MagickFalse)
3750 {
3751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3752 " length: %lu",(unsigned long) length);
3753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3754 " Keyword: %s",text[i].key);
3755 }
3756
3757 value=DestroyString(value);
3758 }
3759 }
3760 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003761 }
glennrp3c218112010-11-27 15:31:26 +00003762
cristy3ed852e2009-09-05 21:47:34 +00003763#ifdef MNG_OBJECT_BUFFERS
3764 /*
3765 Store the object if necessary.
3766 */
3767 if (object_id && !mng_info->frozen[object_id])
3768 {
3769 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3770 {
3771 /*
3772 create a new object buffer.
3773 */
3774 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003775 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003776
cristy3ed852e2009-09-05 21:47:34 +00003777 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3778 {
3779 mng_info->ob[object_id]->image=(Image *) NULL;
3780 mng_info->ob[object_id]->reference_count=1;
3781 }
3782 }
glennrp47b9dd52010-11-24 18:12:06 +00003783
cristy3ed852e2009-09-05 21:47:34 +00003784 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3785 mng_info->ob[object_id]->frozen)
3786 {
3787 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003788 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003789
cristy3ed852e2009-09-05 21:47:34 +00003790 if (mng_info->ob[object_id]->frozen)
glennrpedaa0382012-04-12 14:16:21 +00003791 png_error(ping,"Cannot overwrite frozen MNG object buffer");
cristy3ed852e2009-09-05 21:47:34 +00003792 }
glennrp0fe50b42010-11-16 03:52:51 +00003793
cristy3ed852e2009-09-05 21:47:34 +00003794 else
3795 {
cristy3ed852e2009-09-05 21:47:34 +00003796
3797 if (mng_info->ob[object_id]->image != (Image *) NULL)
3798 mng_info->ob[object_id]->image=DestroyImage
3799 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003800
cristy3ed852e2009-09-05 21:47:34 +00003801 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristy16ea1392012-03-21 20:38:41 +00003802 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003803
cristy3ed852e2009-09-05 21:47:34 +00003804 if (mng_info->ob[object_id]->image != (Image *) NULL)
3805 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003806
cristy3ed852e2009-09-05 21:47:34 +00003807 else
glennrpedaa0382012-04-12 14:16:21 +00003808 png_error(ping, "Cloning image for object buffer failed");
glennrp0fe50b42010-11-16 03:52:51 +00003809
glennrpfaa852b2010-03-30 12:17:00 +00003810 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003811 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003812
glennrpfaa852b2010-03-30 12:17:00 +00003813 mng_info->ob[object_id]->width=ping_width;
3814 mng_info->ob[object_id]->height=ping_height;
3815 mng_info->ob[object_id]->color_type=ping_color_type;
3816 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3817 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3818 mng_info->ob[object_id]->compression_method=
3819 ping_compression_method;
3820 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003821
glennrpfaa852b2010-03-30 12:17:00 +00003822 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003823 {
cristy3ed852e2009-09-05 21:47:34 +00003824 png_colorp
3825 plte;
3826
3827 /*
3828 Copy the PLTE to the object buffer.
3829 */
3830 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3831 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003832
cristy3ed852e2009-09-05 21:47:34 +00003833 for (i=0; i < number_colors; i++)
3834 {
3835 mng_info->ob[object_id]->plte[i]=plte[i];
3836 }
3837 }
glennrp47b9dd52010-11-24 18:12:06 +00003838
cristy3ed852e2009-09-05 21:47:34 +00003839 else
3840 mng_info->ob[object_id]->plte_length=0;
3841 }
3842 }
3843#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003844
cristy8a46d822012-08-28 23:32:39 +00003845 /* Set image->alpha_trait to MagickTrue if the input colortype supports
glennrp0a55b4c2011-03-23 12:38:38 +00003846 * alpha or if a valid tRNS chunk is present, no matter whether there
3847 * is actual transparency present.
3848 */
cristy8a46d822012-08-28 23:32:39 +00003849 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
glennrp0a55b4c2011-03-23 12:38:38 +00003850 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3851 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
cristyb0a657e2012-08-29 00:45:37 +00003852 BlendPixelTrait : UndefinedPixelTrait;
glennrp0a55b4c2011-03-23 12:38:38 +00003853
glennrp224ad8d2013-02-01 17:04:04 +00003854#if 0 /* I'm not sure what's wrong here but it does not work. */
glennrp5830fbc2013-01-27 06:11:45 +00003855 if (image->alpha_trait == BlendPixelTrait)
3856 {
3857 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3858 (void) SetImageType(image,GrayscaleMatteType,exception);
3859
3860 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3861 (void) SetImageType(image,PaletteMatteType,exception);
3862
3863 else
3864 (void) SetImageType(image,TrueColorMatteType,exception);
3865 }
3866
3867 else
3868 {
3869 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3870 (void) SetImageType(image,GrayscaleType,exception);
3871
3872 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3873 (void) SetImageType(image,PaletteType,exception);
3874
3875 else
3876 (void) SetImageType(image,TrueColorType,exception);
3877 }
glennrp224ad8d2013-02-01 17:04:04 +00003878#endif
glennrp5830fbc2013-01-27 06:11:45 +00003879
glennrpcb395ac2011-03-30 19:50:23 +00003880 /* Set more properties for identify to retrieve */
3881 {
3882 char
3883 msg[MaxTextExtent];
3884
glennrp4eb39312011-03-30 21:34:55 +00003885 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003886 {
3887 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003888 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003889 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrp3398b5b2013-05-03 23:10:31 +00003890 (void) SetImageProperty(image,"png:text",msg,
cristy16ea1392012-03-21 20:38:41 +00003891 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003892 }
3893
3894 if (num_raw_profiles != 0)
3895 {
cristy3b6fd2e2011-05-20 12:53:50 +00003896 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003897 "%d were found", num_raw_profiles);
cristy16ea1392012-03-21 20:38:41 +00003898 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3899 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003900 }
3901
glennrp98b83d42012-07-23 02:50:31 +00003902 if (ping_found_cHRM != MagickFalse)
glennrp59612252011-03-30 21:45:21 +00003903 {
cristy3b6fd2e2011-05-20 12:53:50 +00003904 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003905 "chunk was found (see Chromaticity, above)");
glennrp3398b5b2013-05-03 23:10:31 +00003906 (void) SetImageProperty(image,"png:cHRM",msg,
cristy16ea1392012-03-21 20:38:41 +00003907 exception);
glennrp59612252011-03-30 21:45:21 +00003908 }
glennrpcb395ac2011-03-30 19:50:23 +00003909
3910 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003911 {
cristy3b6fd2e2011-05-20 12:53:50 +00003912 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003913 "chunk was found (see Background color, above)");
glennrp3398b5b2013-05-03 23:10:31 +00003914 (void) SetImageProperty(image,"png:bKGD",msg,
cristy16ea1392012-03-21 20:38:41 +00003915 exception);
glennrp59612252011-03-30 21:45:21 +00003916 }
3917
cristy3b6fd2e2011-05-20 12:53:50 +00003918 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003919 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003920
glennrp98b83d42012-07-23 02:50:31 +00003921#if defined(PNG_iCCP_SUPPORTED)
3922 if (ping_found_iCCP != MagickFalse)
glennrp3398b5b2013-05-03 23:10:31 +00003923 (void) SetImageProperty(image,"png:iCCP",msg,
cristy16ea1392012-03-21 20:38:41 +00003924 exception);
glennrp98b83d42012-07-23 02:50:31 +00003925#endif
glennrpcb395ac2011-03-30 19:50:23 +00003926
glennrpcb395ac2011-03-30 19:50:23 +00003927 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrp3398b5b2013-05-03 23:10:31 +00003928 (void) SetImageProperty(image,"png:tRNS",msg,
cristy16ea1392012-03-21 20:38:41 +00003929 exception);
glennrp4eb39312011-03-30 21:34:55 +00003930
3931#if defined(PNG_sRGB_SUPPORTED)
glennrp98b83d42012-07-23 02:50:31 +00003932 if (ping_found_sRGB != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003933 {
cristy3b6fd2e2011-05-20 12:53:50 +00003934 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp98b83d42012-07-23 02:50:31 +00003935 "intent=%d (%s)",
3936 (int) intent,
3937 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
glennrp3398b5b2013-05-03 23:10:31 +00003938 (void) SetImageProperty(image,"png:sRGB",msg,
glennrp98b83d42012-07-23 02:50:31 +00003939 exception);
glennrp4eb39312011-03-30 21:34:55 +00003940 }
3941#endif
3942
glennrp98b83d42012-07-23 02:50:31 +00003943 if (ping_found_gAMA != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003944 {
cristy3b6fd2e2011-05-20 12:53:50 +00003945 (void) FormatLocaleString(msg,MaxTextExtent,
cristy16ea1392012-03-21 20:38:41 +00003946 "gamma=%.8g (See Gamma, above)",
3947 file_gamma);
glennrp3398b5b2013-05-03 23:10:31 +00003948 (void) SetImageProperty(image,"png:gAMA",msg,
cristy16ea1392012-03-21 20:38:41 +00003949 exception);
glennrp4eb39312011-03-30 21:34:55 +00003950 }
3951
3952#if defined(PNG_pHYs_SUPPORTED)
3953 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3954 {
cristy3b6fd2e2011-05-20 12:53:50 +00003955 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003956 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003957 (double) x_resolution,(double) y_resolution, unit_type);
glennrp3398b5b2013-05-03 23:10:31 +00003958 (void) SetImageProperty(image,"png:pHYs",msg,
cristy16ea1392012-03-21 20:38:41 +00003959 exception);
glennrp4eb39312011-03-30 21:34:55 +00003960 }
3961#endif
3962
3963#if defined(PNG_oFFs_SUPPORTED)
3964 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3965 {
cristy3b6fd2e2011-05-20 12:53:50 +00003966 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003967 (double) image->page.x,(double) image->page.y);
glennrp3398b5b2013-05-03 23:10:31 +00003968 (void) SetImageProperty(image,"png:oFFs",msg,
cristy16ea1392012-03-21 20:38:41 +00003969 exception);
glennrp4eb39312011-03-30 21:34:55 +00003970 }
3971#endif
3972
glennrp07523c72011-03-31 18:12:10 +00003973 if ((image->page.width != 0 && image->page.width != image->columns) ||
3974 (image->page.height != 0 && image->page.height != image->rows))
3975 {
cristy3b6fd2e2011-05-20 12:53:50 +00003976 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003977 "width=%.20g, height=%.20g",
3978 (double) image->page.width,(double) image->page.height);
glennrp3398b5b2013-05-03 23:10:31 +00003979 (void) SetImageProperty(image,"png:vpAg",msg,
cristy16ea1392012-03-21 20:38:41 +00003980 exception);
glennrp07523c72011-03-31 18:12:10 +00003981 }
glennrpcb395ac2011-03-30 19:50:23 +00003982 }
3983
cristy3ed852e2009-09-05 21:47:34 +00003984 /*
3985 Relinquish resources.
3986 */
3987 png_destroy_read_struct(&ping,&ping_info,&end_info);
3988
cristy09973322013-06-30 21:06:30 +00003989 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00003990
3991 if (logging != MagickFalse)
3992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3993 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003994
glennrpedaa0382012-04-12 14:16:21 +00003995#ifdef PNG_SETJMP_NOT_THREAD_SAFE
3996 UnlockSemaphoreInfo(ping_semaphore);
3997#endif
3998
3999 /* } for navigation to beginning of SETJMP-protected block, revert to
4000 * Throwing an Exception when an error occurs.
4001 */
4002
cristy3ed852e2009-09-05 21:47:34 +00004003 return(image);
4004
4005/* end of reading one PNG image */
4006}
4007
4008static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4009{
4010 Image
4011 *image,
4012 *previous;
4013
4014 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004015 have_mng_structure,
4016 logging,
cristy3ed852e2009-09-05 21:47:34 +00004017 status;
4018
4019 MngInfo
4020 *mng_info;
4021
4022 char
4023 magic_number[MaxTextExtent];
4024
cristy3ed852e2009-09-05 21:47:34 +00004025 ssize_t
4026 count;
4027
4028 /*
4029 Open image file.
4030 */
4031 assert(image_info != (const ImageInfo *) NULL);
4032 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00004033
cristy3ed852e2009-09-05 21:47:34 +00004034 if (image_info->debug != MagickFalse)
4035 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4036 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00004037
cristy3ed852e2009-09-05 21:47:34 +00004038 assert(exception != (ExceptionInfo *) NULL);
4039 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004040 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004041 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004042 mng_info=(MngInfo *) NULL;
4043 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004044
cristy3ed852e2009-09-05 21:47:34 +00004045 if (status == MagickFalse)
4046 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00004047
cristy3ed852e2009-09-05 21:47:34 +00004048 /*
4049 Verify PNG signature.
4050 */
4051 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004052
glennrpdde35db2011-02-21 12:06:32 +00004053 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004054 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004055
cristy3ed852e2009-09-05 21:47:34 +00004056 /*
4057 Allocate a MngInfo structure.
4058 */
4059 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004060 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004061
cristy3ed852e2009-09-05 21:47:34 +00004062 if (mng_info == (MngInfo *) NULL)
4063 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004064
cristy3ed852e2009-09-05 21:47:34 +00004065 /*
4066 Initialize members of the MngInfo structure.
4067 */
4068 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4069 mng_info->image=image;
4070 have_mng_structure=MagickTrue;
4071
4072 previous=image;
4073 image=ReadOnePNGImage(mng_info,image_info,exception);
4074 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00004075
cristy3ed852e2009-09-05 21:47:34 +00004076 if (image == (Image *) NULL)
4077 {
4078 if (previous != (Image *) NULL)
4079 {
4080 if (previous->signature != MagickSignature)
4081 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004082
cristy3ed852e2009-09-05 21:47:34 +00004083 (void) CloseBlob(previous);
4084 (void) DestroyImageList(previous);
4085 }
glennrp0fe50b42010-11-16 03:52:51 +00004086
cristy3ed852e2009-09-05 21:47:34 +00004087 if (logging != MagickFalse)
4088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4089 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004090
cristy3ed852e2009-09-05 21:47:34 +00004091 return((Image *) NULL);
4092 }
glennrp47b9dd52010-11-24 18:12:06 +00004093
cristy3ed852e2009-09-05 21:47:34 +00004094 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004095
cristy3ed852e2009-09-05 21:47:34 +00004096 if ((image->columns == 0) || (image->rows == 0))
4097 {
4098 if (logging != MagickFalse)
4099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4100 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00004101
cristy3ed852e2009-09-05 21:47:34 +00004102 ThrowReaderException(CorruptImageError,"CorruptImage");
4103 }
glennrp47b9dd52010-11-24 18:12:06 +00004104
cristy72715f52012-06-26 17:55:16 +00004105 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
glennrp3d627862013-02-26 00:19:34 +00004106 ((image->gamma < .45) || (image->gamma > .46)) &&
4107 !(image->chromaticity.red_primary.x>0.6399f &&
4108 image->chromaticity.red_primary.x<0.6401f &&
4109 image->chromaticity.red_primary.y>0.3299f &&
4110 image->chromaticity.red_primary.y<0.3301f &&
4111 image->chromaticity.green_primary.x>0.2999f &&
4112 image->chromaticity.green_primary.x<0.3001f &&
4113 image->chromaticity.green_primary.y>0.5999f &&
4114 image->chromaticity.green_primary.y<0.6001f &&
4115 image->chromaticity.blue_primary.x>0.1499f &&
4116 image->chromaticity.blue_primary.x<0.1501f &&
4117 image->chromaticity.blue_primary.y>0.0599f &&
4118 image->chromaticity.blue_primary.y<0.0601f &&
4119 image->chromaticity.white_point.x>0.3126f &&
4120 image->chromaticity.white_point.x<0.3128f &&
4121 image->chromaticity.white_point.y>0.3289f &&
4122 image->chromaticity.white_point.y<0.3291f))
cristy72715f52012-06-26 17:55:16 +00004123 SetImageColorspace(image,RGBColorspace,exception);
4124
cristy3ed852e2009-09-05 21:47:34 +00004125 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00004126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4127 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4128 (double) image->page.width,(double) image->page.height,
4129 (double) image->page.x,(double) image->page.y);
4130
4131 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004133
cristy3ed852e2009-09-05 21:47:34 +00004134 return(image);
4135}
4136
4137
4138
4139#if defined(JNG_SUPPORTED)
4140/*
4141%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4142% %
4143% %
4144% %
4145% R e a d O n e J N G I m a g e %
4146% %
4147% %
4148% %
4149%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4150%
4151% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4152% (minus the 8-byte signature) and returns it. It allocates the memory
4153% necessary for the new Image structure and returns a pointer to the new
4154% image.
4155%
4156% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4157%
4158% The format of the ReadOneJNGImage method is:
4159%
4160% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4161% ExceptionInfo *exception)
4162%
4163% A description of each parameter follows:
4164%
4165% o mng_info: Specifies a pointer to a MngInfo structure.
4166%
4167% o image_info: the image info.
4168%
4169% o exception: return any errors or warnings in this structure.
4170%
4171*/
4172static Image *ReadOneJNGImage(MngInfo *mng_info,
4173 const ImageInfo *image_info, ExceptionInfo *exception)
4174{
4175 Image
4176 *alpha_image,
4177 *color_image,
4178 *image,
4179 *jng_image;
4180
4181 ImageInfo
4182 *alpha_image_info,
4183 *color_image_info;
4184
cristy4383ec82011-01-05 15:42:32 +00004185 MagickBooleanType
4186 logging;
4187
cristybb503372010-05-27 20:51:26 +00004188 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004189 y;
4190
4191 MagickBooleanType
4192 status;
4193
4194 png_uint_32
4195 jng_height,
4196 jng_width;
4197
4198 png_byte
4199 jng_color_type,
4200 jng_image_sample_depth,
4201 jng_image_compression_method,
4202 jng_image_interlace_method,
4203 jng_alpha_sample_depth,
4204 jng_alpha_compression_method,
4205 jng_alpha_filter_method,
4206 jng_alpha_interlace_method;
4207
cristy16ea1392012-03-21 20:38:41 +00004208 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00004209 *s;
4210
cristybb503372010-05-27 20:51:26 +00004211 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004212 i,
4213 x;
4214
cristy16ea1392012-03-21 20:38:41 +00004215 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004216 *q;
4217
4218 register unsigned char
4219 *p;
4220
4221 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00004222 read_JSEP,
4223 reading_idat,
4224 skip_to_iend;
4225
cristybb503372010-05-27 20:51:26 +00004226 size_t
cristy3ed852e2009-09-05 21:47:34 +00004227 length;
4228
4229 jng_alpha_compression_method=0;
4230 jng_alpha_sample_depth=8;
4231 jng_color_type=0;
4232 jng_height=0;
4233 jng_width=0;
4234 alpha_image=(Image *) NULL;
4235 color_image=(Image *) NULL;
4236 alpha_image_info=(ImageInfo *) NULL;
4237 color_image_info=(ImageInfo *) NULL;
4238
4239 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00004240 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004241
4242 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00004243
cristy16ea1392012-03-21 20:38:41 +00004244 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004245 {
4246 /*
4247 Allocate next image structure.
4248 */
4249 if (logging != MagickFalse)
4250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4251 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004252
cristy16ea1392012-03-21 20:38:41 +00004253 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004254
cristy3ed852e2009-09-05 21:47:34 +00004255 if (GetNextImageInList(image) == (Image *) NULL)
4256 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004257
cristy3ed852e2009-09-05 21:47:34 +00004258 image=SyncNextImageInList(image);
4259 }
4260 mng_info->image=image;
4261
4262 /*
4263 Signature bytes have already been read.
4264 */
4265
4266 read_JSEP=MagickFalse;
4267 reading_idat=MagickFalse;
4268 skip_to_iend=MagickFalse;
4269 for (;;)
4270 {
4271 char
4272 type[MaxTextExtent];
4273
4274 unsigned char
4275 *chunk;
4276
4277 unsigned int
4278 count;
4279
4280 /*
4281 Read a new JNG chunk.
4282 */
4283 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4284 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004285
cristy3ed852e2009-09-05 21:47:34 +00004286 if (status == MagickFalse)
4287 break;
glennrp0fe50b42010-11-16 03:52:51 +00004288
cristy3ed852e2009-09-05 21:47:34 +00004289 type[0]='\0';
4290 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4291 length=ReadBlobMSBLong(image);
4292 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4293
4294 if (logging != MagickFalse)
4295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004296 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4297 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004298
4299 if (length > PNG_UINT_31_MAX || count == 0)
4300 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004301
cristy3ed852e2009-09-05 21:47:34 +00004302 p=NULL;
4303 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00004304
cristy3ed852e2009-09-05 21:47:34 +00004305 if (length)
4306 {
4307 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00004308
cristy3ed852e2009-09-05 21:47:34 +00004309 if (chunk == (unsigned char *) NULL)
4310 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004311
cristybb503372010-05-27 20:51:26 +00004312 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004313 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00004314
cristy3ed852e2009-09-05 21:47:34 +00004315 p=chunk;
4316 }
glennrp47b9dd52010-11-24 18:12:06 +00004317
cristy3ed852e2009-09-05 21:47:34 +00004318 (void) ReadBlobMSBLong(image); /* read crc word */
4319
4320 if (skip_to_iend)
4321 {
4322 if (length)
4323 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004324
cristy3ed852e2009-09-05 21:47:34 +00004325 continue;
4326 }
4327
4328 if (memcmp(type,mng_JHDR,4) == 0)
4329 {
4330 if (length == 16)
4331 {
cristybb503372010-05-27 20:51:26 +00004332 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004333 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00004334 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004335 (p[6] << 8) | p[7]);
4336 jng_color_type=p[8];
4337 jng_image_sample_depth=p[9];
4338 jng_image_compression_method=p[10];
4339 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00004340
cristy3ed852e2009-09-05 21:47:34 +00004341 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4342 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00004343
cristy3ed852e2009-09-05 21:47:34 +00004344 jng_alpha_sample_depth=p[12];
4345 jng_alpha_compression_method=p[13];
4346 jng_alpha_filter_method=p[14];
4347 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00004348
cristy3ed852e2009-09-05 21:47:34 +00004349 if (logging != MagickFalse)
4350 {
4351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004352 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00004353
cristy3ed852e2009-09-05 21:47:34 +00004354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004355 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00004356
cristy3ed852e2009-09-05 21:47:34 +00004357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4358 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00004359
cristy3ed852e2009-09-05 21:47:34 +00004360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4361 " jng_image_sample_depth: %3d",
4362 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004363
cristy3ed852e2009-09-05 21:47:34 +00004364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4365 " jng_image_compression_method:%3d",
4366 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004367
cristy3ed852e2009-09-05 21:47:34 +00004368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4369 " jng_image_interlace_method: %3d",
4370 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00004371
cristy3ed852e2009-09-05 21:47:34 +00004372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4373 " jng_alpha_sample_depth: %3d",
4374 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004375
cristy3ed852e2009-09-05 21:47:34 +00004376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4377 " jng_alpha_compression_method:%3d",
4378 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004379
cristy3ed852e2009-09-05 21:47:34 +00004380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4381 " jng_alpha_filter_method: %3d",
4382 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004383
cristy3ed852e2009-09-05 21:47:34 +00004384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4385 " jng_alpha_interlace_method: %3d",
4386 jng_alpha_interlace_method);
4387 }
4388 }
glennrp47b9dd52010-11-24 18:12:06 +00004389
cristy3ed852e2009-09-05 21:47:34 +00004390 if (length)
4391 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004392
cristy3ed852e2009-09-05 21:47:34 +00004393 continue;
4394 }
4395
4396
4397 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4398 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4399 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4400 {
4401 /*
4402 o create color_image
4403 o open color_blob, attached to color_image
4404 o if (color type has alpha)
4405 open alpha_blob, attached to alpha_image
4406 */
4407
cristy73bd4a52010-10-05 11:24:23 +00004408 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004409
cristy3ed852e2009-09-05 21:47:34 +00004410 if (color_image_info == (ImageInfo *) NULL)
4411 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004412
cristy3ed852e2009-09-05 21:47:34 +00004413 GetImageInfo(color_image_info);
cristy16ea1392012-03-21 20:38:41 +00004414 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004415
cristy3ed852e2009-09-05 21:47:34 +00004416 if (color_image == (Image *) NULL)
4417 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4418
4419 if (logging != MagickFalse)
4420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4421 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004422
cristy3ed852e2009-09-05 21:47:34 +00004423 (void) AcquireUniqueFilename(color_image->filename);
4424 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4425 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004426
cristy3ed852e2009-09-05 21:47:34 +00004427 if (status == MagickFalse)
4428 return((Image *) NULL);
4429
4430 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4431 {
4432 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004433 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004434
cristy3ed852e2009-09-05 21:47:34 +00004435 if (alpha_image_info == (ImageInfo *) NULL)
4436 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004437
cristy3ed852e2009-09-05 21:47:34 +00004438 GetImageInfo(alpha_image_info);
cristy16ea1392012-03-21 20:38:41 +00004439 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004440
cristy3ed852e2009-09-05 21:47:34 +00004441 if (alpha_image == (Image *) NULL)
4442 {
4443 alpha_image=DestroyImage(alpha_image);
4444 ThrowReaderException(ResourceLimitError,
4445 "MemoryAllocationFailed");
4446 }
glennrp0fe50b42010-11-16 03:52:51 +00004447
cristy3ed852e2009-09-05 21:47:34 +00004448 if (logging != MagickFalse)
4449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4450 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004451
cristy3ed852e2009-09-05 21:47:34 +00004452 (void) AcquireUniqueFilename(alpha_image->filename);
4453 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4454 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004455
cristy3ed852e2009-09-05 21:47:34 +00004456 if (status == MagickFalse)
4457 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004458
cristy3ed852e2009-09-05 21:47:34 +00004459 if (jng_alpha_compression_method == 0)
4460 {
4461 unsigned char
4462 data[18];
4463
4464 if (logging != MagickFalse)
4465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4466 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004467
cristy3ed852e2009-09-05 21:47:34 +00004468 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4469 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004470
cristy3ed852e2009-09-05 21:47:34 +00004471 (void) WriteBlobMSBULong(alpha_image,13L);
4472 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004473 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004474 PNGLong(data+4,jng_width);
4475 PNGLong(data+8,jng_height);
4476 data[12]=jng_alpha_sample_depth;
4477 data[13]=0; /* color_type gray */
4478 data[14]=0; /* compression method 0 */
4479 data[15]=0; /* filter_method 0 */
4480 data[16]=0; /* interlace_method 0 */
4481 (void) WriteBlob(alpha_image,17,data);
4482 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4483 }
4484 }
4485 reading_idat=MagickTrue;
4486 }
4487
4488 if (memcmp(type,mng_JDAT,4) == 0)
4489 {
glennrp47b9dd52010-11-24 18:12:06 +00004490 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004491
4492 if (logging != MagickFalse)
4493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4494 " Copying JDAT chunk data to color_blob.");
4495
4496 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004497
cristy3ed852e2009-09-05 21:47:34 +00004498 if (length)
4499 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004500
cristy3ed852e2009-09-05 21:47:34 +00004501 continue;
4502 }
4503
4504 if (memcmp(type,mng_IDAT,4) == 0)
4505 {
4506 png_byte
4507 data[5];
4508
glennrp47b9dd52010-11-24 18:12:06 +00004509 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004510
4511 if (image_info->ping == MagickFalse)
4512 {
4513 if (logging != MagickFalse)
4514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4515 " Copying IDAT chunk data to alpha_blob.");
4516
cristybb503372010-05-27 20:51:26 +00004517 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004518 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004519 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004520 (void) WriteBlob(alpha_image,4,data);
4521 (void) WriteBlob(alpha_image,length,chunk);
4522 (void) WriteBlobMSBULong(alpha_image,
4523 crc32(crc32(0,data,4),chunk,(uInt) length));
4524 }
glennrp0fe50b42010-11-16 03:52:51 +00004525
cristy3ed852e2009-09-05 21:47:34 +00004526 if (length)
4527 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004528
cristy3ed852e2009-09-05 21:47:34 +00004529 continue;
4530 }
4531
4532 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4533 {
glennrp47b9dd52010-11-24 18:12:06 +00004534 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004535
4536 if (image_info->ping == MagickFalse)
4537 {
4538 if (logging != MagickFalse)
4539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4540 " Copying JDAA chunk data to alpha_blob.");
4541
4542 (void) WriteBlob(alpha_image,length,chunk);
4543 }
glennrp0fe50b42010-11-16 03:52:51 +00004544
cristy3ed852e2009-09-05 21:47:34 +00004545 if (length)
4546 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004547
cristy3ed852e2009-09-05 21:47:34 +00004548 continue;
4549 }
4550
4551 if (memcmp(type,mng_JSEP,4) == 0)
4552 {
4553 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004554
cristy3ed852e2009-09-05 21:47:34 +00004555 if (length)
4556 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004557
cristy3ed852e2009-09-05 21:47:34 +00004558 continue;
4559 }
4560
4561 if (memcmp(type,mng_bKGD,4) == 0)
4562 {
4563 if (length == 2)
4564 {
4565 image->background_color.red=ScaleCharToQuantum(p[1]);
4566 image->background_color.green=image->background_color.red;
4567 image->background_color.blue=image->background_color.red;
4568 }
glennrp0fe50b42010-11-16 03:52:51 +00004569
cristy3ed852e2009-09-05 21:47:34 +00004570 if (length == 6)
4571 {
4572 image->background_color.red=ScaleCharToQuantum(p[1]);
4573 image->background_color.green=ScaleCharToQuantum(p[3]);
4574 image->background_color.blue=ScaleCharToQuantum(p[5]);
4575 }
glennrp0fe50b42010-11-16 03:52:51 +00004576
cristy3ed852e2009-09-05 21:47:34 +00004577 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4578 continue;
4579 }
4580
4581 if (memcmp(type,mng_gAMA,4) == 0)
4582 {
4583 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004584 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004585
cristy3ed852e2009-09-05 21:47:34 +00004586 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4587 continue;
4588 }
4589
4590 if (memcmp(type,mng_cHRM,4) == 0)
4591 {
4592 if (length == 32)
4593 {
cristy8182b072010-05-30 20:10:53 +00004594 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4595 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4596 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4597 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4598 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4599 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4600 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4601 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004602 }
glennrp47b9dd52010-11-24 18:12:06 +00004603
cristy3ed852e2009-09-05 21:47:34 +00004604 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4605 continue;
4606 }
4607
4608 if (memcmp(type,mng_sRGB,4) == 0)
4609 {
4610 if (length == 1)
4611 {
glennrpe610a072010-08-05 17:08:46 +00004612 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004613 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristyda7803d2012-05-03 01:22:45 +00004614 image->gamma=1.000f/2.200f;
cristy3ed852e2009-09-05 21:47:34 +00004615 image->chromaticity.red_primary.x=0.6400f;
4616 image->chromaticity.red_primary.y=0.3300f;
4617 image->chromaticity.green_primary.x=0.3000f;
4618 image->chromaticity.green_primary.y=0.6000f;
4619 image->chromaticity.blue_primary.x=0.1500f;
4620 image->chromaticity.blue_primary.y=0.0600f;
4621 image->chromaticity.white_point.x=0.3127f;
4622 image->chromaticity.white_point.y=0.3290f;
4623 }
glennrp47b9dd52010-11-24 18:12:06 +00004624
cristy3ed852e2009-09-05 21:47:34 +00004625 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4626 continue;
4627 }
4628
4629 if (memcmp(type,mng_oFFs,4) == 0)
4630 {
4631 if (length > 8)
4632 {
glennrp5eae7602011-02-22 15:21:32 +00004633 image->page.x=(ssize_t) mng_get_long(p);
4634 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004635
cristy3ed852e2009-09-05 21:47:34 +00004636 if ((int) p[8] != 0)
4637 {
4638 image->page.x/=10000;
4639 image->page.y/=10000;
4640 }
4641 }
glennrp47b9dd52010-11-24 18:12:06 +00004642
cristy3ed852e2009-09-05 21:47:34 +00004643 if (length)
4644 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004645
cristy3ed852e2009-09-05 21:47:34 +00004646 continue;
4647 }
4648
4649 if (memcmp(type,mng_pHYs,4) == 0)
4650 {
4651 if (length > 8)
4652 {
cristy16ea1392012-03-21 20:38:41 +00004653 image->resolution.x=(double) mng_get_long(p);
4654 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004655 if ((int) p[8] == PNG_RESOLUTION_METER)
4656 {
4657 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00004658 image->resolution.x=image->resolution.x/100.0f;
4659 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004660 }
4661 }
glennrp0fe50b42010-11-16 03:52:51 +00004662
cristy3ed852e2009-09-05 21:47:34 +00004663 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4664 continue;
4665 }
4666
4667#if 0
4668 if (memcmp(type,mng_iCCP,4) == 0)
4669 {
glennrpfd05d622011-02-25 04:10:33 +00004670 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004671 if (length)
4672 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004673
cristy3ed852e2009-09-05 21:47:34 +00004674 continue;
4675 }
4676#endif
4677
4678 if (length)
4679 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4680
4681 if (memcmp(type,mng_IEND,4))
4682 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004683
cristy3ed852e2009-09-05 21:47:34 +00004684 break;
4685 }
4686
4687
4688 /* IEND found */
4689
4690 /*
4691 Finish up reading image data:
4692
4693 o read main image from color_blob.
4694
4695 o close color_blob.
4696
4697 o if (color_type has alpha)
4698 if alpha_encoding is PNG
4699 read secondary image from alpha_blob via ReadPNG
4700 if alpha_encoding is JPEG
4701 read secondary image from alpha_blob via ReadJPEG
4702
4703 o close alpha_blob.
4704
4705 o copy intensity of secondary image into
cristy16ea1392012-03-21 20:38:41 +00004706 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004707
4708 o destroy the secondary image.
4709 */
4710
4711 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004712
cristy3ed852e2009-09-05 21:47:34 +00004713 if (logging != MagickFalse)
4714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4715 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004716
cristy3b6fd2e2011-05-20 12:53:50 +00004717 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004718 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004719
cristy3ed852e2009-09-05 21:47:34 +00004720 color_image_info->ping=MagickFalse; /* To do: avoid this */
4721 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004722
cristy3ed852e2009-09-05 21:47:34 +00004723 if (jng_image == (Image *) NULL)
4724 return((Image *) NULL);
4725
4726 (void) RelinquishUniqueFileResource(color_image->filename);
4727 color_image=DestroyImage(color_image);
4728 color_image_info=DestroyImageInfo(color_image_info);
4729
4730 if (jng_image == (Image *) NULL)
4731 return((Image *) NULL);
4732
4733 if (logging != MagickFalse)
4734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4735 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004736
cristy3ed852e2009-09-05 21:47:34 +00004737 image->rows=jng_height;
4738 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004739
cristybb503372010-05-27 20:51:26 +00004740 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004741 {
cristy16ea1392012-03-21 20:38:41 +00004742 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004743 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00004744 for (x=(ssize_t) image->columns; x != 0; x--)
4745 {
4746 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4747 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4748 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4749 q+=GetPixelChannels(image);
4750 s+=GetPixelChannels(jng_image);
4751 }
glennrp47b9dd52010-11-24 18:12:06 +00004752
cristy3ed852e2009-09-05 21:47:34 +00004753 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4754 break;
4755 }
glennrp0fe50b42010-11-16 03:52:51 +00004756
cristy3ed852e2009-09-05 21:47:34 +00004757 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004758
cristy3ed852e2009-09-05 21:47:34 +00004759 if (image_info->ping == MagickFalse)
4760 {
4761 if (jng_color_type >= 12)
4762 {
4763 if (jng_alpha_compression_method == 0)
4764 {
4765 png_byte
4766 data[5];
4767 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4768 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004769 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004770 (void) WriteBlob(alpha_image,4,data);
4771 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4772 }
glennrp0fe50b42010-11-16 03:52:51 +00004773
cristy3ed852e2009-09-05 21:47:34 +00004774 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004775
cristy3ed852e2009-09-05 21:47:34 +00004776 if (logging != MagickFalse)
4777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00004778 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004779
cristy3b6fd2e2011-05-20 12:53:50 +00004780 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004781 "%s",alpha_image->filename);
4782
4783 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004784
cristy3ed852e2009-09-05 21:47:34 +00004785 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004786 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004787 {
4788 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy16ea1392012-03-21 20:38:41 +00004789 exception);
cristy3ed852e2009-09-05 21:47:34 +00004790 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004791
cristy8a46d822012-08-28 23:32:39 +00004792 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00004793 for (x=(ssize_t) image->columns; x != 0; x--)
4794 {
4795 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4796 q+=GetPixelChannels(image);
4797 s+=GetPixelChannels(jng_image);
4798 }
glennrp0fe50b42010-11-16 03:52:51 +00004799
cristy3ed852e2009-09-05 21:47:34 +00004800 else
cristy16ea1392012-03-21 20:38:41 +00004801 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004802 {
cristy16ea1392012-03-21 20:38:41 +00004803 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4804 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy8a46d822012-08-28 23:32:39 +00004805 image->alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00004806 q+=GetPixelChannels(image);
4807 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004808 }
glennrp0fe50b42010-11-16 03:52:51 +00004809
cristy3ed852e2009-09-05 21:47:34 +00004810 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4811 break;
4812 }
4813 (void) RelinquishUniqueFileResource(alpha_image->filename);
4814 alpha_image=DestroyImage(alpha_image);
4815 alpha_image_info=DestroyImageInfo(alpha_image_info);
4816 if (jng_image != (Image *) NULL)
4817 jng_image=DestroyImage(jng_image);
4818 }
4819 }
4820
glennrp47b9dd52010-11-24 18:12:06 +00004821 /* Read the JNG image. */
4822
cristy3ed852e2009-09-05 21:47:34 +00004823 if (mng_info->mng_type == 0)
4824 {
4825 mng_info->mng_width=jng_width;
4826 mng_info->mng_height=jng_height;
4827 }
glennrp0fe50b42010-11-16 03:52:51 +00004828
cristy3ed852e2009-09-05 21:47:34 +00004829 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004830 {
4831 image->page.width=jng_width;
4832 image->page.height=jng_height;
4833 }
4834
cristy3ed852e2009-09-05 21:47:34 +00004835 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004836 {
4837 image->page.x=mng_info->x_off[mng_info->object_id];
4838 image->page.y=mng_info->y_off[mng_info->object_id];
4839 }
4840
cristy3ed852e2009-09-05 21:47:34 +00004841 else
glennrp0fe50b42010-11-16 03:52:51 +00004842 {
4843 image->page.y=mng_info->y_off[mng_info->object_id];
4844 }
4845
cristy3ed852e2009-09-05 21:47:34 +00004846 mng_info->image_found++;
4847 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4848 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004849
cristy3ed852e2009-09-05 21:47:34 +00004850 if (logging != MagickFalse)
4851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4852 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004853
cristy3ed852e2009-09-05 21:47:34 +00004854 return(image);
4855}
4856
4857/*
4858%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4859% %
4860% %
4861% %
4862% R e a d J N G I m a g e %
4863% %
4864% %
4865% %
4866%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4867%
4868% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4869% (including the 8-byte signature) and returns it. It allocates the memory
4870% necessary for the new Image structure and returns a pointer to the new
4871% image.
4872%
4873% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4874%
4875% The format of the ReadJNGImage method is:
4876%
4877% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4878% *exception)
4879%
4880% A description of each parameter follows:
4881%
4882% o image_info: the image info.
4883%
4884% o exception: return any errors or warnings in this structure.
4885%
4886*/
4887
4888static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4889{
4890 Image
4891 *image,
4892 *previous;
4893
4894 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004895 have_mng_structure,
4896 logging,
cristy3ed852e2009-09-05 21:47:34 +00004897 status;
4898
4899 MngInfo
4900 *mng_info;
4901
4902 char
4903 magic_number[MaxTextExtent];
4904
cristy3ed852e2009-09-05 21:47:34 +00004905 size_t
4906 count;
4907
4908 /*
4909 Open image file.
4910 */
4911 assert(image_info != (const ImageInfo *) NULL);
4912 assert(image_info->signature == MagickSignature);
4913 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4914 assert(exception != (ExceptionInfo *) NULL);
4915 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004916 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004917 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004918 mng_info=(MngInfo *) NULL;
4919 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004920
cristy3ed852e2009-09-05 21:47:34 +00004921 if (status == MagickFalse)
4922 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004923
cristy3ed852e2009-09-05 21:47:34 +00004924 if (LocaleCompare(image_info->magick,"JNG") != 0)
4925 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004926
glennrp47b9dd52010-11-24 18:12:06 +00004927 /* Verify JNG signature. */
4928
cristy3ed852e2009-09-05 21:47:34 +00004929 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004930
glennrp3b8763e2011-02-21 12:08:18 +00004931 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004932 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004933
glennrp47b9dd52010-11-24 18:12:06 +00004934 /* Allocate a MngInfo structure. */
4935
cristy3ed852e2009-09-05 21:47:34 +00004936 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004937 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004938
cristy3ed852e2009-09-05 21:47:34 +00004939 if (mng_info == (MngInfo *) NULL)
4940 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004941
glennrp47b9dd52010-11-24 18:12:06 +00004942 /* Initialize members of the MngInfo structure. */
4943
cristy3ed852e2009-09-05 21:47:34 +00004944 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4945 have_mng_structure=MagickTrue;
4946
4947 mng_info->image=image;
4948 previous=image;
4949 image=ReadOneJNGImage(mng_info,image_info,exception);
4950 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004951
cristy3ed852e2009-09-05 21:47:34 +00004952 if (image == (Image *) NULL)
4953 {
4954 if (IsImageObject(previous) != MagickFalse)
4955 {
4956 (void) CloseBlob(previous);
4957 (void) DestroyImageList(previous);
4958 }
glennrp0fe50b42010-11-16 03:52:51 +00004959
cristy3ed852e2009-09-05 21:47:34 +00004960 if (logging != MagickFalse)
4961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4962 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004963
cristy3ed852e2009-09-05 21:47:34 +00004964 return((Image *) NULL);
4965 }
4966 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004967
cristy3ed852e2009-09-05 21:47:34 +00004968 if (image->columns == 0 || image->rows == 0)
4969 {
4970 if (logging != MagickFalse)
4971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4972 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004973
cristy3ed852e2009-09-05 21:47:34 +00004974 ThrowReaderException(CorruptImageError,"CorruptImage");
4975 }
glennrp0fe50b42010-11-16 03:52:51 +00004976
cristy3ed852e2009-09-05 21:47:34 +00004977 if (logging != MagickFalse)
4978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004979
cristy3ed852e2009-09-05 21:47:34 +00004980 return(image);
4981}
4982#endif
4983
4984static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4985{
4986 char
4987 page_geometry[MaxTextExtent];
4988
4989 Image
4990 *image,
4991 *previous;
4992
cristy4383ec82011-01-05 15:42:32 +00004993 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004994 logging,
4995 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004996
cristy3ed852e2009-09-05 21:47:34 +00004997 volatile int
4998 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004999 object_id,
5000 term_chunk_found,
5001 skip_to_iend;
5002
cristybb503372010-05-27 20:51:26 +00005003 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005004 image_count=0;
5005
5006 MagickBooleanType
5007 status;
5008
5009 MagickOffsetType
5010 offset;
5011
5012 MngInfo
5013 *mng_info;
5014
5015 MngBox
5016 default_fb,
5017 fb,
5018 previous_fb;
5019
5020#if defined(MNG_INSERT_LAYERS)
cristy16ea1392012-03-21 20:38:41 +00005021 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005022 mng_background_color;
5023#endif
5024
5025 register unsigned char
5026 *p;
5027
cristybb503372010-05-27 20:51:26 +00005028 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005029 i;
5030
5031 size_t
5032 count;
5033
cristybb503372010-05-27 20:51:26 +00005034 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005035 loop_level;
5036
5037 volatile short
5038 skipping_loop;
5039
5040#if defined(MNG_INSERT_LAYERS)
5041 unsigned int
5042 mandatory_back=0;
5043#endif
5044
5045 volatile unsigned int
5046#ifdef MNG_OBJECT_BUFFERS
5047 mng_background_object=0,
5048#endif
5049 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5050
cristybb503372010-05-27 20:51:26 +00005051 size_t
cristy3ed852e2009-09-05 21:47:34 +00005052 default_frame_timeout,
5053 frame_timeout,
5054#if defined(MNG_INSERT_LAYERS)
5055 image_height,
5056 image_width,
5057#endif
5058 length;
5059
glennrp38ea0832010-06-02 18:50:28 +00005060 /* These delays are all measured in image ticks_per_second,
5061 * not in MNG ticks_per_second
5062 */
cristybb503372010-05-27 20:51:26 +00005063 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00005064 default_frame_delay,
5065 final_delay,
5066 final_image_delay,
5067 frame_delay,
5068#if defined(MNG_INSERT_LAYERS)
5069 insert_layers,
5070#endif
5071 mng_iterations=1,
5072 simplicity=0,
5073 subframe_height=0,
5074 subframe_width=0;
5075
5076 previous_fb.top=0;
5077 previous_fb.bottom=0;
5078 previous_fb.left=0;
5079 previous_fb.right=0;
5080 default_fb.top=0;
5081 default_fb.bottom=0;
5082 default_fb.left=0;
5083 default_fb.right=0;
5084
glennrp47b9dd52010-11-24 18:12:06 +00005085 /* Open image file. */
5086
cristy3ed852e2009-09-05 21:47:34 +00005087 assert(image_info != (const ImageInfo *) NULL);
5088 assert(image_info->signature == MagickSignature);
5089 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
5090 assert(exception != (ExceptionInfo *) NULL);
5091 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00005092 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy16ea1392012-03-21 20:38:41 +00005093 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005094 mng_info=(MngInfo *) NULL;
5095 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005096
cristy3ed852e2009-09-05 21:47:34 +00005097 if (status == MagickFalse)
5098 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00005099
cristy3ed852e2009-09-05 21:47:34 +00005100 first_mng_object=MagickFalse;
5101 skipping_loop=(-1);
5102 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005103
5104 /* Allocate a MngInfo structure. */
5105
cristy73bd4a52010-10-05 11:24:23 +00005106 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00005107
cristy3ed852e2009-09-05 21:47:34 +00005108 if (mng_info == (MngInfo *) NULL)
5109 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00005110
glennrp47b9dd52010-11-24 18:12:06 +00005111 /* Initialize members of the MngInfo structure. */
5112
cristy3ed852e2009-09-05 21:47:34 +00005113 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5114 mng_info->image=image;
5115 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00005116
5117 if (LocaleCompare(image_info->magick,"MNG") == 0)
5118 {
5119 char
5120 magic_number[MaxTextExtent];
5121
glennrp47b9dd52010-11-24 18:12:06 +00005122 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00005123 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5124 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5125 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005126
5127 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00005128 for (i=0; i < MNG_MAX_OBJECTS; i++)
5129 {
cristybb503372010-05-27 20:51:26 +00005130 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5131 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00005132 }
5133 mng_info->exists[0]=MagickTrue;
5134 }
glennrp47b9dd52010-11-24 18:12:06 +00005135
cristy3ed852e2009-09-05 21:47:34 +00005136 first_mng_object=MagickTrue;
5137 mng_type=0;
5138#if defined(MNG_INSERT_LAYERS)
5139 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5140#endif
5141 default_frame_delay=0;
5142 default_frame_timeout=0;
5143 frame_delay=0;
5144 final_delay=1;
5145 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5146 object_id=0;
5147 skip_to_iend=MagickFalse;
5148 term_chunk_found=MagickFalse;
5149 mng_info->framing_mode=1;
5150#if defined(MNG_INSERT_LAYERS)
5151 mandatory_back=MagickFalse;
5152#endif
5153#if defined(MNG_INSERT_LAYERS)
5154 mng_background_color=image->background_color;
5155#endif
5156 default_fb=mng_info->frame;
5157 previous_fb=mng_info->frame;
5158 do
5159 {
5160 char
5161 type[MaxTextExtent];
5162
5163 if (LocaleCompare(image_info->magick,"MNG") == 0)
5164 {
5165 unsigned char
5166 *chunk;
5167
5168 /*
5169 Read a new chunk.
5170 */
5171 type[0]='\0';
5172 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5173 length=ReadBlobMSBLong(image);
5174 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5175
5176 if (logging != MagickFalse)
5177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005178 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5179 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00005180
5181 if (length > PNG_UINT_31_MAX)
5182 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005183
cristy3ed852e2009-09-05 21:47:34 +00005184 if (count == 0)
5185 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00005186
cristy3ed852e2009-09-05 21:47:34 +00005187 p=NULL;
5188 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00005189
cristy3ed852e2009-09-05 21:47:34 +00005190 if (length)
5191 {
5192 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00005193
cristy3ed852e2009-09-05 21:47:34 +00005194 if (chunk == (unsigned char *) NULL)
5195 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00005196
cristybb503372010-05-27 20:51:26 +00005197 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005198 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00005199
cristy3ed852e2009-09-05 21:47:34 +00005200 p=chunk;
5201 }
glennrp0fe50b42010-11-16 03:52:51 +00005202
cristy3ed852e2009-09-05 21:47:34 +00005203 (void) ReadBlobMSBLong(image); /* read crc word */
5204
5205#if !defined(JNG_SUPPORTED)
5206 if (memcmp(type,mng_JHDR,4) == 0)
5207 {
5208 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005209
cristy3ed852e2009-09-05 21:47:34 +00005210 if (mng_info->jhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005211 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005212 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005213
cristy3ed852e2009-09-05 21:47:34 +00005214 mng_info->jhdr_warning++;
5215 }
5216#endif
5217 if (memcmp(type,mng_DHDR,4) == 0)
5218 {
5219 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005220
cristy3ed852e2009-09-05 21:47:34 +00005221 if (mng_info->dhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005222 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005223 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005224
cristy3ed852e2009-09-05 21:47:34 +00005225 mng_info->dhdr_warning++;
5226 }
5227 if (memcmp(type,mng_MEND,4) == 0)
5228 break;
glennrp47b9dd52010-11-24 18:12:06 +00005229
cristy3ed852e2009-09-05 21:47:34 +00005230 if (skip_to_iend)
5231 {
5232 if (memcmp(type,mng_IEND,4) == 0)
5233 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005234
cristy3ed852e2009-09-05 21:47:34 +00005235 if (length)
5236 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005237
cristy3ed852e2009-09-05 21:47:34 +00005238 if (logging != MagickFalse)
5239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5240 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00005241
cristy3ed852e2009-09-05 21:47:34 +00005242 continue;
5243 }
glennrp0fe50b42010-11-16 03:52:51 +00005244
cristy3ed852e2009-09-05 21:47:34 +00005245 if (memcmp(type,mng_MHDR,4) == 0)
5246 {
cristybb503372010-05-27 20:51:26 +00005247 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005248 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00005249
cristybb503372010-05-27 20:51:26 +00005250 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005251 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00005252
cristy3ed852e2009-09-05 21:47:34 +00005253 if (logging != MagickFalse)
5254 {
5255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005256 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00005257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005258 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005259 }
glennrp0fe50b42010-11-16 03:52:51 +00005260
cristy3ed852e2009-09-05 21:47:34 +00005261 p+=8;
cristy8182b072010-05-30 20:10:53 +00005262 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005263
cristy3ed852e2009-09-05 21:47:34 +00005264 if (mng_info->ticks_per_second == 0)
5265 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005266
cristy3ed852e2009-09-05 21:47:34 +00005267 else
5268 default_frame_delay=1UL*image->ticks_per_second/
5269 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005270
cristy3ed852e2009-09-05 21:47:34 +00005271 frame_delay=default_frame_delay;
5272 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00005273
cristy3ed852e2009-09-05 21:47:34 +00005274 if (length > 16)
5275 {
5276 p+=16;
cristy8182b072010-05-30 20:10:53 +00005277 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005278 }
glennrp0fe50b42010-11-16 03:52:51 +00005279
cristy3ed852e2009-09-05 21:47:34 +00005280 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00005281
cristy3ed852e2009-09-05 21:47:34 +00005282 if ((simplicity != 0) && ((simplicity | 11) == 11))
5283 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00005284
cristy3ed852e2009-09-05 21:47:34 +00005285 if ((simplicity != 0) && ((simplicity | 9) == 9))
5286 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00005287
cristy3ed852e2009-09-05 21:47:34 +00005288#if defined(MNG_INSERT_LAYERS)
5289 if (mng_type != 3)
5290 insert_layers=MagickTrue;
5291#endif
cristy16ea1392012-03-21 20:38:41 +00005292 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005293 {
glennrp47b9dd52010-11-24 18:12:06 +00005294 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005295 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005296
cristy3ed852e2009-09-05 21:47:34 +00005297 if (GetNextImageInList(image) == (Image *) NULL)
5298 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00005299
cristy3ed852e2009-09-05 21:47:34 +00005300 image=SyncNextImageInList(image);
5301 mng_info->image=image;
5302 }
5303
5304 if ((mng_info->mng_width > 65535L) ||
5305 (mng_info->mng_height > 65535L))
5306 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00005307
cristy3b6fd2e2011-05-20 12:53:50 +00005308 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00005309 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00005310 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00005311
cristy3ed852e2009-09-05 21:47:34 +00005312 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00005313 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00005314 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00005315 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00005316 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005317
cristy3ed852e2009-09-05 21:47:34 +00005318 for (i=0; i < MNG_MAX_OBJECTS; i++)
5319 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005320
cristy3ed852e2009-09-05 21:47:34 +00005321 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5322 continue;
5323 }
5324
5325 if (memcmp(type,mng_TERM,4) == 0)
5326 {
5327 int
5328 repeat=0;
5329
5330
5331 if (length)
5332 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005333
cristy3ed852e2009-09-05 21:47:34 +00005334 if (repeat == 3)
5335 {
cristy8182b072010-05-30 20:10:53 +00005336 final_delay=(png_uint_32) mng_get_long(&p[2]);
5337 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00005338
cristy3ed852e2009-09-05 21:47:34 +00005339 if (mng_iterations == PNG_UINT_31_MAX)
5340 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00005341
cristy3ed852e2009-09-05 21:47:34 +00005342 image->iterations=mng_iterations;
5343 term_chunk_found=MagickTrue;
5344 }
glennrp0fe50b42010-11-16 03:52:51 +00005345
cristy3ed852e2009-09-05 21:47:34 +00005346 if (logging != MagickFalse)
5347 {
5348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5349 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00005350
cristy3ed852e2009-09-05 21:47:34 +00005351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005352 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00005353
cristy3ed852e2009-09-05 21:47:34 +00005354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005355 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00005356 }
glennrp0fe50b42010-11-16 03:52:51 +00005357
cristy3ed852e2009-09-05 21:47:34 +00005358 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5359 continue;
5360 }
5361 if (memcmp(type,mng_DEFI,4) == 0)
5362 {
5363 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005364 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005365 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5366 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005367
cristy3ed852e2009-09-05 21:47:34 +00005368 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005369
cristy3ed852e2009-09-05 21:47:34 +00005370 if (mng_type == 2 && object_id != 0)
cristy16ea1392012-03-21 20:38:41 +00005371 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005372 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5373 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005374
cristy3ed852e2009-09-05 21:47:34 +00005375 if (object_id > MNG_MAX_OBJECTS)
5376 {
5377 /*
glennrpedaa0382012-04-12 14:16:21 +00005378 Instead of using a warning we should allocate a larger
cristy3ed852e2009-09-05 21:47:34 +00005379 MngInfo structure and continue.
5380 */
cristy16ea1392012-03-21 20:38:41 +00005381 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005382 CoderError,"object id too large","`%s'",image->filename);
5383 object_id=MNG_MAX_OBJECTS;
5384 }
glennrp0fe50b42010-11-16 03:52:51 +00005385
cristy3ed852e2009-09-05 21:47:34 +00005386 if (mng_info->exists[object_id])
5387 if (mng_info->frozen[object_id])
5388 {
5389 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristy16ea1392012-03-21 20:38:41 +00005390 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005391 GetMagickModule(),CoderError,
5392 "DEFI cannot redefine a frozen MNG object","`%s'",
5393 image->filename);
5394 continue;
5395 }
glennrp0fe50b42010-11-16 03:52:51 +00005396
cristy3ed852e2009-09-05 21:47:34 +00005397 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005398
cristy3ed852e2009-09-05 21:47:34 +00005399 if (length > 2)
5400 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005401
cristy3ed852e2009-09-05 21:47:34 +00005402 /*
5403 Extract object offset info.
5404 */
5405 if (length > 11)
5406 {
glennrp0fe50b42010-11-16 03:52:51 +00005407 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5408 (p[5] << 16) | (p[6] << 8) | p[7]);
5409
5410 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5411 (p[9] << 16) | (p[10] << 8) | p[11]);
5412
cristy3ed852e2009-09-05 21:47:34 +00005413 if (logging != MagickFalse)
5414 {
5415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005416 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005417 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005418
cristy3ed852e2009-09-05 21:47:34 +00005419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005420 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005421 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005422 }
5423 }
glennrp0fe50b42010-11-16 03:52:51 +00005424
cristy3ed852e2009-09-05 21:47:34 +00005425 /*
5426 Extract object clipping info.
5427 */
5428 if (length > 27)
5429 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5430 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005431
cristy3ed852e2009-09-05 21:47:34 +00005432 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5433 continue;
5434 }
5435 if (memcmp(type,mng_bKGD,4) == 0)
5436 {
5437 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005438
cristy3ed852e2009-09-05 21:47:34 +00005439 if (length > 5)
5440 {
5441 mng_info->mng_global_bkgd.red=
5442 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005443
cristy3ed852e2009-09-05 21:47:34 +00005444 mng_info->mng_global_bkgd.green=
5445 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005446
cristy3ed852e2009-09-05 21:47:34 +00005447 mng_info->mng_global_bkgd.blue=
5448 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005449
cristy3ed852e2009-09-05 21:47:34 +00005450 mng_info->have_global_bkgd=MagickTrue;
5451 }
glennrp0fe50b42010-11-16 03:52:51 +00005452
cristy3ed852e2009-09-05 21:47:34 +00005453 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5454 continue;
5455 }
5456 if (memcmp(type,mng_BACK,4) == 0)
5457 {
5458#if defined(MNG_INSERT_LAYERS)
5459 if (length > 6)
5460 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005461
cristy3ed852e2009-09-05 21:47:34 +00005462 else
5463 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005464
cristy3ed852e2009-09-05 21:47:34 +00005465 if (mandatory_back && length > 5)
5466 {
5467 mng_background_color.red=
5468 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005469
cristy3ed852e2009-09-05 21:47:34 +00005470 mng_background_color.green=
5471 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005472
cristy3ed852e2009-09-05 21:47:34 +00005473 mng_background_color.blue=
5474 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005475
cristy16ea1392012-03-21 20:38:41 +00005476 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005477 }
glennrp0fe50b42010-11-16 03:52:51 +00005478
cristy3ed852e2009-09-05 21:47:34 +00005479#ifdef MNG_OBJECT_BUFFERS
5480 if (length > 8)
5481 mng_background_object=(p[7] << 8) | p[8];
5482#endif
5483#endif
5484 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5485 continue;
5486 }
glennrp47b9dd52010-11-24 18:12:06 +00005487
cristy3ed852e2009-09-05 21:47:34 +00005488 if (memcmp(type,mng_PLTE,4) == 0)
5489 {
glennrp47b9dd52010-11-24 18:12:06 +00005490 /* Read global PLTE. */
5491
cristy3ed852e2009-09-05 21:47:34 +00005492 if (length && (length < 769))
5493 {
5494 if (mng_info->global_plte == (png_colorp) NULL)
5495 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5496 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005497
cristybb503372010-05-27 20:51:26 +00005498 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005499 {
5500 mng_info->global_plte[i].red=p[3*i];
5501 mng_info->global_plte[i].green=p[3*i+1];
5502 mng_info->global_plte[i].blue=p[3*i+2];
5503 }
glennrp0fe50b42010-11-16 03:52:51 +00005504
cristy35ef8242010-06-03 16:24:13 +00005505 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005506 }
5507#ifdef MNG_LOOSE
5508 for ( ; i < 256; i++)
5509 {
5510 mng_info->global_plte[i].red=i;
5511 mng_info->global_plte[i].green=i;
5512 mng_info->global_plte[i].blue=i;
5513 }
glennrp0fe50b42010-11-16 03:52:51 +00005514
cristy3ed852e2009-09-05 21:47:34 +00005515 if (length)
5516 mng_info->global_plte_length=256;
5517#endif
5518 else
5519 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005520
cristy3ed852e2009-09-05 21:47:34 +00005521 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5522 continue;
5523 }
glennrp47b9dd52010-11-24 18:12:06 +00005524
cristy3ed852e2009-09-05 21:47:34 +00005525 if (memcmp(type,mng_tRNS,4) == 0)
5526 {
5527 /* read global tRNS */
5528
5529 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005530 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005531 mng_info->global_trns[i]=p[i];
5532
5533#ifdef MNG_LOOSE
5534 for ( ; i < 256; i++)
5535 mng_info->global_trns[i]=255;
5536#endif
cristy12560f32010-06-03 16:51:08 +00005537 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005538 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5539 continue;
5540 }
5541 if (memcmp(type,mng_gAMA,4) == 0)
5542 {
5543 if (length == 4)
5544 {
cristybb503372010-05-27 20:51:26 +00005545 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005546 igamma;
5547
cristy8182b072010-05-30 20:10:53 +00005548 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005549 mng_info->global_gamma=((float) igamma)*0.00001;
5550 mng_info->have_global_gama=MagickTrue;
5551 }
glennrp0fe50b42010-11-16 03:52:51 +00005552
cristy3ed852e2009-09-05 21:47:34 +00005553 else
5554 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005555
cristy3ed852e2009-09-05 21:47:34 +00005556 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5557 continue;
5558 }
5559
5560 if (memcmp(type,mng_cHRM,4) == 0)
5561 {
glennrp47b9dd52010-11-24 18:12:06 +00005562 /* Read global cHRM */
5563
cristy3ed852e2009-09-05 21:47:34 +00005564 if (length == 32)
5565 {
cristy8182b072010-05-30 20:10:53 +00005566 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5567 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5568 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005569 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005570 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005571 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005572 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005573 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005574 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005575 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005576 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005577 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005578 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005579 mng_info->have_global_chrm=MagickTrue;
5580 }
5581 else
5582 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005583
cristy3ed852e2009-09-05 21:47:34 +00005584 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5585 continue;
5586 }
glennrp47b9dd52010-11-24 18:12:06 +00005587
cristy3ed852e2009-09-05 21:47:34 +00005588 if (memcmp(type,mng_sRGB,4) == 0)
5589 {
5590 /*
5591 Read global sRGB.
5592 */
5593 if (length)
5594 {
glennrpe610a072010-08-05 17:08:46 +00005595 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005596 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005597 mng_info->have_global_srgb=MagickTrue;
5598 }
5599 else
5600 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005601
cristy3ed852e2009-09-05 21:47:34 +00005602 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5603 continue;
5604 }
glennrp47b9dd52010-11-24 18:12:06 +00005605
cristy3ed852e2009-09-05 21:47:34 +00005606 if (memcmp(type,mng_iCCP,4) == 0)
5607 {
glennrpfd05d622011-02-25 04:10:33 +00005608 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005609
5610 /*
5611 Read global iCCP.
5612 */
5613 if (length)
5614 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005615
cristy3ed852e2009-09-05 21:47:34 +00005616 continue;
5617 }
glennrp47b9dd52010-11-24 18:12:06 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619 if (memcmp(type,mng_FRAM,4) == 0)
5620 {
5621 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005622 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005623 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5624 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005625
cristy3ed852e2009-09-05 21:47:34 +00005626 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5627 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005628
cristy3ed852e2009-09-05 21:47:34 +00005629 frame_delay=default_frame_delay;
5630 frame_timeout=default_frame_timeout;
5631 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005632
cristy3ed852e2009-09-05 21:47:34 +00005633 if (length)
5634 if (p[0])
5635 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005636
cristy3ed852e2009-09-05 21:47:34 +00005637 if (logging != MagickFalse)
5638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5639 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005640
cristy3ed852e2009-09-05 21:47:34 +00005641 if (length > 6)
5642 {
glennrp47b9dd52010-11-24 18:12:06 +00005643 /* Note the delay and frame clipping boundaries. */
5644
cristy3ed852e2009-09-05 21:47:34 +00005645 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005646
cristybb503372010-05-27 20:51:26 +00005647 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005648 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005649
cristy3ed852e2009-09-05 21:47:34 +00005650 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005651
cristybb503372010-05-27 20:51:26 +00005652 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005653 {
5654 int
5655 change_delay,
5656 change_timeout,
5657 change_clipping;
5658
5659 change_delay=(*p++);
5660 change_timeout=(*p++);
5661 change_clipping=(*p++);
5662 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005663
cristy3ed852e2009-09-05 21:47:34 +00005664 if (change_delay)
5665 {
cristy8182b072010-05-30 20:10:53 +00005666 frame_delay=1UL*image->ticks_per_second*
5667 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005668
cristy8182b072010-05-30 20:10:53 +00005669 if (mng_info->ticks_per_second != 0)
5670 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005671
glennrpbb010dd2010-06-01 13:07:15 +00005672 else
5673 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005674
cristy3ed852e2009-09-05 21:47:34 +00005675 if (change_delay == 2)
5676 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005677
cristy3ed852e2009-09-05 21:47:34 +00005678 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005679
cristy3ed852e2009-09-05 21:47:34 +00005680 if (logging != MagickFalse)
5681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005682 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005683 }
glennrp47b9dd52010-11-24 18:12:06 +00005684
cristy3ed852e2009-09-05 21:47:34 +00005685 if (change_timeout)
5686 {
glennrpbb010dd2010-06-01 13:07:15 +00005687 frame_timeout=1UL*image->ticks_per_second*
5688 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005689
glennrpbb010dd2010-06-01 13:07:15 +00005690 if (mng_info->ticks_per_second != 0)
5691 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005692
glennrpbb010dd2010-06-01 13:07:15 +00005693 else
5694 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005695
cristy3ed852e2009-09-05 21:47:34 +00005696 if (change_delay == 2)
5697 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005698
cristy3ed852e2009-09-05 21:47:34 +00005699 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005700
cristy3ed852e2009-09-05 21:47:34 +00005701 if (logging != MagickFalse)
5702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005703 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005704 }
glennrp47b9dd52010-11-24 18:12:06 +00005705
cristy3ed852e2009-09-05 21:47:34 +00005706 if (change_clipping)
5707 {
5708 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5709 p+=17;
5710 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005711
cristy3ed852e2009-09-05 21:47:34 +00005712 if (logging != MagickFalse)
5713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005714 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005715 (double) fb.left,(double) fb.right,(double) fb.top,
5716 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005717
cristy3ed852e2009-09-05 21:47:34 +00005718 if (change_clipping == 2)
5719 default_fb=fb;
5720 }
5721 }
5722 }
5723 mng_info->clip=fb;
5724 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005725
cristybb503372010-05-27 20:51:26 +00005726 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005727 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005728
cristybb503372010-05-27 20:51:26 +00005729 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005730 -mng_info->clip.top);
5731 /*
5732 Insert a background layer behind the frame if framing_mode is 4.
5733 */
5734#if defined(MNG_INSERT_LAYERS)
5735 if (logging != MagickFalse)
5736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005737 " subframe_width=%.20g, subframe_height=%.20g",(double)
5738 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005739
cristy3ed852e2009-09-05 21:47:34 +00005740 if (insert_layers && (mng_info->framing_mode == 4) &&
5741 (subframe_width) && (subframe_height))
5742 {
glennrp47b9dd52010-11-24 18:12:06 +00005743 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005744 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005745 {
cristy16ea1392012-03-21 20:38:41 +00005746 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005747
cristy3ed852e2009-09-05 21:47:34 +00005748 if (GetNextImageInList(image) == (Image *) NULL)
5749 {
5750 image=DestroyImageList(image);
5751 MngInfoFreeStruct(mng_info,&have_mng_structure);
5752 return((Image *) NULL);
5753 }
glennrp47b9dd52010-11-24 18:12:06 +00005754
cristy3ed852e2009-09-05 21:47:34 +00005755 image=SyncNextImageInList(image);
5756 }
glennrp0fe50b42010-11-16 03:52:51 +00005757
cristy3ed852e2009-09-05 21:47:34 +00005758 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005759
cristy3ed852e2009-09-05 21:47:34 +00005760 if (term_chunk_found)
5761 {
5762 image->start_loop=MagickTrue;
5763 image->iterations=mng_iterations;
5764 term_chunk_found=MagickFalse;
5765 }
glennrp0fe50b42010-11-16 03:52:51 +00005766
cristy3ed852e2009-09-05 21:47:34 +00005767 else
5768 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005769
cristy3ed852e2009-09-05 21:47:34 +00005770 image->columns=subframe_width;
5771 image->rows=subframe_height;
5772 image->page.width=subframe_width;
5773 image->page.height=subframe_height;
5774 image->page.x=mng_info->clip.left;
5775 image->page.y=mng_info->clip.top;
5776 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00005777 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00005778 image->delay=0;
cristy16ea1392012-03-21 20:38:41 +00005779 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005780
cristy3ed852e2009-09-05 21:47:34 +00005781 if (logging != MagickFalse)
5782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005783 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005784 (double) mng_info->clip.left,(double) mng_info->clip.right,
5785 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005786 }
5787#endif
5788 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5789 continue;
5790 }
5791 if (memcmp(type,mng_CLIP,4) == 0)
5792 {
5793 unsigned int
5794 first_object,
5795 last_object;
5796
5797 /*
5798 Read CLIP.
5799 */
5800 first_object=(p[0] << 8) | p[1];
5801 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005802
cristy3ed852e2009-09-05 21:47:34 +00005803 for (i=(int) first_object; i <= (int) last_object; i++)
5804 {
5805 if (mng_info->exists[i] && !mng_info->frozen[i])
5806 {
5807 MngBox
5808 box;
5809
5810 box=mng_info->object_clip[i];
5811 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5812 }
5813 }
glennrp47b9dd52010-11-24 18:12:06 +00005814
cristy3ed852e2009-09-05 21:47:34 +00005815 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5816 continue;
5817 }
5818 if (memcmp(type,mng_SAVE,4) == 0)
5819 {
5820 for (i=1; i < MNG_MAX_OBJECTS; i++)
5821 if (mng_info->exists[i])
5822 {
5823 mng_info->frozen[i]=MagickTrue;
5824#ifdef MNG_OBJECT_BUFFERS
5825 if (mng_info->ob[i] != (MngBuffer *) NULL)
5826 mng_info->ob[i]->frozen=MagickTrue;
5827#endif
5828 }
glennrp0fe50b42010-11-16 03:52:51 +00005829
cristy3ed852e2009-09-05 21:47:34 +00005830 if (length)
5831 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005832
cristy3ed852e2009-09-05 21:47:34 +00005833 continue;
5834 }
5835
5836 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5837 {
glennrp47b9dd52010-11-24 18:12:06 +00005838 /* Read DISC or SEEK. */
5839
cristy3ed852e2009-09-05 21:47:34 +00005840 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5841 {
5842 for (i=1; i < MNG_MAX_OBJECTS; i++)
5843 MngInfoDiscardObject(mng_info,i);
5844 }
glennrp0fe50b42010-11-16 03:52:51 +00005845
cristy3ed852e2009-09-05 21:47:34 +00005846 else
5847 {
cristybb503372010-05-27 20:51:26 +00005848 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005849 j;
5850
cristybb503372010-05-27 20:51:26 +00005851 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005852 {
5853 i=p[j] << 8 | p[j+1];
5854 MngInfoDiscardObject(mng_info,i);
5855 }
5856 }
glennrp0fe50b42010-11-16 03:52:51 +00005857
cristy3ed852e2009-09-05 21:47:34 +00005858 if (length)
5859 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005860
cristy3ed852e2009-09-05 21:47:34 +00005861 continue;
5862 }
glennrp47b9dd52010-11-24 18:12:06 +00005863
cristy3ed852e2009-09-05 21:47:34 +00005864 if (memcmp(type,mng_MOVE,4) == 0)
5865 {
cristybb503372010-05-27 20:51:26 +00005866 size_t
cristy3ed852e2009-09-05 21:47:34 +00005867 first_object,
5868 last_object;
5869
glennrp47b9dd52010-11-24 18:12:06 +00005870 /* read MOVE */
5871
cristy3ed852e2009-09-05 21:47:34 +00005872 first_object=(p[0] << 8) | p[1];
5873 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005874 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005875 {
5876 if (mng_info->exists[i] && !mng_info->frozen[i])
5877 {
5878 MngPair
5879 new_pair;
5880
5881 MngPair
5882 old_pair;
5883
5884 old_pair.a=mng_info->x_off[i];
5885 old_pair.b=mng_info->y_off[i];
5886 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5887 mng_info->x_off[i]=new_pair.a;
5888 mng_info->y_off[i]=new_pair.b;
5889 }
5890 }
glennrp47b9dd52010-11-24 18:12:06 +00005891
cristy3ed852e2009-09-05 21:47:34 +00005892 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5893 continue;
5894 }
5895
5896 if (memcmp(type,mng_LOOP,4) == 0)
5897 {
cristybb503372010-05-27 20:51:26 +00005898 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005899 loop_level=chunk[0];
5900 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005901
5902 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005903 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005904
cristy3ed852e2009-09-05 21:47:34 +00005905 if (logging != MagickFalse)
5906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005907 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5908 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005909
cristy3ed852e2009-09-05 21:47:34 +00005910 if (loop_iters == 0)
5911 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005912
cristy3ed852e2009-09-05 21:47:34 +00005913 else
5914 {
5915 mng_info->loop_jump[loop_level]=TellBlob(image);
5916 mng_info->loop_count[loop_level]=loop_iters;
5917 }
glennrp0fe50b42010-11-16 03:52:51 +00005918
cristy3ed852e2009-09-05 21:47:34 +00005919 mng_info->loop_iteration[loop_level]=0;
5920 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5921 continue;
5922 }
glennrp47b9dd52010-11-24 18:12:06 +00005923
cristy3ed852e2009-09-05 21:47:34 +00005924 if (memcmp(type,mng_ENDL,4) == 0)
5925 {
5926 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005927
cristy3ed852e2009-09-05 21:47:34 +00005928 if (skipping_loop > 0)
5929 {
5930 if (skipping_loop == loop_level)
5931 {
5932 /*
5933 Found end of zero-iteration loop.
5934 */
5935 skipping_loop=(-1);
5936 mng_info->loop_active[loop_level]=0;
5937 }
5938 }
glennrp47b9dd52010-11-24 18:12:06 +00005939
cristy3ed852e2009-09-05 21:47:34 +00005940 else
5941 {
5942 if (mng_info->loop_active[loop_level] == 1)
5943 {
5944 mng_info->loop_count[loop_level]--;
5945 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005946
cristy3ed852e2009-09-05 21:47:34 +00005947 if (logging != MagickFalse)
5948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005949 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005950 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005951 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005952
cristy3ed852e2009-09-05 21:47:34 +00005953 if (mng_info->loop_count[loop_level] != 0)
5954 {
5955 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5956 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005957
cristy3ed852e2009-09-05 21:47:34 +00005958 if (offset < 0)
5959 ThrowReaderException(CorruptImageError,
5960 "ImproperImageHeader");
5961 }
glennrp47b9dd52010-11-24 18:12:06 +00005962
cristy3ed852e2009-09-05 21:47:34 +00005963 else
5964 {
5965 short
5966 last_level;
5967
5968 /*
5969 Finished loop.
5970 */
5971 mng_info->loop_active[loop_level]=0;
5972 last_level=(-1);
5973 for (i=0; i < loop_level; i++)
5974 if (mng_info->loop_active[i] == 1)
5975 last_level=(short) i;
5976 loop_level=last_level;
5977 }
5978 }
5979 }
glennrp47b9dd52010-11-24 18:12:06 +00005980
cristy3ed852e2009-09-05 21:47:34 +00005981 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5982 continue;
5983 }
glennrp47b9dd52010-11-24 18:12:06 +00005984
cristy3ed852e2009-09-05 21:47:34 +00005985 if (memcmp(type,mng_CLON,4) == 0)
5986 {
5987 if (mng_info->clon_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005988 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005989 CoderError,"CLON is not implemented yet","`%s'",
5990 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005991
cristy3ed852e2009-09-05 21:47:34 +00005992 mng_info->clon_warning++;
5993 }
glennrp47b9dd52010-11-24 18:12:06 +00005994
cristy3ed852e2009-09-05 21:47:34 +00005995 if (memcmp(type,mng_MAGN,4) == 0)
5996 {
5997 png_uint_16
5998 magn_first,
5999 magn_last,
6000 magn_mb,
6001 magn_ml,
6002 magn_mr,
6003 magn_mt,
6004 magn_mx,
6005 magn_my,
6006 magn_methx,
6007 magn_methy;
6008
6009 if (length > 1)
6010 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00006011
cristy3ed852e2009-09-05 21:47:34 +00006012 else
6013 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00006014
cristy3ed852e2009-09-05 21:47:34 +00006015 if (length > 3)
6016 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00006017
cristy3ed852e2009-09-05 21:47:34 +00006018 else
6019 magn_last=magn_first;
6020#ifndef MNG_OBJECT_BUFFERS
6021 if (magn_first || magn_last)
6022 if (mng_info->magn_warning == 0)
6023 {
cristy16ea1392012-03-21 20:38:41 +00006024 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00006025 GetMagickModule(),CoderError,
6026 "MAGN is not implemented yet for nonzero objects",
6027 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006028
cristy3ed852e2009-09-05 21:47:34 +00006029 mng_info->magn_warning++;
6030 }
6031#endif
6032 if (length > 4)
6033 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00006034
cristy3ed852e2009-09-05 21:47:34 +00006035 else
6036 magn_methx=0;
6037
6038 if (length > 6)
6039 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00006040
cristy3ed852e2009-09-05 21:47:34 +00006041 else
6042 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00006043
cristy3ed852e2009-09-05 21:47:34 +00006044 if (magn_mx == 0)
6045 magn_mx=1;
6046
6047 if (length > 8)
6048 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00006049
cristy3ed852e2009-09-05 21:47:34 +00006050 else
6051 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006052
cristy3ed852e2009-09-05 21:47:34 +00006053 if (magn_my == 0)
6054 magn_my=1;
6055
6056 if (length > 10)
6057 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00006058
cristy3ed852e2009-09-05 21:47:34 +00006059 else
6060 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006061
cristy3ed852e2009-09-05 21:47:34 +00006062 if (magn_ml == 0)
6063 magn_ml=1;
6064
6065 if (length > 12)
6066 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00006067
cristy3ed852e2009-09-05 21:47:34 +00006068 else
6069 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006070
cristy3ed852e2009-09-05 21:47:34 +00006071 if (magn_mr == 0)
6072 magn_mr=1;
6073
6074 if (length > 14)
6075 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00006076
cristy3ed852e2009-09-05 21:47:34 +00006077 else
6078 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006079
cristy3ed852e2009-09-05 21:47:34 +00006080 if (magn_mt == 0)
6081 magn_mt=1;
6082
6083 if (length > 16)
6084 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00006085
cristy3ed852e2009-09-05 21:47:34 +00006086 else
6087 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006088
cristy3ed852e2009-09-05 21:47:34 +00006089 if (magn_mb == 0)
6090 magn_mb=1;
6091
6092 if (length > 17)
6093 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00006094
cristy3ed852e2009-09-05 21:47:34 +00006095 else
6096 magn_methy=magn_methx;
6097
glennrp47b9dd52010-11-24 18:12:06 +00006098
cristy3ed852e2009-09-05 21:47:34 +00006099 if (magn_methx > 5 || magn_methy > 5)
6100 if (mng_info->magn_warning == 0)
6101 {
cristy16ea1392012-03-21 20:38:41 +00006102 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00006103 GetMagickModule(),CoderError,
6104 "Unknown MAGN method in MNG datastream","`%s'",
6105 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006106
cristy3ed852e2009-09-05 21:47:34 +00006107 mng_info->magn_warning++;
6108 }
6109#ifdef MNG_OBJECT_BUFFERS
6110 /* Magnify existing objects in the range magn_first to magn_last */
6111#endif
6112 if (magn_first == 0 || magn_last == 0)
6113 {
6114 /* Save the magnification factors for object 0 */
6115 mng_info->magn_mb=magn_mb;
6116 mng_info->magn_ml=magn_ml;
6117 mng_info->magn_mr=magn_mr;
6118 mng_info->magn_mt=magn_mt;
6119 mng_info->magn_mx=magn_mx;
6120 mng_info->magn_my=magn_my;
6121 mng_info->magn_methx=magn_methx;
6122 mng_info->magn_methy=magn_methy;
6123 }
6124 }
glennrp47b9dd52010-11-24 18:12:06 +00006125
cristy3ed852e2009-09-05 21:47:34 +00006126 if (memcmp(type,mng_PAST,4) == 0)
6127 {
6128 if (mng_info->past_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006129 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006130 CoderError,"PAST is not implemented yet","`%s'",
6131 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006132
cristy3ed852e2009-09-05 21:47:34 +00006133 mng_info->past_warning++;
6134 }
glennrp47b9dd52010-11-24 18:12:06 +00006135
cristy3ed852e2009-09-05 21:47:34 +00006136 if (memcmp(type,mng_SHOW,4) == 0)
6137 {
6138 if (mng_info->show_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006139 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006140 CoderError,"SHOW is not implemented yet","`%s'",
6141 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006142
cristy3ed852e2009-09-05 21:47:34 +00006143 mng_info->show_warning++;
6144 }
glennrp47b9dd52010-11-24 18:12:06 +00006145
cristy3ed852e2009-09-05 21:47:34 +00006146 if (memcmp(type,mng_sBIT,4) == 0)
6147 {
6148 if (length < 4)
6149 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006150
cristy3ed852e2009-09-05 21:47:34 +00006151 else
6152 {
6153 mng_info->global_sbit.gray=p[0];
6154 mng_info->global_sbit.red=p[0];
6155 mng_info->global_sbit.green=p[1];
6156 mng_info->global_sbit.blue=p[2];
6157 mng_info->global_sbit.alpha=p[3];
6158 mng_info->have_global_sbit=MagickTrue;
6159 }
6160 }
6161 if (memcmp(type,mng_pHYs,4) == 0)
6162 {
6163 if (length > 8)
6164 {
6165 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00006166 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00006167 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00006168 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00006169 mng_info->global_phys_unit_type=p[8];
6170 mng_info->have_global_phys=MagickTrue;
6171 }
glennrp47b9dd52010-11-24 18:12:06 +00006172
cristy3ed852e2009-09-05 21:47:34 +00006173 else
6174 mng_info->have_global_phys=MagickFalse;
6175 }
6176 if (memcmp(type,mng_pHYg,4) == 0)
6177 {
6178 if (mng_info->phyg_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006179 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006180 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006181
cristy3ed852e2009-09-05 21:47:34 +00006182 mng_info->phyg_warning++;
6183 }
6184 if (memcmp(type,mng_BASI,4) == 0)
6185 {
6186 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00006187
cristy3ed852e2009-09-05 21:47:34 +00006188 if (mng_info->basi_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006189 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006190 CoderError,"BASI is not implemented yet","`%s'",
6191 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006192
cristy3ed852e2009-09-05 21:47:34 +00006193 mng_info->basi_warning++;
6194#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00006195 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00006196 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00006197 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00006198 (p[6] << 8) | p[7]);
6199 basi_color_type=p[8];
6200 basi_compression_method=p[9];
6201 basi_filter_type=p[10];
6202 basi_interlace_method=p[11];
6203 if (length > 11)
6204 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00006205
cristy3ed852e2009-09-05 21:47:34 +00006206 else
6207 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00006208
cristy3ed852e2009-09-05 21:47:34 +00006209 if (length > 13)
6210 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00006211
cristy3ed852e2009-09-05 21:47:34 +00006212 else
6213 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00006214
cristy3ed852e2009-09-05 21:47:34 +00006215 if (length > 15)
6216 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00006217
cristy3ed852e2009-09-05 21:47:34 +00006218 else
6219 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00006220
cristy3ed852e2009-09-05 21:47:34 +00006221 if (length > 17)
6222 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00006223
cristy3ed852e2009-09-05 21:47:34 +00006224 else
6225 {
6226 if (basi_sample_depth == 16)
6227 basi_alpha=65535L;
6228 else
6229 basi_alpha=255;
6230 }
glennrp47b9dd52010-11-24 18:12:06 +00006231
cristy3ed852e2009-09-05 21:47:34 +00006232 if (length > 19)
6233 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00006234
cristy3ed852e2009-09-05 21:47:34 +00006235 else
6236 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00006237
cristy3ed852e2009-09-05 21:47:34 +00006238#endif
6239 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6240 continue;
6241 }
glennrp47b9dd52010-11-24 18:12:06 +00006242
cristy3ed852e2009-09-05 21:47:34 +00006243 if (memcmp(type,mng_IHDR,4)
6244#if defined(JNG_SUPPORTED)
6245 && memcmp(type,mng_JHDR,4)
6246#endif
6247 )
6248 {
6249 /* Not an IHDR or JHDR chunk */
6250 if (length)
6251 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00006252
cristy3ed852e2009-09-05 21:47:34 +00006253 continue;
6254 }
6255/* Process IHDR */
6256 if (logging != MagickFalse)
6257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6258 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006259
cristy3ed852e2009-09-05 21:47:34 +00006260 mng_info->exists[object_id]=MagickTrue;
6261 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00006262
cristy3ed852e2009-09-05 21:47:34 +00006263 if (mng_info->invisible[object_id])
6264 {
6265 if (logging != MagickFalse)
6266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6267 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00006268
cristy3ed852e2009-09-05 21:47:34 +00006269 skip_to_iend=MagickTrue;
6270 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6271 continue;
6272 }
6273#if defined(MNG_INSERT_LAYERS)
6274 if (length < 8)
6275 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00006276
cristy8182b072010-05-30 20:10:53 +00006277 image_width=(size_t) mng_get_long(p);
6278 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00006279#endif
6280 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6281
6282 /*
6283 Insert a transparent background layer behind the entire animation
6284 if it is not full screen.
6285 */
6286#if defined(MNG_INSERT_LAYERS)
6287 if (insert_layers && mng_type && first_mng_object)
6288 {
6289 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6290 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00006291 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00006292 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00006293 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00006294 {
cristy16ea1392012-03-21 20:38:41 +00006295 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006296 {
6297 /*
6298 Allocate next image structure.
6299 */
cristy16ea1392012-03-21 20:38:41 +00006300 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006301
cristy3ed852e2009-09-05 21:47:34 +00006302 if (GetNextImageInList(image) == (Image *) NULL)
6303 {
6304 image=DestroyImageList(image);
6305 MngInfoFreeStruct(mng_info,&have_mng_structure);
6306 return((Image *) NULL);
6307 }
glennrp47b9dd52010-11-24 18:12:06 +00006308
cristy3ed852e2009-09-05 21:47:34 +00006309 image=SyncNextImageInList(image);
6310 }
6311 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00006312
cristy3ed852e2009-09-05 21:47:34 +00006313 if (term_chunk_found)
6314 {
6315 image->start_loop=MagickTrue;
6316 image->iterations=mng_iterations;
6317 term_chunk_found=MagickFalse;
6318 }
glennrp47b9dd52010-11-24 18:12:06 +00006319
cristy3ed852e2009-09-05 21:47:34 +00006320 else
6321 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006322
6323 /* Make a background rectangle. */
6324
cristy3ed852e2009-09-05 21:47:34 +00006325 image->delay=0;
6326 image->columns=mng_info->mng_width;
6327 image->rows=mng_info->mng_height;
6328 image->page.width=mng_info->mng_width;
6329 image->page.height=mng_info->mng_height;
6330 image->page.x=0;
6331 image->page.y=0;
6332 image->background_color=mng_background_color;
cristy16ea1392012-03-21 20:38:41 +00006333 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006334 if (logging != MagickFalse)
6335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006336 " Inserted transparent background layer, W=%.20g, H=%.20g",
6337 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00006338 }
6339 }
6340 /*
6341 Insert a background layer behind the upcoming image if
6342 framing_mode is 3, and we haven't already inserted one.
6343 */
6344 if (insert_layers && (mng_info->framing_mode == 3) &&
6345 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6346 (simplicity & 0x08)))
6347 {
cristy16ea1392012-03-21 20:38:41 +00006348 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006349 {
6350 /*
6351 Allocate next image structure.
6352 */
cristy16ea1392012-03-21 20:38:41 +00006353 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006354
cristy3ed852e2009-09-05 21:47:34 +00006355 if (GetNextImageInList(image) == (Image *) NULL)
6356 {
6357 image=DestroyImageList(image);
6358 MngInfoFreeStruct(mng_info,&have_mng_structure);
6359 return((Image *) NULL);
6360 }
glennrp47b9dd52010-11-24 18:12:06 +00006361
cristy3ed852e2009-09-05 21:47:34 +00006362 image=SyncNextImageInList(image);
6363 }
glennrp0fe50b42010-11-16 03:52:51 +00006364
cristy3ed852e2009-09-05 21:47:34 +00006365 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00006366
cristy3ed852e2009-09-05 21:47:34 +00006367 if (term_chunk_found)
6368 {
6369 image->start_loop=MagickTrue;
6370 image->iterations=mng_iterations;
6371 term_chunk_found=MagickFalse;
6372 }
glennrp0fe50b42010-11-16 03:52:51 +00006373
cristy3ed852e2009-09-05 21:47:34 +00006374 else
6375 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006376
cristy3ed852e2009-09-05 21:47:34 +00006377 image->delay=0;
6378 image->columns=subframe_width;
6379 image->rows=subframe_height;
6380 image->page.width=subframe_width;
6381 image->page.height=subframe_height;
6382 image->page.x=mng_info->clip.left;
6383 image->page.y=mng_info->clip.top;
6384 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00006385 image->alpha_trait=UndefinedPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00006386 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006387
cristy3ed852e2009-09-05 21:47:34 +00006388 if (logging != MagickFalse)
6389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006390 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006391 (double) mng_info->clip.left,(double) mng_info->clip.right,
6392 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006393 }
6394#endif /* MNG_INSERT_LAYERS */
6395 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006396
cristy16ea1392012-03-21 20:38:41 +00006397 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006398 {
6399 /*
6400 Allocate next image structure.
6401 */
cristy16ea1392012-03-21 20:38:41 +00006402 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006403
cristy3ed852e2009-09-05 21:47:34 +00006404 if (GetNextImageInList(image) == (Image *) NULL)
6405 {
6406 image=DestroyImageList(image);
6407 MngInfoFreeStruct(mng_info,&have_mng_structure);
6408 return((Image *) NULL);
6409 }
glennrp47b9dd52010-11-24 18:12:06 +00006410
cristy3ed852e2009-09-05 21:47:34 +00006411 image=SyncNextImageInList(image);
6412 }
6413 mng_info->image=image;
6414 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6415 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006416
cristy3ed852e2009-09-05 21:47:34 +00006417 if (status == MagickFalse)
6418 break;
glennrp0fe50b42010-11-16 03:52:51 +00006419
cristy3ed852e2009-09-05 21:47:34 +00006420 if (term_chunk_found)
6421 {
6422 image->start_loop=MagickTrue;
6423 term_chunk_found=MagickFalse;
6424 }
glennrp0fe50b42010-11-16 03:52:51 +00006425
cristy3ed852e2009-09-05 21:47:34 +00006426 else
6427 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006428
cristy3ed852e2009-09-05 21:47:34 +00006429 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6430 {
6431 image->delay=frame_delay;
6432 frame_delay=default_frame_delay;
6433 }
glennrp0fe50b42010-11-16 03:52:51 +00006434
cristy3ed852e2009-09-05 21:47:34 +00006435 else
6436 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006437
cristy3ed852e2009-09-05 21:47:34 +00006438 image->page.width=mng_info->mng_width;
6439 image->page.height=mng_info->mng_height;
6440 image->page.x=mng_info->x_off[object_id];
6441 image->page.y=mng_info->y_off[object_id];
6442 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006443
cristy3ed852e2009-09-05 21:47:34 +00006444 /*
6445 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6446 */
glennrp47b9dd52010-11-24 18:12:06 +00006447
cristy3ed852e2009-09-05 21:47:34 +00006448 if (logging != MagickFalse)
6449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6450 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6451 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006452
cristybb503372010-05-27 20:51:26 +00006453 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006454
cristy3ed852e2009-09-05 21:47:34 +00006455 if (offset < 0)
6456 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6457 }
6458
6459 previous=image;
6460 mng_info->image=image;
6461 mng_info->mng_type=mng_type;
6462 mng_info->object_id=object_id;
6463
6464 if (memcmp(type,mng_IHDR,4) == 0)
6465 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006466
cristy3ed852e2009-09-05 21:47:34 +00006467#if defined(JNG_SUPPORTED)
6468 else
6469 image=ReadOneJNGImage(mng_info,image_info,exception);
6470#endif
6471
6472 if (image == (Image *) NULL)
6473 {
6474 if (IsImageObject(previous) != MagickFalse)
6475 {
6476 (void) DestroyImageList(previous);
6477 (void) CloseBlob(previous);
6478 }
glennrp47b9dd52010-11-24 18:12:06 +00006479
cristy3ed852e2009-09-05 21:47:34 +00006480 MngInfoFreeStruct(mng_info,&have_mng_structure);
6481 return((Image *) NULL);
6482 }
glennrp0fe50b42010-11-16 03:52:51 +00006483
cristy3ed852e2009-09-05 21:47:34 +00006484 if (image->columns == 0 || image->rows == 0)
6485 {
6486 (void) CloseBlob(image);
6487 image=DestroyImageList(image);
6488 MngInfoFreeStruct(mng_info,&have_mng_structure);
6489 return((Image *) NULL);
6490 }
glennrp0fe50b42010-11-16 03:52:51 +00006491
cristy3ed852e2009-09-05 21:47:34 +00006492 mng_info->image=image;
6493
6494 if (mng_type)
6495 {
6496 MngBox
6497 crop_box;
6498
6499 if (mng_info->magn_methx || mng_info->magn_methy)
6500 {
6501 png_uint_32
6502 magnified_height,
6503 magnified_width;
6504
6505 if (logging != MagickFalse)
6506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6507 " Processing MNG MAGN chunk");
6508
6509 if (mng_info->magn_methx == 1)
6510 {
6511 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006512
cristy3ed852e2009-09-05 21:47:34 +00006513 if (image->columns > 1)
6514 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006515
cristy3ed852e2009-09-05 21:47:34 +00006516 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006517 magnified_width += (png_uint_32)
6518 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006519 }
glennrp47b9dd52010-11-24 18:12:06 +00006520
cristy3ed852e2009-09-05 21:47:34 +00006521 else
6522 {
cristy4e5bc842010-06-09 13:56:01 +00006523 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006524
cristy3ed852e2009-09-05 21:47:34 +00006525 if (image->columns > 1)
6526 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006527
cristy3ed852e2009-09-05 21:47:34 +00006528 if (image->columns > 2)
6529 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006530
cristy3ed852e2009-09-05 21:47:34 +00006531 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006532 magnified_width += (png_uint_32)
6533 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006534 }
glennrp47b9dd52010-11-24 18:12:06 +00006535
cristy3ed852e2009-09-05 21:47:34 +00006536 if (mng_info->magn_methy == 1)
6537 {
6538 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006539
cristy3ed852e2009-09-05 21:47:34 +00006540 if (image->rows > 1)
6541 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006542
cristy3ed852e2009-09-05 21:47:34 +00006543 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006544 magnified_height += (png_uint_32)
6545 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006546 }
glennrp47b9dd52010-11-24 18:12:06 +00006547
cristy3ed852e2009-09-05 21:47:34 +00006548 else
6549 {
cristy4e5bc842010-06-09 13:56:01 +00006550 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006551
cristy3ed852e2009-09-05 21:47:34 +00006552 if (image->rows > 1)
6553 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006554
cristy3ed852e2009-09-05 21:47:34 +00006555 if (image->rows > 2)
6556 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006557
cristy3ed852e2009-09-05 21:47:34 +00006558 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006559 magnified_height += (png_uint_32)
6560 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006561 }
glennrp47b9dd52010-11-24 18:12:06 +00006562
cristy3ed852e2009-09-05 21:47:34 +00006563 if (magnified_height > image->rows ||
6564 magnified_width > image->columns)
6565 {
6566 Image
6567 *large_image;
6568
6569 int
6570 yy;
6571
cristy16ea1392012-03-21 20:38:41 +00006572 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006573 *next,
6574 *prev;
6575
6576 png_uint_16
6577 magn_methx,
6578 magn_methy;
6579
cristy16ea1392012-03-21 20:38:41 +00006580 ssize_t
6581 m,
6582 y;
6583
6584 register Quantum
6585 *n,
6586 *q;
6587
6588 register ssize_t
6589 x;
6590
glennrp47b9dd52010-11-24 18:12:06 +00006591 /* Allocate next image structure. */
6592
cristy3ed852e2009-09-05 21:47:34 +00006593 if (logging != MagickFalse)
6594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6595 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006596
cristy16ea1392012-03-21 20:38:41 +00006597 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006598
cristy3ed852e2009-09-05 21:47:34 +00006599 if (GetNextImageInList(image) == (Image *) NULL)
6600 {
6601 image=DestroyImageList(image);
6602 MngInfoFreeStruct(mng_info,&have_mng_structure);
6603 return((Image *) NULL);
6604 }
6605
6606 large_image=SyncNextImageInList(image);
6607
6608 large_image->columns=magnified_width;
6609 large_image->rows=magnified_height;
6610
6611 magn_methx=mng_info->magn_methx;
6612 magn_methy=mng_info->magn_methy;
6613
glennrp3faa9a32011-04-23 14:00:25 +00006614#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006615#define QM unsigned short
6616 if (magn_methx != 1 || magn_methy != 1)
6617 {
6618 /*
6619 Scale pixels to unsigned shorts to prevent
6620 overflow of intermediate values of interpolations
6621 */
cristybb503372010-05-27 20:51:26 +00006622 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006623 {
6624 q=GetAuthenticPixels(image,0,y,image->columns,1,
6625 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006626
cristybb503372010-05-27 20:51:26 +00006627 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006628 {
cristy16ea1392012-03-21 20:38:41 +00006629 SetPixelRed(image,ScaleQuantumToShort(
6630 GetPixelRed(image,q)),q);
6631 SetPixelGreen(image,ScaleQuantumToShort(
6632 GetPixelGreen(image,q)),q);
6633 SetPixelBlue(image,ScaleQuantumToShort(
6634 GetPixelBlue(image,q)),q);
6635 SetPixelAlpha(image,ScaleQuantumToShort(
6636 GetPixelAlpha(image,q)),q);
6637 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006638 }
glennrp47b9dd52010-11-24 18:12:06 +00006639
cristy3ed852e2009-09-05 21:47:34 +00006640 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6641 break;
6642 }
6643 }
6644#else
6645#define QM Quantum
6646#endif
6647
cristy8a46d822012-08-28 23:32:39 +00006648 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006649 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006650
cristy3ed852e2009-09-05 21:47:34 +00006651 else
6652 {
cristy16ea1392012-03-21 20:38:41 +00006653 large_image->background_color.alpha=OpaqueAlpha;
6654 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006655
cristy3ed852e2009-09-05 21:47:34 +00006656 if (magn_methx == 4)
6657 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006658
cristy3ed852e2009-09-05 21:47:34 +00006659 if (magn_methx == 5)
6660 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006661
cristy3ed852e2009-09-05 21:47:34 +00006662 if (magn_methy == 4)
6663 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006664
cristy3ed852e2009-09-05 21:47:34 +00006665 if (magn_methy == 5)
6666 magn_methy=3;
6667 }
6668
6669 /* magnify the rows into the right side of the large image */
6670
6671 if (logging != MagickFalse)
6672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006673 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006674 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006675 yy=0;
cristy16ea1392012-03-21 20:38:41 +00006676 length=(size_t) image->columns*GetPixelChannels(image);
6677 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6678 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006679
cristy16ea1392012-03-21 20:38:41 +00006680 if ((prev == (Quantum *) NULL) ||
6681 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006682 {
6683 image=DestroyImageList(image);
6684 MngInfoFreeStruct(mng_info,&have_mng_structure);
6685 ThrowReaderException(ResourceLimitError,
6686 "MemoryAllocationFailed");
6687 }
glennrp47b9dd52010-11-24 18:12:06 +00006688
cristy3ed852e2009-09-05 21:47:34 +00006689 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6690 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006691
cristybb503372010-05-27 20:51:26 +00006692 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006693 {
6694 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006695 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006696
cristybb503372010-05-27 20:51:26 +00006697 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6698 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006699
cristybb503372010-05-27 20:51:26 +00006700 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6701 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006702
cristybb503372010-05-27 20:51:26 +00006703 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006704 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006705
cristy3ed852e2009-09-05 21:47:34 +00006706 else
cristybb503372010-05-27 20:51:26 +00006707 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006708
cristy3ed852e2009-09-05 21:47:34 +00006709 n=prev;
6710 prev=next;
6711 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006712
cristybb503372010-05-27 20:51:26 +00006713 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006714 {
6715 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6716 exception);
6717 (void) CopyMagickMemory(next,n,length);
6718 }
glennrp47b9dd52010-11-24 18:12:06 +00006719
cristy3ed852e2009-09-05 21:47:34 +00006720 for (i=0; i < m; i++, yy++)
6721 {
cristy16ea1392012-03-21 20:38:41 +00006722 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006723 *pixels;
6724
cristybb503372010-05-27 20:51:26 +00006725 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006726 pixels=prev;
6727 n=next;
6728 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006729 1,exception);
cristy16ea1392012-03-21 20:38:41 +00006730 q+=(large_image->columns-image->columns)*
6731 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006732
cristybb503372010-05-27 20:51:26 +00006733 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006734 {
glennrpfd05d622011-02-25 04:10:33 +00006735 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006736 /*
6737 if (image->storage_class == PseudoClass)
6738 {
6739 }
6740 */
6741
6742 if (magn_methy <= 1)
6743 {
glennrpbb4f99d2011-05-22 11:13:17 +00006744 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006745 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6746 SetPixelGreen(large_image,GetPixelGreen(image,
6747 pixels),q);
6748 SetPixelBlue(large_image,GetPixelBlue(image,
6749 pixels),q);
6750 SetPixelAlpha(large_image,GetPixelAlpha(image,
6751 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006752 }
glennrp47b9dd52010-11-24 18:12:06 +00006753
cristy3ed852e2009-09-05 21:47:34 +00006754 else if (magn_methy == 2 || magn_methy == 4)
6755 {
6756 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006757 {
cristy16ea1392012-03-21 20:38:41 +00006758 SetPixelRed(large_image,GetPixelRed(image,
6759 pixels),q);
6760 SetPixelGreen(large_image,GetPixelGreen(image,
6761 pixels),q);
6762 SetPixelBlue(large_image,GetPixelBlue(image,
6763 pixels),q);
6764 SetPixelAlpha(large_image,GetPixelAlpha(image,
6765 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006766 }
glennrp47b9dd52010-11-24 18:12:06 +00006767
cristy3ed852e2009-09-05 21:47:34 +00006768 else
6769 {
6770 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006771 SetPixelRed(large_image,((QM) (((ssize_t)
6772 (2*i*(GetPixelRed(image,n)
6773 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006774 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006775 +GetPixelRed(image,pixels)))),q);
6776 SetPixelGreen(large_image,((QM) (((ssize_t)
6777 (2*i*(GetPixelGreen(image,n)
6778 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006779 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006780 +GetPixelGreen(image,pixels)))),q);
6781 SetPixelBlue(large_image,((QM) (((ssize_t)
6782 (2*i*(GetPixelBlue(image,n)
6783 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006784 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006785 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006786
cristy8a46d822012-08-28 23:32:39 +00006787 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006788 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6789 (2*i*(GetPixelAlpha(image,n)
6790 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006791 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006792 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006793 }
glennrp47b9dd52010-11-24 18:12:06 +00006794
cristy3ed852e2009-09-05 21:47:34 +00006795 if (magn_methy == 4)
6796 {
6797 /* Replicate nearest */
6798 if (i <= ((m+1) << 1))
cristy16ea1392012-03-21 20:38:41 +00006799 SetPixelAlpha(large_image,GetPixelAlpha(image,
6800 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006801 else
cristy16ea1392012-03-21 20:38:41 +00006802 SetPixelAlpha(large_image,GetPixelAlpha(image,
6803 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006804 }
6805 }
glennrp47b9dd52010-11-24 18:12:06 +00006806
cristy3ed852e2009-09-05 21:47:34 +00006807 else /* if (magn_methy == 3 || magn_methy == 5) */
6808 {
6809 /* Replicate nearest */
6810 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006811 {
cristy16ea1392012-03-21 20:38:41 +00006812 SetPixelRed(large_image,GetPixelRed(image,
6813 pixels),q);
6814 SetPixelGreen(large_image,GetPixelGreen(image,
6815 pixels),q);
6816 SetPixelBlue(large_image,GetPixelBlue(image,
6817 pixels),q);
6818 SetPixelAlpha(large_image,GetPixelAlpha(image,
6819 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006820 }
glennrp47b9dd52010-11-24 18:12:06 +00006821
cristy3ed852e2009-09-05 21:47:34 +00006822 else
glennrpbb4f99d2011-05-22 11:13:17 +00006823 {
cristy16ea1392012-03-21 20:38:41 +00006824 SetPixelRed(large_image,GetPixelRed(image,n),q);
6825 SetPixelGreen(large_image,GetPixelGreen(image,n),
6826 q);
6827 SetPixelBlue(large_image,GetPixelBlue(image,n),
6828 q);
6829 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6830 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006831 }
glennrp47b9dd52010-11-24 18:12:06 +00006832
cristy3ed852e2009-09-05 21:47:34 +00006833 if (magn_methy == 5)
6834 {
cristy16ea1392012-03-21 20:38:41 +00006835 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6836 (GetPixelAlpha(image,n)
6837 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006838 +m))/((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006839 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006840 }
6841 }
cristy16ea1392012-03-21 20:38:41 +00006842 n+=GetPixelChannels(image);
6843 q+=GetPixelChannels(large_image);
6844 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006845 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006846
cristy3ed852e2009-09-05 21:47:34 +00006847 if (SyncAuthenticPixels(large_image,exception) == 0)
6848 break;
glennrp47b9dd52010-11-24 18:12:06 +00006849
cristy3ed852e2009-09-05 21:47:34 +00006850 } /* i */
6851 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006852
cristy16ea1392012-03-21 20:38:41 +00006853 prev=(Quantum *) RelinquishMagickMemory(prev);
6854 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006855
6856 length=image->columns;
6857
6858 if (logging != MagickFalse)
6859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6860 " Delete original image");
6861
6862 DeleteImageFromList(&image);
6863
6864 image=large_image;
6865
6866 mng_info->image=image;
6867
6868 /* magnify the columns */
6869 if (logging != MagickFalse)
6870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006871 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006872
cristybb503372010-05-27 20:51:26 +00006873 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006874 {
cristy16ea1392012-03-21 20:38:41 +00006875 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006876 *pixels;
6877
6878 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00006879 pixels=q+(image->columns-length)*GetPixelChannels(image);
6880 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006881
cristybb503372010-05-27 20:51:26 +00006882 for (x=(ssize_t) (image->columns-length);
6883 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006884 {
cristy16ea1392012-03-21 20:38:41 +00006885 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006886
cristybb503372010-05-27 20:51:26 +00006887 if (x == (ssize_t) (image->columns-length))
6888 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006889
cristybb503372010-05-27 20:51:26 +00006890 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6891 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006892
cristybb503372010-05-27 20:51:26 +00006893 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6894 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006895
cristybb503372010-05-27 20:51:26 +00006896 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006897 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006898
cristy3ed852e2009-09-05 21:47:34 +00006899 else
cristybb503372010-05-27 20:51:26 +00006900 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006901
cristy3ed852e2009-09-05 21:47:34 +00006902 for (i=0; i < m; i++)
6903 {
6904 if (magn_methx <= 1)
6905 {
6906 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006907 SetPixelRed(image,GetPixelRed(image,pixels),q);
6908 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6909 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6910 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006911 }
glennrp47b9dd52010-11-24 18:12:06 +00006912
cristy3ed852e2009-09-05 21:47:34 +00006913 else if (magn_methx == 2 || magn_methx == 4)
6914 {
6915 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006916 {
cristy16ea1392012-03-21 20:38:41 +00006917 SetPixelRed(image,GetPixelRed(image,pixels),q);
6918 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6919 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6920 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006921 }
glennrp47b9dd52010-11-24 18:12:06 +00006922
cristy16ea1392012-03-21 20:38:41 +00006923 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006924 else
6925 {
6926 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006927 SetPixelRed(image,(QM) ((2*i*(
6928 GetPixelRed(image,n)
6929 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006930 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006931 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006932
cristy16ea1392012-03-21 20:38:41 +00006933 SetPixelGreen(image,(QM) ((2*i*(
6934 GetPixelGreen(image,n)
6935 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006936 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006937 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006938
cristy16ea1392012-03-21 20:38:41 +00006939 SetPixelBlue(image,(QM) ((2*i*(
6940 GetPixelBlue(image,n)
6941 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006942 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006943 GetPixelBlue(image,pixels)),q);
cristy8a46d822012-08-28 23:32:39 +00006944 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006945 SetPixelAlpha(image,(QM) ((2*i*(
6946 GetPixelAlpha(image,n)
6947 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006948 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006949 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006950 }
glennrp47b9dd52010-11-24 18:12:06 +00006951
cristy3ed852e2009-09-05 21:47:34 +00006952 if (magn_methx == 4)
6953 {
6954 /* Replicate nearest */
6955 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006956 {
cristy16ea1392012-03-21 20:38:41 +00006957 SetPixelAlpha(image,
6958 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006959 }
cristy3ed852e2009-09-05 21:47:34 +00006960 else
glennrpbb4f99d2011-05-22 11:13:17 +00006961 {
cristy16ea1392012-03-21 20:38:41 +00006962 SetPixelAlpha(image,
6963 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006964 }
cristy3ed852e2009-09-05 21:47:34 +00006965 }
6966 }
glennrp47b9dd52010-11-24 18:12:06 +00006967
cristy3ed852e2009-09-05 21:47:34 +00006968 else /* if (magn_methx == 3 || magn_methx == 5) */
6969 {
6970 /* Replicate nearest */
6971 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006972 {
cristy16ea1392012-03-21 20:38:41 +00006973 SetPixelRed(image,GetPixelRed(image,pixels),q);
6974 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6975 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6976 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006977 }
glennrp47b9dd52010-11-24 18:12:06 +00006978
cristy3ed852e2009-09-05 21:47:34 +00006979 else
glennrpbb4f99d2011-05-22 11:13:17 +00006980 {
cristy16ea1392012-03-21 20:38:41 +00006981 SetPixelRed(image,GetPixelRed(image,n),q);
6982 SetPixelGreen(image,GetPixelGreen(image,n),q);
6983 SetPixelBlue(image,GetPixelBlue(image,n),q);
6984 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006985 }
glennrp47b9dd52010-11-24 18:12:06 +00006986
cristy3ed852e2009-09-05 21:47:34 +00006987 if (magn_methx == 5)
6988 {
6989 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006990 SetPixelAlpha(image,
6991 (QM) ((2*i*( GetPixelAlpha(image,n)
6992 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006993 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006994 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006995 }
6996 }
cristy16ea1392012-03-21 20:38:41 +00006997 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006998 }
cristy16ea1392012-03-21 20:38:41 +00006999 n+=GetPixelChannels(image);
7000 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00007001 }
glennrp47b9dd52010-11-24 18:12:06 +00007002
cristy3ed852e2009-09-05 21:47:34 +00007003 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7004 break;
7005 }
glennrp3faa9a32011-04-23 14:00:25 +00007006#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00007007 if (magn_methx != 1 || magn_methy != 1)
7008 {
7009 /*
7010 Rescale pixels to Quantum
7011 */
cristybb503372010-05-27 20:51:26 +00007012 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007013 {
7014 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00007015
cristybb503372010-05-27 20:51:26 +00007016 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00007017 {
cristy16ea1392012-03-21 20:38:41 +00007018 SetPixelRed(image,ScaleShortToQuantum(
7019 GetPixelRed(image,q)),q);
7020 SetPixelGreen(image,ScaleShortToQuantum(
7021 GetPixelGreen(image,q)),q);
7022 SetPixelBlue(image,ScaleShortToQuantum(
7023 GetPixelBlue(image,q)),q);
7024 SetPixelAlpha(image,ScaleShortToQuantum(
7025 GetPixelAlpha(image,q)),q);
7026 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00007027 }
glennrp47b9dd52010-11-24 18:12:06 +00007028
cristy3ed852e2009-09-05 21:47:34 +00007029 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7030 break;
7031 }
7032 }
7033#endif
7034 if (logging != MagickFalse)
7035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7036 " Finished MAGN processing");
7037 }
7038 }
7039
7040 /*
7041 Crop_box is with respect to the upper left corner of the MNG.
7042 */
7043 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7044 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7045 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7046 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7047 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7048 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7049 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7050 if ((crop_box.left != (mng_info->image_box.left
7051 +mng_info->x_off[object_id])) ||
7052 (crop_box.right != (mng_info->image_box.right
7053 +mng_info->x_off[object_id])) ||
7054 (crop_box.top != (mng_info->image_box.top
7055 +mng_info->y_off[object_id])) ||
7056 (crop_box.bottom != (mng_info->image_box.bottom
7057 +mng_info->y_off[object_id])))
7058 {
7059 if (logging != MagickFalse)
7060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7061 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00007062
cristy3ed852e2009-09-05 21:47:34 +00007063 if ((crop_box.left < crop_box.right) &&
7064 (crop_box.top < crop_box.bottom))
7065 {
7066 Image
7067 *im;
7068
7069 RectangleInfo
7070 crop_info;
7071
7072 /*
7073 Crop_info is with respect to the upper left corner of
7074 the image.
7075 */
7076 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7077 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00007078 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7079 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00007080 image->page.width=image->columns;
7081 image->page.height=image->rows;
7082 image->page.x=0;
7083 image->page.y=0;
7084 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00007085
cristy3ed852e2009-09-05 21:47:34 +00007086 if (im != (Image *) NULL)
7087 {
7088 image->columns=im->columns;
7089 image->rows=im->rows;
7090 im=DestroyImage(im);
7091 image->page.width=image->columns;
7092 image->page.height=image->rows;
7093 image->page.x=crop_box.left;
7094 image->page.y=crop_box.top;
7095 }
7096 }
glennrp47b9dd52010-11-24 18:12:06 +00007097
cristy3ed852e2009-09-05 21:47:34 +00007098 else
7099 {
7100 /*
7101 No pixels in crop area. The MNG spec still requires
7102 a layer, though, so make a single transparent pixel in
7103 the top left corner.
7104 */
7105 image->columns=1;
7106 image->rows=1;
7107 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00007108 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007109 image->page.width=1;
7110 image->page.height=1;
7111 image->page.x=0;
7112 image->page.y=0;
7113 }
7114 }
7115#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7116 image=mng_info->image;
7117#endif
7118 }
7119
glennrp2b013e42010-11-24 16:55:50 +00007120#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7121 /* PNG does not handle depths greater than 16 so reduce it even
cristy16ea1392012-03-21 20:38:41 +00007122 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00007123 */
7124 if (image->depth > 16)
7125 image->depth=16;
7126#endif
7127
glennrp3faa9a32011-04-23 14:00:25 +00007128#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00007129 if (image->depth > 8)
7130 {
7131 /* To do: fill low byte properly */
7132 image->depth=16;
7133 }
7134
cristy16ea1392012-03-21 20:38:41 +00007135 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007136 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00007137#endif
glennrpd6afd542010-11-19 01:53:05 +00007138
cristy3ed852e2009-09-05 21:47:34 +00007139 if (image_info->number_scenes != 0)
7140 {
7141 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00007142 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00007143 break;
7144 }
glennrpd6afd542010-11-19 01:53:05 +00007145
cristy3ed852e2009-09-05 21:47:34 +00007146 if (logging != MagickFalse)
7147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7148 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00007149
cristy3ed852e2009-09-05 21:47:34 +00007150 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00007151
cristy3ed852e2009-09-05 21:47:34 +00007152 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00007153
cristy3ed852e2009-09-05 21:47:34 +00007154 if (logging != MagickFalse)
7155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7156 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00007157
cristy3ed852e2009-09-05 21:47:34 +00007158#if defined(MNG_INSERT_LAYERS)
7159 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7160 (mng_info->mng_height))
7161 {
7162 /*
7163 Insert a background layer if nothing else was found.
7164 */
7165 if (logging != MagickFalse)
7166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7167 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00007168
cristy16ea1392012-03-21 20:38:41 +00007169 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00007170 {
7171 /*
7172 Allocate next image structure.
7173 */
cristy16ea1392012-03-21 20:38:41 +00007174 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007175 if (GetNextImageInList(image) == (Image *) NULL)
7176 {
7177 image=DestroyImageList(image);
7178 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00007179
cristy3ed852e2009-09-05 21:47:34 +00007180 if (logging != MagickFalse)
7181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7182 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00007183
cristy3ed852e2009-09-05 21:47:34 +00007184 return((Image *) NULL);
7185 }
7186 image=SyncNextImageInList(image);
7187 }
7188 image->columns=mng_info->mng_width;
7189 image->rows=mng_info->mng_height;
7190 image->page.width=mng_info->mng_width;
7191 image->page.height=mng_info->mng_height;
7192 image->page.x=0;
7193 image->page.y=0;
7194 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00007195 image->alpha_trait=UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00007196
cristy3ed852e2009-09-05 21:47:34 +00007197 if (image_info->ping == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00007198 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007199
cristy3ed852e2009-09-05 21:47:34 +00007200 mng_info->image_found++;
7201 }
7202#endif
7203 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00007204
cristy3ed852e2009-09-05 21:47:34 +00007205 if (mng_iterations == 1)
7206 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00007207
cristy3ed852e2009-09-05 21:47:34 +00007208 while (GetPreviousImageInList(image) != (Image *) NULL)
7209 {
7210 image_count++;
7211 if (image_count > 10*mng_info->image_found)
7212 {
7213 if (logging != MagickFalse)
7214 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00007215
cristy16ea1392012-03-21 20:38:41 +00007216 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007217 CoderError,"Linked list is corrupted, beginning of list not found",
7218 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007219
cristy3ed852e2009-09-05 21:47:34 +00007220 return((Image *) NULL);
7221 }
glennrp0fe50b42010-11-16 03:52:51 +00007222
cristy3ed852e2009-09-05 21:47:34 +00007223 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00007224
cristy3ed852e2009-09-05 21:47:34 +00007225 if (GetNextImageInList(image) == (Image *) NULL)
7226 {
7227 if (logging != MagickFalse)
7228 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00007229
cristy16ea1392012-03-21 20:38:41 +00007230 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007231 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7232 image_info->filename);
7233 }
7234 }
glennrp47b9dd52010-11-24 18:12:06 +00007235
cristy3ed852e2009-09-05 21:47:34 +00007236 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7237 GetNextImageInList(image) ==
7238 (Image *) NULL)
7239 {
7240 if (logging != MagickFalse)
7241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7242 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00007243
cristy16ea1392012-03-21 20:38:41 +00007244 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007245 CoderError,"image->next for first image is NULL but shouldn't be.",
7246 "`%s'",image_info->filename);
7247 }
glennrp47b9dd52010-11-24 18:12:06 +00007248
cristy3ed852e2009-09-05 21:47:34 +00007249 if (mng_info->image_found == 0)
7250 {
7251 if (logging != MagickFalse)
7252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7253 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00007254
cristy16ea1392012-03-21 20:38:41 +00007255 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007256 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007257
cristy3ed852e2009-09-05 21:47:34 +00007258 if (image != (Image *) NULL)
7259 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007260
cristy3ed852e2009-09-05 21:47:34 +00007261 MngInfoFreeStruct(mng_info,&have_mng_structure);
7262 return((Image *) NULL);
7263 }
7264
7265 if (mng_info->ticks_per_second)
7266 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7267 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00007268
cristy3ed852e2009-09-05 21:47:34 +00007269 else
7270 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00007271
cristy3ed852e2009-09-05 21:47:34 +00007272 /* Find final nonzero image delay */
7273 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00007274
cristy3ed852e2009-09-05 21:47:34 +00007275 while (GetNextImageInList(image) != (Image *) NULL)
7276 {
7277 if (image->delay)
7278 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00007279
cristy3ed852e2009-09-05 21:47:34 +00007280 image=GetNextImageInList(image);
7281 }
glennrp0fe50b42010-11-16 03:52:51 +00007282
cristy3ed852e2009-09-05 21:47:34 +00007283 if (final_delay < final_image_delay)
7284 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00007285
cristy3ed852e2009-09-05 21:47:34 +00007286 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00007287
cristy3ed852e2009-09-05 21:47:34 +00007288 if (logging != MagickFalse)
7289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007290 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7291 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00007292
cristy3ed852e2009-09-05 21:47:34 +00007293 if (logging != MagickFalse)
7294 {
7295 int
7296 scene;
7297
7298 scene=0;
7299 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007300
cristy3ed852e2009-09-05 21:47:34 +00007301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7302 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007303
cristy3ed852e2009-09-05 21:47:34 +00007304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007305 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00007306
cristy3ed852e2009-09-05 21:47:34 +00007307 while (GetNextImageInList(image) != (Image *) NULL)
7308 {
7309 image=GetNextImageInList(image);
7310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007311 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00007312 }
7313 }
7314
7315 image=GetFirstImageInList(image);
7316#ifdef MNG_COALESCE_LAYERS
7317 if (insert_layers)
7318 {
7319 Image
7320 *next_image,
7321 *next;
7322
cristybb503372010-05-27 20:51:26 +00007323 size_t
cristy3ed852e2009-09-05 21:47:34 +00007324 scene;
7325
7326 if (logging != MagickFalse)
7327 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00007328
cristy3ed852e2009-09-05 21:47:34 +00007329 scene=image->scene;
cristy16ea1392012-03-21 20:38:41 +00007330 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00007331
cristy3ed852e2009-09-05 21:47:34 +00007332 if (next_image == (Image *) NULL)
7333 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00007334
cristy3ed852e2009-09-05 21:47:34 +00007335 image=DestroyImageList(image);
7336 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00007337
cristy3ed852e2009-09-05 21:47:34 +00007338 for (next=image; next != (Image *) NULL; next=next_image)
7339 {
7340 next->page.width=mng_info->mng_width;
7341 next->page.height=mng_info->mng_height;
7342 next->page.x=0;
7343 next->page.y=0;
7344 next->scene=scene++;
7345 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00007346
cristy3ed852e2009-09-05 21:47:34 +00007347 if (next_image == (Image *) NULL)
7348 break;
glennrp47b9dd52010-11-24 18:12:06 +00007349
cristy3ed852e2009-09-05 21:47:34 +00007350 if (next->delay == 0)
7351 {
7352 scene--;
7353 next_image->previous=GetPreviousImageInList(next);
7354 if (GetPreviousImageInList(next) == (Image *) NULL)
7355 image=next_image;
7356 else
7357 next->previous->next=next_image;
7358 next=DestroyImage(next);
7359 }
7360 }
7361 }
7362#endif
7363
7364 while (GetNextImageInList(image) != (Image *) NULL)
7365 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007366
cristy3ed852e2009-09-05 21:47:34 +00007367 image->dispose=BackgroundDispose;
7368
7369 if (logging != MagickFalse)
7370 {
7371 int
7372 scene;
7373
7374 scene=0;
7375 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007376
cristy3ed852e2009-09-05 21:47:34 +00007377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7378 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007379
cristy3ed852e2009-09-05 21:47:34 +00007380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007381 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7382 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007383
cristy3ed852e2009-09-05 21:47:34 +00007384 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007385 {
7386 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007387
cristyf2faecf2010-05-28 19:19:36 +00007388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007389 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7390 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007391 }
7392 }
glennrp47b9dd52010-11-24 18:12:06 +00007393
cristy3ed852e2009-09-05 21:47:34 +00007394 image=GetFirstImageInList(image);
7395 MngInfoFreeStruct(mng_info,&have_mng_structure);
7396 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007397
cristy3ed852e2009-09-05 21:47:34 +00007398 if (logging != MagickFalse)
7399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007400
cristy3ed852e2009-09-05 21:47:34 +00007401 return(GetFirstImageInList(image));
7402}
glennrp25c1e2b2010-03-25 01:39:56 +00007403#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007404static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7405{
7406 printf("Your PNG library is too old: You have libpng-%s\n",
7407 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007408
cristy3ed852e2009-09-05 21:47:34 +00007409 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7410 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007411
cristy3ed852e2009-09-05 21:47:34 +00007412 return(Image *) NULL;
7413}
glennrp47b9dd52010-11-24 18:12:06 +00007414
cristy3ed852e2009-09-05 21:47:34 +00007415static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7416{
7417 return(ReadPNGImage(image_info,exception));
7418}
glennrp25c1e2b2010-03-25 01:39:56 +00007419#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007420#endif
7421
7422/*
7423%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7424% %
7425% %
7426% %
7427% R e g i s t e r P N G I m a g e %
7428% %
7429% %
7430% %
7431%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7432%
7433% RegisterPNGImage() adds properties for the PNG image format to
7434% the list of supported formats. The properties include the image format
7435% tag, a method to read and/or write the format, whether the format
7436% supports the saving of more than one frame to the same file or blob,
7437% whether the format supports native in-memory I/O, and a brief
7438% description of the format.
7439%
7440% The format of the RegisterPNGImage method is:
7441%
cristybb503372010-05-27 20:51:26 +00007442% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007443%
7444*/
cristybb503372010-05-27 20:51:26 +00007445ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007446{
7447 char
7448 version[MaxTextExtent];
7449
7450 MagickInfo
7451 *entry;
7452
7453 static const char
7454 *PNGNote=
7455 {
7456 "See http://www.libpng.org/ for details about the PNG format."
7457 },
glennrp47b9dd52010-11-24 18:12:06 +00007458
cristy3ed852e2009-09-05 21:47:34 +00007459 *JNGNote=
7460 {
7461 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7462 "format."
7463 },
glennrp47b9dd52010-11-24 18:12:06 +00007464
cristy3ed852e2009-09-05 21:47:34 +00007465 *MNGNote=
7466 {
7467 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7468 "format."
7469 };
7470
7471 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007472
cristy3ed852e2009-09-05 21:47:34 +00007473#if defined(PNG_LIBPNG_VER_STRING)
7474 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7475 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007476
cristy3ed852e2009-09-05 21:47:34 +00007477 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7478 {
7479 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7480 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7481 MaxTextExtent);
7482 }
7483#endif
glennrp47b9dd52010-11-24 18:12:06 +00007484
cristy3ed852e2009-09-05 21:47:34 +00007485 entry=SetMagickInfo("MNG");
7486 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007487
cristy3ed852e2009-09-05 21:47:34 +00007488#if defined(MAGICKCORE_PNG_DELEGATE)
7489 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7490 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7491#endif
glennrp47b9dd52010-11-24 18:12:06 +00007492
cristy3ed852e2009-09-05 21:47:34 +00007493 entry->magick=(IsImageFormatHandler *) IsMNG;
7494 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007495
cristy3ed852e2009-09-05 21:47:34 +00007496 if (*version != '\0')
7497 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007498
cristy3ed852e2009-09-05 21:47:34 +00007499 entry->module=ConstantString("PNG");
7500 entry->note=ConstantString(MNGNote);
7501 (void) RegisterMagickInfo(entry);
7502
7503 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007504
cristy3ed852e2009-09-05 21:47:34 +00007505#if defined(MAGICKCORE_PNG_DELEGATE)
7506 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7507 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7508#endif
glennrp47b9dd52010-11-24 18:12:06 +00007509
cristy3ed852e2009-09-05 21:47:34 +00007510 entry->magick=(IsImageFormatHandler *) IsPNG;
7511 entry->adjoin=MagickFalse;
7512 entry->description=ConstantString("Portable Network Graphics");
7513 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007514
cristy3ed852e2009-09-05 21:47:34 +00007515 if (*version != '\0')
7516 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007517
cristy3ed852e2009-09-05 21:47:34 +00007518 entry->note=ConstantString(PNGNote);
7519 (void) RegisterMagickInfo(entry);
7520
7521 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007522
cristy3ed852e2009-09-05 21:47:34 +00007523#if defined(MAGICKCORE_PNG_DELEGATE)
7524 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7525 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7526#endif
glennrp47b9dd52010-11-24 18:12:06 +00007527
cristy3ed852e2009-09-05 21:47:34 +00007528 entry->magick=(IsImageFormatHandler *) IsPNG;
7529 entry->adjoin=MagickFalse;
7530 entry->description=ConstantString(
7531 "8-bit indexed with optional binary transparency");
7532 entry->module=ConstantString("PNG");
7533 (void) RegisterMagickInfo(entry);
7534
7535 entry=SetMagickInfo("PNG24");
7536 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007537
cristy3ed852e2009-09-05 21:47:34 +00007538#if defined(ZLIB_VERSION)
7539 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7540 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007541
cristy3ed852e2009-09-05 21:47:34 +00007542 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7543 {
7544 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7545 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7546 }
7547#endif
glennrp47b9dd52010-11-24 18:12:06 +00007548
cristy3ed852e2009-09-05 21:47:34 +00007549 if (*version != '\0')
7550 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007551
cristy3ed852e2009-09-05 21:47:34 +00007552#if defined(MAGICKCORE_PNG_DELEGATE)
7553 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7554 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7555#endif
glennrp47b9dd52010-11-24 18:12:06 +00007556
cristy3ed852e2009-09-05 21:47:34 +00007557 entry->magick=(IsImageFormatHandler *) IsPNG;
7558 entry->adjoin=MagickFalse;
glennrpfd164d22013-01-26 21:10:22 +00007559 entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
cristy3ed852e2009-09-05 21:47:34 +00007560 entry->module=ConstantString("PNG");
7561 (void) RegisterMagickInfo(entry);
7562
7563 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007564
cristy3ed852e2009-09-05 21:47:34 +00007565#if defined(MAGICKCORE_PNG_DELEGATE)
7566 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7567 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7568#endif
glennrp47b9dd52010-11-24 18:12:06 +00007569
cristy3ed852e2009-09-05 21:47:34 +00007570 entry->magick=(IsImageFormatHandler *) IsPNG;
7571 entry->adjoin=MagickFalse;
7572 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7573 entry->module=ConstantString("PNG");
7574 (void) RegisterMagickInfo(entry);
7575
glennrpfd164d22013-01-26 21:10:22 +00007576 entry=SetMagickInfo("PNG48");
7577
7578#if defined(MAGICKCORE_PNG_DELEGATE)
7579 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7580 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7581#endif
7582
7583 entry->magick=(IsImageFormatHandler *) IsPNG;
7584 entry->adjoin=MagickFalse;
7585 entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7586 entry->module=ConstantString("PNG");
7587 (void) RegisterMagickInfo(entry);
7588
7589 entry=SetMagickInfo("PNG64");
7590
7591#if defined(MAGICKCORE_PNG_DELEGATE)
7592 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7593 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7594#endif
7595
7596 entry->magick=(IsImageFormatHandler *) IsPNG;
7597 entry->adjoin=MagickFalse;
7598 entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7599 entry->module=ConstantString("PNG");
7600 (void) RegisterMagickInfo(entry);
7601
glennrp5830fbc2013-01-27 06:11:45 +00007602 entry=SetMagickInfo("PNG00");
7603
7604#if defined(MAGICKCORE_PNG_DELEGATE)
7605 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7606 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7607#endif
7608
7609 entry->magick=(IsImageFormatHandler *) IsPNG;
7610 entry->adjoin=MagickFalse;
glennrp62708572013-02-15 12:51:48 +00007611 entry->description=ConstantString(
7612 "PNG inheriting bit-depth and color-type from original");
glennrp5830fbc2013-01-27 06:11:45 +00007613 entry->module=ConstantString("PNG");
7614 (void) RegisterMagickInfo(entry);
7615
cristy3ed852e2009-09-05 21:47:34 +00007616 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007617
cristy3ed852e2009-09-05 21:47:34 +00007618#if defined(JNG_SUPPORTED)
7619#if defined(MAGICKCORE_PNG_DELEGATE)
7620 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7621 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7622#endif
7623#endif
glennrp47b9dd52010-11-24 18:12:06 +00007624
cristy3ed852e2009-09-05 21:47:34 +00007625 entry->magick=(IsImageFormatHandler *) IsJNG;
7626 entry->adjoin=MagickFalse;
7627 entry->description=ConstantString("JPEG Network Graphics");
7628 entry->module=ConstantString("PNG");
7629 entry->note=ConstantString(JNGNote);
7630 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007631
glennrpedaa0382012-04-12 14:16:21 +00007632#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007633 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007634#endif
glennrp47b9dd52010-11-24 18:12:06 +00007635
cristy3ed852e2009-09-05 21:47:34 +00007636 return(MagickImageCoderSignature);
7637}
7638
7639/*
7640%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7641% %
7642% %
7643% %
7644% U n r e g i s t e r P N G I m a g e %
7645% %
7646% %
7647% %
7648%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7649%
7650% UnregisterPNGImage() removes format registrations made by the
7651% PNG module from the list of supported formats.
7652%
7653% The format of the UnregisterPNGImage method is:
7654%
7655% UnregisterPNGImage(void)
7656%
7657*/
7658ModuleExport void UnregisterPNGImage(void)
7659{
7660 (void) UnregisterMagickInfo("MNG");
7661 (void) UnregisterMagickInfo("PNG");
7662 (void) UnregisterMagickInfo("PNG8");
7663 (void) UnregisterMagickInfo("PNG24");
7664 (void) UnregisterMagickInfo("PNG32");
glennrpfd164d22013-01-26 21:10:22 +00007665 (void) UnregisterMagickInfo("PNG48");
7666 (void) UnregisterMagickInfo("PNG64");
glennrp5830fbc2013-01-27 06:11:45 +00007667 (void) UnregisterMagickInfo("PNG00");
cristy3ed852e2009-09-05 21:47:34 +00007668 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007669
glennrpedaa0382012-04-12 14:16:21 +00007670#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007671 if (ping_semaphore != (SemaphoreInfo *) NULL)
7672 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007673#endif
7674}
7675
7676#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007677#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007678/*
7679%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7680% %
7681% %
7682% %
7683% W r i t e M N G I m a g e %
7684% %
7685% %
7686% %
7687%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7688%
7689% WriteMNGImage() writes an image in the Portable Network Graphics
7690% Group's "Multiple-image Network Graphics" encoded image format.
7691%
7692% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7693%
7694% The format of the WriteMNGImage method is:
7695%
cristy16ea1392012-03-21 20:38:41 +00007696% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7697% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007698%
7699% A description of each parameter follows.
7700%
7701% o image_info: the image info.
7702%
7703% o image: The image.
7704%
cristy16ea1392012-03-21 20:38:41 +00007705% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007706%
7707% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7708% "To do" under ReadPNGImage):
7709%
cristy3ed852e2009-09-05 21:47:34 +00007710% Preserve all unknown and not-yet-handled known chunks found in input
7711% PNG file and copy them into output PNG files according to the PNG
7712% copying rules.
7713%
7714% Write the iCCP chunk at MNG level when (icc profile length > 0)
7715%
7716% Improve selection of color type (use indexed-colour or indexed-colour
7717% with tRNS when 256 or fewer unique RGBA values are present).
7718%
7719% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7720% This will be complicated if we limit ourselves to generating MNG-LC
7721% files. For now we ignore disposal method 3 and simply overlay the next
7722% image on it.
7723%
7724% Check for identical PLTE's or PLTE/tRNS combinations and use a
7725% global MNG PLTE or PLTE/tRNS combination when appropriate.
7726% [mostly done 15 June 1999 but still need to take care of tRNS]
7727%
7728% Check for identical sRGB and replace with a global sRGB (and remove
7729% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7730% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7731% local gAMA/cHRM with local sRGB if appropriate).
7732%
7733% Check for identical sBIT chunks and write global ones.
7734%
7735% Provide option to skip writing the signature tEXt chunks.
7736%
7737% Use signatures to detect identical objects and reuse the first
7738% instance of such objects instead of writing duplicate objects.
7739%
7740% Use a smaller-than-32k value of compression window size when
7741% appropriate.
7742%
7743% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7744% ancillary text chunks and save profiles.
7745%
7746% Provide an option to force LC files (to ensure exact framing rate)
7747% instead of VLC.
7748%
7749% Provide an option to force VLC files instead of LC, even when offsets
7750% are present. This will involve expanding the embedded images with a
7751% transparent region at the top and/or left.
7752*/
7753
cristy3ed852e2009-09-05 21:47:34 +00007754static void
glennrpcf002022011-01-30 02:38:15 +00007755Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007756 png_info *ping_info, unsigned char *profile_type, unsigned char
7757 *profile_description, unsigned char *profile_data, png_uint_32 length)
7758{
cristy3ed852e2009-09-05 21:47:34 +00007759 png_textp
7760 text;
7761
cristybb503372010-05-27 20:51:26 +00007762 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007763 i;
7764
7765 unsigned char
7766 *sp;
7767
7768 png_charp
7769 dp;
7770
7771 png_uint_32
7772 allocated_length,
7773 description_length;
7774
7775 unsigned char
7776 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007777
cristy3ed852e2009-09-05 21:47:34 +00007778 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7779 return;
7780
7781 if (image_info->verbose)
7782 {
glennrp0fe50b42010-11-16 03:52:51 +00007783 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7784 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007785 }
glennrp0fe50b42010-11-16 03:52:51 +00007786
glennrpecab7d72013-05-14 22:50:32 +00007787#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +00007788 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7789#else
7790 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7791#endif
cristy3ed852e2009-09-05 21:47:34 +00007792 description_length=(png_uint_32) strlen((const char *) profile_description);
7793 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7794 + description_length);
glennrpecab7d72013-05-14 22:50:32 +00007795#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +00007796 text[0].text=(png_charp) png_malloc(ping,
7797 (png_alloc_size_t) allocated_length);
7798 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7799#else
7800 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7801 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7802#endif
cristy3ed852e2009-09-05 21:47:34 +00007803 text[0].key[0]='\0';
7804 (void) ConcatenateMagickString(text[0].key,
7805 "Raw profile type ",MaxTextExtent);
7806 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7807 sp=profile_data;
7808 dp=text[0].text;
7809 *dp++='\n';
7810 (void) CopyMagickString(dp,(const char *) profile_description,
7811 allocated_length);
7812 dp+=description_length;
7813 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007814 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007815 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007816 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007817
cristybb503372010-05-27 20:51:26 +00007818 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007819 {
7820 if (i%36 == 0)
7821 *dp++='\n';
7822 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7823 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7824 }
glennrp47b9dd52010-11-24 18:12:06 +00007825
cristy3ed852e2009-09-05 21:47:34 +00007826 *dp++='\n';
7827 *dp='\0';
7828 text[0].text_length=(png_size_t) (dp-text[0].text);
7829 text[0].compression=image_info->compression == NoCompression ||
7830 (image_info->compression == UndefinedCompression &&
7831 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007832
cristy3ed852e2009-09-05 21:47:34 +00007833 if (text[0].text_length <= allocated_length)
7834 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007835
cristy3ed852e2009-09-05 21:47:34 +00007836 png_free(ping,text[0].text);
7837 png_free(ping,text[0].key);
7838 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007839}
7840
glennrpcf002022011-01-30 02:38:15 +00007841static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007842 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007843{
7844 char
7845 *name;
7846
7847 const StringInfo
7848 *profile;
7849
7850 unsigned char
7851 *data;
7852
7853 png_uint_32 length;
7854
7855 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007856
7857 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7858 {
cristy3ed852e2009-09-05 21:47:34 +00007859 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007860
cristy3ed852e2009-09-05 21:47:34 +00007861 if (profile != (const StringInfo *) NULL)
7862 {
7863 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007864 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007865
glennrp47b9dd52010-11-24 18:12:06 +00007866 if (LocaleNCompare(name,string,11) == 0)
7867 {
7868 if (logging != MagickFalse)
7869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7870 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007871
glennrpcf002022011-01-30 02:38:15 +00007872 ping_profile=CloneStringInfo(profile);
7873 data=GetStringInfoDatum(ping_profile),
7874 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007875 data[4]=data[3];
7876 data[3]=data[2];
7877 data[2]=data[1];
7878 data[1]=data[0];
7879 (void) WriteBlobMSBULong(image,length-5); /* data length */
7880 (void) WriteBlob(image,length-1,data+1);
7881 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007882 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007883 }
cristy3ed852e2009-09-05 21:47:34 +00007884 }
glennrp47b9dd52010-11-24 18:12:06 +00007885
cristy3ed852e2009-09-05 21:47:34 +00007886 name=GetNextImageProfile(image);
7887 }
glennrp47b9dd52010-11-24 18:12:06 +00007888
cristy3ed852e2009-09-05 21:47:34 +00007889 return(MagickTrue);
7890}
7891
glennrpb9cfe272010-12-21 15:08:06 +00007892
cristy3ed852e2009-09-05 21:47:34 +00007893/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007894static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +00007895 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007896{
cristy09973322013-06-30 21:06:30 +00007897 char
7898 im_vers[32],
7899 libpng_runv[32],
7900 libpng_vers[32],
7901 zlib_runv[32],
7902 zlib_vers[32];
7903
cristy16ea1392012-03-21 20:38:41 +00007904 Image
7905 *image;
7906
7907 ImageInfo
7908 *image_info;
7909
cristy3ed852e2009-09-05 21:47:34 +00007910 char
7911 s[2];
7912
7913 const char
7914 *name,
7915 *property,
7916 *value;
7917
7918 const StringInfo
7919 *profile;
7920
cristy3ed852e2009-09-05 21:47:34 +00007921 int
cristy3ed852e2009-09-05 21:47:34 +00007922 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007923 pass;
7924
glennrpe9c26dc2010-05-30 01:56:35 +00007925 png_byte
7926 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007927
glennrp39992b42010-11-14 00:03:43 +00007928 png_color
7929 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007930
glennrp5af765f2010-03-30 11:12:18 +00007931 png_color_16
7932 ping_background,
7933 ping_trans_color;
7934
cristy3ed852e2009-09-05 21:47:34 +00007935 png_info
7936 *ping_info;
7937
7938 png_struct
7939 *ping;
7940
glennrp5af765f2010-03-30 11:12:18 +00007941 png_uint_32
7942 ping_height,
7943 ping_width;
7944
cristybb503372010-05-27 20:51:26 +00007945 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007946 y;
7947
7948 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007949 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007950 logging,
glennrp58e01762011-01-07 15:28:54 +00007951 matte,
7952
glennrpda8f3a72011-02-27 23:54:12 +00007953 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007954 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007955 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007956 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007957 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007958 ping_have_bKGD,
glennrp918b9dc2013-04-03 13:41:41 +00007959 ping_have_iCCP,
glennrp991d11d2010-11-12 21:55:28 +00007960 ping_have_pHYs,
glennrp918b9dc2013-04-03 13:41:41 +00007961 ping_have_sRGB,
glennrp991d11d2010-11-12 21:55:28 +00007962 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007963
7964 ping_exclude_bKGD,
7965 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007966 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007967 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007968 ping_exclude_gAMA,
7969 ping_exclude_iCCP,
7970 /* ping_exclude_iTXt, */
7971 ping_exclude_oFFs,
7972 ping_exclude_pHYs,
7973 ping_exclude_sRGB,
7974 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007975 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007976 ping_exclude_vpAg,
7977 ping_exclude_zCCP, /* hex-encoded iCCP */
7978 ping_exclude_zTXt,
7979
glennrp8d3d6e52011-04-19 04:39:51 +00007980 ping_preserve_colormap,
glennrpecab7d72013-05-14 22:50:32 +00007981 ping_preserve_iCCP,
glennrp0e8ea192010-12-24 18:00:33 +00007982 ping_need_colortype_warning,
7983
glennrp82b3c532011-03-22 19:20:54 +00007984 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007985 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007986 tried_333,
7987 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007988
cristy09973322013-06-30 21:06:30 +00007989 MemoryInfo
cristyaf1534a2013-06-30 21:12:25 +00007990 *volatile pixel_info;
cristy09973322013-06-30 21:06:30 +00007991
cristy3ed852e2009-09-05 21:47:34 +00007992 QuantumInfo
7993 *quantum_info;
7994
cristy16ea1392012-03-21 20:38:41 +00007995 PNGErrorInfo
7996 error_info;
7997
cristybb503372010-05-27 20:51:26 +00007998 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007999 i,
8000 x;
8001
8002 unsigned char
cristy09973322013-06-30 21:06:30 +00008003 *ping_pixels;
glennrpd0cae252013-03-15 22:30:41 +00008004
glennrp5af765f2010-03-30 11:12:18 +00008005 volatile int
glennrpf09bded2011-01-08 01:15:59 +00008006 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00008007 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00008008 ping_color_type,
8009 ping_interlace_method,
8010 ping_compression_method,
8011 ping_filter_method,
8012 ping_num_trans;
8013
cristybb503372010-05-27 20:51:26 +00008014 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00008015 image_depth,
8016 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008017
cristybb503372010-05-27 20:51:26 +00008018 size_t
cristy3ed852e2009-09-05 21:47:34 +00008019 quality,
8020 rowbytes,
8021 save_image_depth;
8022
glennrpdfd70802010-11-14 01:23:35 +00008023 int
glennrpfd05d622011-02-25 04:10:33 +00008024 j,
glennrpf09bded2011-01-08 01:15:59 +00008025 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00008026 number_opaque,
8027 number_semitransparent,
8028 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00008029 ping_pHYs_unit_type;
8030
8031 png_uint_32
8032 ping_pHYs_x_resolution,
8033 ping_pHYs_y_resolution;
8034
cristy3ed852e2009-09-05 21:47:34 +00008035 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00008036 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00008037
cristy16ea1392012-03-21 20:38:41 +00008038 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8039 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8040 if (image_info == (ImageInfo *) NULL)
8041 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00008042
glennrpd0cae252013-03-15 22:30:41 +00008043 /* Define these outside of the following "if logging()" block so they will
8044 * show in debuggers.
8045 */
8046 *im_vers='\0';
8047 (void) ConcatenateMagickString(im_vers,
8048 MagickLibVersionText,MaxTextExtent);
8049 (void) ConcatenateMagickString(im_vers,
8050 MagickLibAddendum,MaxTextExtent);
glennrpec0ddbc2013-03-16 12:40:12 +00008051
glennrpd0cae252013-03-15 22:30:41 +00008052 *libpng_vers='\0';
8053 (void) ConcatenateMagickString(libpng_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00008054 PNG_LIBPNG_VER_STRING,32);
8055 *libpng_runv='\0';
8056 (void) ConcatenateMagickString(libpng_runv,
8057 png_get_libpng_ver(NULL),32);
8058
glennrpd0cae252013-03-15 22:30:41 +00008059 *zlib_vers='\0';
8060 (void) ConcatenateMagickString(zlib_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00008061 ZLIB_VERSION,32);
8062 *zlib_runv='\0';
8063 (void) ConcatenateMagickString(zlib_runv,
8064 zlib_version,32);
8065
glennrpd0cae252013-03-15 22:30:41 +00008066 if (logging)
8067 {
8068 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8069 im_vers);
8070 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8071 libpng_vers);
glennrpec0ddbc2013-03-16 12:40:12 +00008072 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8073 {
8074 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8075 libpng_runv);
8076 }
glennrpd0cae252013-03-15 22:30:41 +00008077 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8078 zlib_vers);
glennrpec0ddbc2013-03-16 12:40:12 +00008079 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8080 {
8081 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8082 zlib_runv);
8083 }
glennrpd0cae252013-03-15 22:30:41 +00008084 }
8085
glennrp5af765f2010-03-30 11:12:18 +00008086 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00008087 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00008088 ping_color_type=0,
8089 ping_interlace_method=0,
8090 ping_compression_method=0,
8091 ping_filter_method=0,
8092 ping_num_trans = 0;
8093
8094 ping_background.red = 0;
8095 ping_background.green = 0;
8096 ping_background.blue = 0;
8097 ping_background.gray = 0;
8098 ping_background.index = 0;
8099
8100 ping_trans_color.red=0;
8101 ping_trans_color.green=0;
8102 ping_trans_color.blue=0;
8103 ping_trans_color.gray=0;
8104
glennrpdfd70802010-11-14 01:23:35 +00008105 ping_pHYs_unit_type = 0;
8106 ping_pHYs_x_resolution = 0;
8107 ping_pHYs_y_resolution = 0;
8108
glennrpda8f3a72011-02-27 23:54:12 +00008109 ping_have_blob=MagickFalse;
glennrpf70c4d22013-03-19 15:26:48 +00008110 ping_have_cheap_transparency=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00008111 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00008112 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00008113 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00008114 ping_have_bKGD=MagickFalse;
glennrp918b9dc2013-04-03 13:41:41 +00008115 ping_have_iCCP=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00008116 ping_have_pHYs=MagickFalse;
glennrp918b9dc2013-04-03 13:41:41 +00008117 ping_have_sRGB=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00008118 ping_have_tRNS=MagickFalse;
8119
glennrp0e8ea192010-12-24 18:00:33 +00008120 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8121 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00008122 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00008123 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00008124 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00008125 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8126 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8127 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8128 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8129 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8130 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00008131 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00008132 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8133 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8134 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8135
glennrp8d3d6e52011-04-19 04:39:51 +00008136 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrpecab7d72013-05-14 22:50:32 +00008137 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
glennrp0e8ea192010-12-24 18:00:33 +00008138 ping_need_colortype_warning = MagickFalse;
8139
cristy0d57eec2011-09-04 22:13:56 +00008140 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8141 * i.e., eliminate the ICC profile and set image->rendering_intent.
8142 * Note that this will not involve any changes to the actual pixels
8143 * but merely passes information to applications that read the resulting
8144 * PNG image.
glennrpecab7d72013-05-14 22:50:32 +00008145 *
8146 * To do: recognize other variants of the sRGB profile, using the CRC to
8147 * verify all recognized variants including the 7 already known.
8148 *
8149 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8150 *
8151 * Use something other than image->rendering_intent to record the fact
8152 * that the sRGB profile was found.
8153 *
8154 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8155 * profile. Record the Blackpoint Compensation, if any.
cristy0d57eec2011-09-04 22:13:56 +00008156 */
glennrpecab7d72013-05-14 22:50:32 +00008157 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
cristy0d57eec2011-09-04 22:13:56 +00008158 {
8159 char
8160 *name;
8161
8162 const StringInfo
8163 *profile;
8164
8165 ResetImageProfileIterator(image);
8166 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8167 {
8168 profile=GetImageProfile(image,name);
8169
8170 if (profile != (StringInfo *) NULL)
8171 {
8172 if ((LocaleCompare(name,"ICC") == 0) ||
glennrpecab7d72013-05-14 22:50:32 +00008173 (LocaleCompare(name,"ICM") == 0))
glennrpee7b4c02011-10-04 01:21:09 +00008174
glennrpecab7d72013-05-14 22:50:32 +00008175 {
8176 int
8177 icheck,
8178 got_crc=0;
8179
glennrpee7b4c02011-10-04 01:21:09 +00008180
8181 png_uint_32
8182 length,
glennrpecab7d72013-05-14 22:50:32 +00008183 profile_crc=0;
glennrpee7b4c02011-10-04 01:21:09 +00008184
cristy0d57eec2011-09-04 22:13:56 +00008185 unsigned char
8186 *data;
8187
glennrp29a106e2011-09-06 17:11:42 +00008188 length=(png_uint_32) GetStringInfoLength(profile);
8189
glennrpecab7d72013-05-14 22:50:32 +00008190 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
cristy0d57eec2011-09-04 22:13:56 +00008191 {
glennrpecab7d72013-05-14 22:50:32 +00008192 if (length == sRGB_info[icheck].len)
glennrp29a106e2011-09-06 17:11:42 +00008193 {
glennrpecab7d72013-05-14 22:50:32 +00008194 if (got_crc == 0)
8195 {
8196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00008197 " Got a %lu-byte ICC profile (potentially sRGB)",
8198 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00008199
glennrpecab7d72013-05-14 22:50:32 +00008200 data=GetStringInfoDatum(profile);
8201 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00008202
glennrpecab7d72013-05-14 22:50:32 +00008203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8204 " with crc=%8x",(unsigned int) profile_crc);
8205 got_crc++;
8206 }
glennrpee7b4c02011-10-04 01:21:09 +00008207
glennrpecab7d72013-05-14 22:50:32 +00008208 if (profile_crc == sRGB_info[icheck].crc)
glennrpee7b4c02011-10-04 01:21:09 +00008209 {
8210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpecab7d72013-05-14 22:50:32 +00008211 " It is sRGB with rendering intent = %s",
8212 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8213 sRGB_info[icheck].intent));
glennrpee7b4c02011-10-04 01:21:09 +00008214 if (image->rendering_intent==UndefinedIntent)
glennrpecab7d72013-05-14 22:50:32 +00008215 {
8216 image->rendering_intent=
8217 Magick_RenderingIntent_from_PNG_RenderingIntent(
8218 sRGB_info[icheck].intent);
8219 }
8220 ping_exclude_iCCP = MagickTrue;
8221 ping_exclude_zCCP = MagickTrue;
8222 ping_have_sRGB = MagickTrue;
glennrpee7b4c02011-10-04 01:21:09 +00008223 break;
8224 }
glennrp29a106e2011-09-06 17:11:42 +00008225 }
glennrp29a106e2011-09-06 17:11:42 +00008226 }
glennrpecab7d72013-05-14 22:50:32 +00008227 if (sRGB_info[icheck].len == 0)
glennrp29a106e2011-09-06 17:11:42 +00008228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpecab7d72013-05-14 22:50:32 +00008229 " Got a %lu-byte ICC profile not recognized as sRGB",
glennrp29a106e2011-09-06 17:11:42 +00008230 (unsigned long) length);
8231 }
cristy0d57eec2011-09-04 22:13:56 +00008232 }
8233 name=GetNextImageProfile(image);
8234 }
8235 }
8236
glennrp8bb3a022010-12-13 20:40:04 +00008237 number_opaque = 0;
8238 number_semitransparent = 0;
8239 number_transparent = 0;
8240
glennrpfd05d622011-02-25 04:10:33 +00008241 if (logging != MagickFalse)
8242 {
8243 if (image->storage_class == UndefinedClass)
8244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8245 " storage_class=UndefinedClass");
8246 if (image->storage_class == DirectClass)
8247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8248 " storage_class=DirectClass");
8249 if (image->storage_class == PseudoClass)
8250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8251 " storage_class=PseudoClass");
8252 }
glennrp28af3712011-04-06 18:07:30 +00008253
glennrp750105b2012-04-25 16:20:45 +00008254 if (image->storage_class == PseudoClass &&
glennrp7e65e932011-08-19 02:31:16 +00008255 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
glennrpfd164d22013-01-26 21:10:22 +00008256 mng_info->write_png48 || mng_info->write_png64 ||
8257 (mng_info->write_png_colortype != 1 &&
8258 mng_info->write_png_colortype != 5)))
glennrp7e65e932011-08-19 02:31:16 +00008259 {
cristy16ea1392012-03-21 20:38:41 +00008260 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00008261 image->storage_class = DirectClass;
8262 }
8263
glennrpc6c391a2011-04-27 02:23:56 +00008264 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00008265 {
glennrpc6c391a2011-04-27 02:23:56 +00008266 if (image->storage_class != PseudoClass && image->colormap != NULL)
8267 {
8268 /* Free the bogus colormap; it can cause trouble later */
8269 if (logging != MagickFalse)
8270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8271 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00008272 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00008273 image->colormap=NULL;
8274 }
glennrp28af3712011-04-06 18:07:30 +00008275 }
glennrpbb4f99d2011-05-22 11:13:17 +00008276
cristy3d9f5ba2012-06-26 13:37:31 +00008277 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00008278 (void) TransformImageColorspace(image,sRGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00008279
glennrp3241bd02010-12-12 04:36:28 +00008280 /*
8281 Sometimes we get PseudoClass images whose RGB values don't match
8282 the colors in the colormap. This code syncs the RGB values.
8283 */
8284 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristy16ea1392012-03-21 20:38:41 +00008285 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00008286
glennrpa6a06632011-01-19 15:15:34 +00008287#if (MAGICKCORE_QUANTUM_DEPTH == 8)
8288 if (image->depth > 8)
8289 {
8290 if (logging != MagickFalse)
8291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8292 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8293
8294 image->depth=8;
8295 }
8296#endif
8297
glennrp8e58efd2011-05-20 12:16:29 +00008298 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00008299 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
8300 {
cristy16ea1392012-03-21 20:38:41 +00008301 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00008302 *r;
8303
glennrp8e58efd2011-05-20 12:16:29 +00008304 if (image->depth > 8)
8305 {
8306#if MAGICKCORE_QUANTUM_DEPTH > 16
8307 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00008308 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008309
8310 for (y=0; y < (ssize_t) image->rows; y++)
8311 {
cristy16ea1392012-03-21 20:38:41 +00008312 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008313
cristy16ea1392012-03-21 20:38:41 +00008314 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008315 break;
8316
8317 for (x=0; x < (ssize_t) image->columns; x++)
8318 {
cristy16ea1392012-03-21 20:38:41 +00008319 LBR16PixelRGBA(r);
8320 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008321 }
glennrpbb4f99d2011-05-22 11:13:17 +00008322
glennrp8e58efd2011-05-20 12:16:29 +00008323 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8324 break;
8325 }
8326
8327 if (image->storage_class == PseudoClass && image->colormap != NULL)
8328 {
cristy3e08f112011-05-24 13:19:30 +00008329 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008330 {
glennrp91d99252011-06-25 14:30:13 +00008331 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008332 }
8333 }
8334#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8335 }
8336
8337 else if (image->depth > 4)
8338 {
8339#if MAGICKCORE_QUANTUM_DEPTH > 8
8340 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00008341 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008342
8343 for (y=0; y < (ssize_t) image->rows; y++)
8344 {
cristy16ea1392012-03-21 20:38:41 +00008345 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008346
cristy16ea1392012-03-21 20:38:41 +00008347 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008348 break;
8349
8350 for (x=0; x < (ssize_t) image->columns; x++)
8351 {
cristy16ea1392012-03-21 20:38:41 +00008352 LBR08PixelRGBA(r);
8353 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008354 }
glennrpbb4f99d2011-05-22 11:13:17 +00008355
glennrp8e58efd2011-05-20 12:16:29 +00008356 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8357 break;
8358 }
8359
8360 if (image->storage_class == PseudoClass && image->colormap != NULL)
8361 {
cristy3e08f112011-05-24 13:19:30 +00008362 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008363 {
glennrp91d99252011-06-25 14:30:13 +00008364 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008365 }
8366 }
8367#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8368 }
8369 else
8370 if (image->depth > 2)
8371 {
8372 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00008373 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008374
8375 for (y=0; y < (ssize_t) image->rows; y++)
8376 {
cristy16ea1392012-03-21 20:38:41 +00008377 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008378
cristy16ea1392012-03-21 20:38:41 +00008379 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008380 break;
8381
8382 for (x=0; x < (ssize_t) image->columns; x++)
8383 {
cristy16ea1392012-03-21 20:38:41 +00008384 LBR04PixelRGBA(r);
8385 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008386 }
glennrpbb4f99d2011-05-22 11:13:17 +00008387
glennrp8e58efd2011-05-20 12:16:29 +00008388 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8389 break;
8390 }
8391
8392 if (image->storage_class == PseudoClass && image->colormap != NULL)
8393 {
cristy3e08f112011-05-24 13:19:30 +00008394 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008395 {
glennrp91d99252011-06-25 14:30:13 +00008396 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008397 }
8398 }
8399 }
8400
8401 else if (image->depth > 1)
8402 {
8403 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00008404 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008405
8406 for (y=0; y < (ssize_t) image->rows; y++)
8407 {
cristy16ea1392012-03-21 20:38:41 +00008408 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008409
cristy16ea1392012-03-21 20:38:41 +00008410 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008411 break;
8412
8413 for (x=0; x < (ssize_t) image->columns; x++)
8414 {
cristy16ea1392012-03-21 20:38:41 +00008415 LBR02PixelRGBA(r);
8416 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008417 }
glennrpbb4f99d2011-05-22 11:13:17 +00008418
glennrp8e58efd2011-05-20 12:16:29 +00008419 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8420 break;
8421 }
8422
8423 if (image->storage_class == PseudoClass && image->colormap != NULL)
8424 {
cristy3e08f112011-05-24 13:19:30 +00008425 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008426 {
glennrp91d99252011-06-25 14:30:13 +00008427 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008428 }
8429 }
8430 }
8431 else
8432 {
8433 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00008434 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008435
8436 for (y=0; y < (ssize_t) image->rows; y++)
8437 {
cristy16ea1392012-03-21 20:38:41 +00008438 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008439
cristy16ea1392012-03-21 20:38:41 +00008440 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008441 break;
8442
8443 for (x=0; x < (ssize_t) image->columns; x++)
8444 {
cristy16ea1392012-03-21 20:38:41 +00008445 LBR01PixelRGBA(r);
8446 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008447 }
glennrpbb4f99d2011-05-22 11:13:17 +00008448
glennrp8e58efd2011-05-20 12:16:29 +00008449 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8450 break;
8451 }
8452
8453 if (image->storage_class == PseudoClass && image->colormap != NULL)
8454 {
cristy3e08f112011-05-24 13:19:30 +00008455 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008456 {
glennrp91d99252011-06-25 14:30:13 +00008457 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008458 }
8459 }
8460 }
glennrp9d0ea4d2011-04-22 18:35:57 +00008461 }
8462
glennrp67b9c1a2011-04-22 18:47:36 +00008463 /* To do: set to next higher multiple of 8 */
8464 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00008465 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00008466
glennrp2b013e42010-11-24 16:55:50 +00008467#if (MAGICKCORE_QUANTUM_DEPTH > 16)
8468 /* PNG does not handle depths greater than 16 so reduce it even
8469 * if lossy
8470 */
glennrp8e58efd2011-05-20 12:16:29 +00008471 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00008472 image->depth=16;
8473#endif
8474
glennrp3faa9a32011-04-23 14:00:25 +00008475#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00008476 if (image->depth > 8)
8477 {
8478 /* To do: fill low byte properly */
8479 image->depth=16;
8480 }
8481
glennrpc722dd82011-02-24 05:13:21 +00008482 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristy16ea1392012-03-21 20:38:41 +00008483 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00008484 image->depth = 8;
8485#endif
8486
glennrpa8036d62012-11-04 01:46:06 +00008487 if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8488 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8489 mng_info->write_png_colortype < 4 &&
8490 image->alpha_trait != BlendPixelTrait)))
8491 {
8492 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8493 * are not going to need the result.
8494 */
glennrp2feb1412013-01-22 15:02:16 +00008495 image_colors = (int) image->colors;
8496 number_opaque = (int) image->colors;
glennrpa8036d62012-11-04 01:46:06 +00008497 if (mng_info->write_png_colortype == 1 ||
8498 mng_info->write_png_colortype == 5)
8499 ping_have_color=MagickFalse;
8500 else
8501 ping_have_color=MagickTrue;
8502 ping_have_non_bw=MagickFalse;
8503
8504 if (image->alpha_trait == BlendPixelTrait)
8505 {
8506 number_transparent = 2;
8507 number_semitransparent = 1;
8508 }
8509
8510 else
8511 {
8512 number_transparent = 0;
8513 number_semitransparent = 0;
8514 }
8515 }
8516
8517 else
8518 {
8519 /* BUILD_PALETTE
8520 *
8521 * Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00008522 * we reduce the transparency to binary and run again, then if there
8523 * 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 +00008524 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8525 * palette. Then (To do) we take care of a final reduction that is only
8526 * needed if there are still 256 colors present and one of them has both
8527 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00008528 */
glennrp82b3c532011-03-22 19:20:54 +00008529
glennrp8ca51ad2011-05-12 21:22:32 +00008530 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008531 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00008532 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008533
glennrp8ca51ad2011-05-12 21:22:32 +00008534 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00008535 {
glennrpa8036d62012-11-04 01:46:06 +00008536 /*
glennrpd71e86a2011-02-24 01:28:37 +00008537 * Sometimes we get DirectClass images that have 256 colors or fewer.
8538 * This code will build a colormap.
8539 *
8540 * Also, sometimes we get PseudoClass images with an out-of-date
8541 * colormap. This code will replace the colormap with a new one.
8542 * Sometimes we get PseudoClass images that have more than 256 colors.
8543 * This code will delete the colormap and change the image to
8544 * DirectClass.
8545 *
cristy8a46d822012-08-28 23:32:39 +00008546 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008547 * even though it sometimes contains left-over non-opaque values.
8548 *
8549 * Also we gather some information (number of opaque, transparent,
8550 * and semitransparent pixels, and whether the image has any non-gray
8551 * pixels or only black-and-white pixels) that we might need later.
8552 *
8553 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8554 * we need to check for bogus non-opaque values, at least.
8555 */
glennrp3c218112010-11-27 15:31:26 +00008556
glennrpd71e86a2011-02-24 01:28:37 +00008557 int
8558 n;
glennrp3c218112010-11-27 15:31:26 +00008559
cristy16ea1392012-03-21 20:38:41 +00008560 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008561 opaque[260],
8562 semitransparent[260],
8563 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008564
cristy16ea1392012-03-21 20:38:41 +00008565 register const Quantum
8566 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008567
cristy16ea1392012-03-21 20:38:41 +00008568 register Quantum
8569 *q,
glennrpfd05d622011-02-25 04:10:33 +00008570 *r;
8571
glennrpd71e86a2011-02-24 01:28:37 +00008572 if (logging != MagickFalse)
8573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8574 " Enter BUILD_PALETTE:");
8575
8576 if (logging != MagickFalse)
8577 {
glennrp03812ae2010-12-24 01:31:34 +00008578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008579 " image->columns=%.20g",(double) image->columns);
8580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8581 " image->rows=%.20g",(double) image->rows);
8582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00008583 " image->alpha_trait=%.20g",(double) image->alpha_trait);
glennrpd71e86a2011-02-24 01:28:37 +00008584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8585 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008586
glennrpfd05d622011-02-25 04:10:33 +00008587 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008588 {
8589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008590 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008592 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008593
glennrpd71e86a2011-02-24 01:28:37 +00008594 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008595 {
glennrpd71e86a2011-02-24 01:28:37 +00008596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8597 " %d (%d,%d,%d,%d)",
8598 (int) i,
8599 (int) image->colormap[i].red,
8600 (int) image->colormap[i].green,
8601 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008602 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008603 }
glennrp2cc891a2010-12-24 13:44:32 +00008604
glennrpd71e86a2011-02-24 01:28:37 +00008605 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8606 {
8607 if (i > 255)
8608 {
8609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8610 " %d (%d,%d,%d,%d)",
8611 (int) i,
8612 (int) image->colormap[i].red,
8613 (int) image->colormap[i].green,
8614 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008615 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008616 }
8617 }
glennrp03812ae2010-12-24 01:31:34 +00008618 }
glennrp7ddcc222010-12-11 05:01:05 +00008619
glennrpd71e86a2011-02-24 01:28:37 +00008620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8621 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008622
glennrpd71e86a2011-02-24 01:28:37 +00008623 if (image->colors == 0)
cristy16ea1392012-03-21 20:38:41 +00008624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8625 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008626
glennrp8d3d6e52011-04-19 04:39:51 +00008627 if (ping_preserve_colormap == MagickFalse)
8628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8629 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008630 }
8631
glennrpd71e86a2011-02-24 01:28:37 +00008632 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008633 number_opaque = 0;
8634 number_semitransparent = 0;
8635 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008636
8637 for (y=0; y < (ssize_t) image->rows; y++)
8638 {
8639 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8640
cristy16ea1392012-03-21 20:38:41 +00008641 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008642 break;
8643
8644 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008645 {
cristy8a46d822012-08-28 23:32:39 +00008646 if (image->alpha_trait != BlendPixelTrait ||
cristy16ea1392012-03-21 20:38:41 +00008647 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008648 {
8649 if (number_opaque < 259)
8650 {
8651 if (number_opaque == 0)
8652 {
cristy16ea1392012-03-21 20:38:41 +00008653 GetPixelInfoPixel(image, q, opaque);
8654 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008655 number_opaque=1;
8656 }
glennrp2cc891a2010-12-24 13:44:32 +00008657
glennrpd71e86a2011-02-24 01:28:37 +00008658 for (i=0; i< (ssize_t) number_opaque; i++)
8659 {
cristy16ea1392012-03-21 20:38:41 +00008660 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008661 break;
8662 }
glennrp7ddcc222010-12-11 05:01:05 +00008663
cristy16ea1392012-03-21 20:38:41 +00008664 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008665 {
8666 number_opaque++;
cristy16ea1392012-03-21 20:38:41 +00008667 GetPixelInfoPixel(image, q, opaque+i);
8668 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008669 }
8670 }
8671 }
cristy16ea1392012-03-21 20:38:41 +00008672 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008673 {
8674 if (number_transparent < 259)
8675 {
8676 if (number_transparent == 0)
8677 {
cristy16ea1392012-03-21 20:38:41 +00008678 GetPixelInfoPixel(image, q, transparent);
8679 ping_trans_color.red=(unsigned short)
8680 GetPixelRed(image,q);
8681 ping_trans_color.green=(unsigned short)
8682 GetPixelGreen(image,q);
8683 ping_trans_color.blue=(unsigned short)
8684 GetPixelBlue(image,q);
8685 ping_trans_color.gray=(unsigned short)
cristy972d1c42012-07-14 23:29:14 +00008686 GetPixelGray(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008687 number_transparent = 1;
8688 }
8689
8690 for (i=0; i< (ssize_t) number_transparent; i++)
8691 {
cristy16ea1392012-03-21 20:38:41 +00008692 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008693 break;
8694 }
8695
8696 if (i == (ssize_t) number_transparent &&
8697 number_transparent < 259)
8698 {
8699 number_transparent++;
cristy16ea1392012-03-21 20:38:41 +00008700 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008701 }
8702 }
8703 }
8704 else
8705 {
8706 if (number_semitransparent < 259)
8707 {
8708 if (number_semitransparent == 0)
8709 {
cristy16ea1392012-03-21 20:38:41 +00008710 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008711 number_semitransparent = 1;
8712 }
8713
8714 for (i=0; i< (ssize_t) number_semitransparent; i++)
8715 {
cristy16ea1392012-03-21 20:38:41 +00008716 if (IsPixelEquivalent(image,q, semitransparent+i)
8717 && GetPixelAlpha(image,q) ==
8718 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008719 break;
8720 }
8721
8722 if (i == (ssize_t) number_semitransparent &&
8723 number_semitransparent < 259)
8724 {
8725 number_semitransparent++;
cristy16ea1392012-03-21 20:38:41 +00008726 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008727 }
8728 }
8729 }
cristy16ea1392012-03-21 20:38:41 +00008730 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008731 }
8732 }
8733
cristy4054bfb2011-08-29 23:41:39 +00008734 if (mng_info->write_png8 == MagickFalse &&
8735 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008736 {
8737 /* Add the background color to the palette, if it
8738 * isn't already there.
8739 */
glennrpc6c391a2011-04-27 02:23:56 +00008740 if (logging != MagickFalse)
8741 {
8742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8743 " Check colormap for background (%d,%d,%d)",
8744 (int) image->background_color.red,
8745 (int) image->background_color.green,
8746 (int) image->background_color.blue);
8747 }
glennrpd71e86a2011-02-24 01:28:37 +00008748 for (i=0; i<number_opaque; i++)
8749 {
glennrpca7ad3a2011-04-26 04:44:54 +00008750 if (opaque[i].red == image->background_color.red &&
8751 opaque[i].green == image->background_color.green &&
8752 opaque[i].blue == image->background_color.blue)
8753 break;
glennrpd71e86a2011-02-24 01:28:37 +00008754 }
glennrpd71e86a2011-02-24 01:28:37 +00008755 if (number_opaque < 259 && i == number_opaque)
8756 {
glennrp8e045c82011-04-27 16:40:27 +00008757 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008758 ping_background.index = i;
glennrp388a8c82012-06-27 13:41:51 +00008759 number_opaque++;
glennrpc6c391a2011-04-27 02:23:56 +00008760 if (logging != MagickFalse)
8761 {
8762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8763 " background_color index is %d",(int) i);
8764 }
8765
glennrpd71e86a2011-02-24 01:28:37 +00008766 }
glennrpa080bc32011-03-11 18:03:44 +00008767 else if (logging != MagickFalse)
8768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8769 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008770 }
8771
8772 image_colors=number_opaque+number_transparent+number_semitransparent;
8773
glennrpa080bc32011-03-11 18:03:44 +00008774 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8775 {
8776 /* No room for the background color; remove it. */
8777 number_opaque--;
8778 image_colors--;
8779 }
8780
glennrpd71e86a2011-02-24 01:28:37 +00008781 if (logging != MagickFalse)
8782 {
8783 if (image_colors > 256)
8784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8785 " image has more than 256 colors");
8786
8787 else
8788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8789 " image has %d colors",image_colors);
8790 }
8791
glennrp8d3d6e52011-04-19 04:39:51 +00008792 if (ping_preserve_colormap != MagickFalse)
8793 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008794
glennrpfd05d622011-02-25 04:10:33 +00008795 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008796 {
8797 ping_have_color=MagickFalse;
8798 ping_have_non_bw=MagickFalse;
8799
glennrp98b95772012-11-29 01:32:00 +00008800 if ((IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) ||
8801 (IssRGBColorspace(image->colorspace) != MagickFalse))
glennrp0fa25802012-07-20 14:01:06 +00008802 {
8803 ping_have_color=MagickTrue;
glennrp98b95772012-11-29 01:32:00 +00008804 ping_have_non_bw=MagickTrue;
glennrp0fa25802012-07-20 14:01:06 +00008805 }
8806
glennrpd71e86a2011-02-24 01:28:37 +00008807 if(image_colors > 256)
8808 {
8809 for (y=0; y < (ssize_t) image->rows; y++)
8810 {
8811 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8812
cristy16ea1392012-03-21 20:38:41 +00008813 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008814 break;
8815
glennrpe5e6b802011-07-20 14:44:40 +00008816 s=q;
8817 for (x=0; x < (ssize_t) image->columns; x++)
8818 {
cristy16ea1392012-03-21 20:38:41 +00008819 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8820 GetPixelRed(image,s) != GetPixelBlue(image,s))
glennrpe5e6b802011-07-20 14:44:40 +00008821 {
8822 ping_have_color=MagickTrue;
8823 ping_have_non_bw=MagickTrue;
8824 break;
8825 }
cristy16ea1392012-03-21 20:38:41 +00008826 s+=GetPixelChannels(image);
glennrpe5e6b802011-07-20 14:44:40 +00008827 }
8828
8829 if (ping_have_color != MagickFalse)
8830 break;
8831
glennrpd71e86a2011-02-24 01:28:37 +00008832 /* Worst case is black-and-white; we are looking at every
8833 * pixel twice.
8834 */
8835
glennrpd71e86a2011-02-24 01:28:37 +00008836 if (ping_have_non_bw == MagickFalse)
8837 {
8838 s=q;
8839 for (x=0; x < (ssize_t) image->columns; x++)
8840 {
cristy16ea1392012-03-21 20:38:41 +00008841 if (GetPixelRed(image,s) != 0 &&
8842 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008843 {
8844 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008845 break;
glennrpd71e86a2011-02-24 01:28:37 +00008846 }
cristy16ea1392012-03-21 20:38:41 +00008847 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008848 }
glennrpe5e6b802011-07-20 14:44:40 +00008849 }
glennrpd71e86a2011-02-24 01:28:37 +00008850 }
glennrpbb4f99d2011-05-22 11:13:17 +00008851 }
8852 }
glennrpd71e86a2011-02-24 01:28:37 +00008853
8854 if (image_colors < 257)
8855 {
cristy16ea1392012-03-21 20:38:41 +00008856 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008857 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008858
glennrpd71e86a2011-02-24 01:28:37 +00008859 /*
8860 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008861 */
8862
glennrpd71e86a2011-02-24 01:28:37 +00008863 if (logging != MagickFalse)
8864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8865 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008866
glennrpd71e86a2011-02-24 01:28:37 +00008867 /* Sort palette, transparent first */;
8868
8869 n = 0;
8870
8871 for (i=0; i<number_transparent; i++)
8872 colormap[n++] = transparent[i];
8873
8874 for (i=0; i<number_semitransparent; i++)
8875 colormap[n++] = semitransparent[i];
8876
8877 for (i=0; i<number_opaque; i++)
8878 colormap[n++] = opaque[i];
8879
glennrpc6c391a2011-04-27 02:23:56 +00008880 ping_background.index +=
8881 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008882
glennrpd71e86a2011-02-24 01:28:37 +00008883 /* image_colors < 257; search the colormap instead of the pixels
8884 * to get ping_have_color and ping_have_non_bw
8885 */
8886 for (i=0; i<n; i++)
8887 {
8888 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008889 {
glennrpd71e86a2011-02-24 01:28:37 +00008890 if (colormap[i].red != colormap[i].green ||
8891 colormap[i].red != colormap[i].blue)
8892 {
8893 ping_have_color=MagickTrue;
8894 ping_have_non_bw=MagickTrue;
8895 break;
8896 }
8897 }
8898
8899 if (ping_have_non_bw == MagickFalse)
8900 {
8901 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008902 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008903 }
glennrp8bb3a022010-12-13 20:40:04 +00008904 }
8905
glennrpd71e86a2011-02-24 01:28:37 +00008906 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8907 (number_transparent == 0 && number_semitransparent == 0)) &&
8908 (((mng_info->write_png_colortype-1) ==
8909 PNG_COLOR_TYPE_PALETTE) ||
8910 (mng_info->write_png_colortype == 0)))
8911 {
glennrp6185c532011-01-14 17:58:40 +00008912 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008913 {
glennrpd71e86a2011-02-24 01:28:37 +00008914 if (n != (ssize_t) image_colors)
8915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8916 " image_colors (%d) and n (%d) don't match",
8917 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008918
glennrpd71e86a2011-02-24 01:28:37 +00008919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8920 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008921 }
glennrp03812ae2010-12-24 01:31:34 +00008922
glennrpd71e86a2011-02-24 01:28:37 +00008923 image->colors = image_colors;
8924
cristy16ea1392012-03-21 20:38:41 +00008925 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008926 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008927 ThrowWriterException(ResourceLimitError,
8928 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008929
8930 for (i=0; i< (ssize_t) image_colors; i++)
8931 image->colormap[i] = colormap[i];
8932
8933 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008934 {
glennrpd71e86a2011-02-24 01:28:37 +00008935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8936 " image->colors=%d (%d)",
8937 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008938
glennrpd71e86a2011-02-24 01:28:37 +00008939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8940 " Update the pixel indexes");
8941 }
glennrp6185c532011-01-14 17:58:40 +00008942
glennrpfd05d622011-02-25 04:10:33 +00008943 /* Sync the pixel indices with the new colormap */
8944
glennrpd71e86a2011-02-24 01:28:37 +00008945 for (y=0; y < (ssize_t) image->rows; y++)
8946 {
cristy16ea1392012-03-21 20:38:41 +00008947 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008948
cristy16ea1392012-03-21 20:38:41 +00008949 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008950 break;
glennrp6185c532011-01-14 17:58:40 +00008951
glennrpd71e86a2011-02-24 01:28:37 +00008952 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008953 {
glennrpd71e86a2011-02-24 01:28:37 +00008954 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008955 {
cristy8a46d822012-08-28 23:32:39 +00008956 if ((image->alpha_trait != BlendPixelTrait ||
cristy16ea1392012-03-21 20:38:41 +00008957 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8958 image->colormap[i].red == GetPixelRed(image,q) &&
8959 image->colormap[i].green == GetPixelGreen(image,q) &&
8960 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008961 {
cristy16ea1392012-03-21 20:38:41 +00008962 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008963 break;
glennrp6185c532011-01-14 17:58:40 +00008964 }
glennrp6185c532011-01-14 17:58:40 +00008965 }
cristy16ea1392012-03-21 20:38:41 +00008966 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008967 }
glennrp6185c532011-01-14 17:58:40 +00008968
glennrpd71e86a2011-02-24 01:28:37 +00008969 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8970 break;
8971 }
8972 }
8973 }
8974
8975 if (logging != MagickFalse)
8976 {
8977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8978 " image->colors=%d", (int) image->colors);
8979
8980 if (image->colormap != NULL)
8981 {
8982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008983 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008984
8985 for (i=0; i < (ssize_t) image->colors; i++)
8986 {
cristy72988482011-03-29 16:34:38 +00008987 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008988 {
8989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8990 " %d (%d,%d,%d,%d)",
8991 (int) i,
8992 (int) image->colormap[i].red,
8993 (int) image->colormap[i].green,
8994 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008995 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008996 }
glennrp6185c532011-01-14 17:58:40 +00008997 }
8998 }
glennrp03812ae2010-12-24 01:31:34 +00008999
glennrpd71e86a2011-02-24 01:28:37 +00009000 if (number_transparent < 257)
9001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9002 " number_transparent = %d",
9003 number_transparent);
9004 else
glennrp03812ae2010-12-24 01:31:34 +00009005
glennrpd71e86a2011-02-24 01:28:37 +00009006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9007 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00009008
glennrpd71e86a2011-02-24 01:28:37 +00009009 if (number_opaque < 257)
9010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9011 " number_opaque = %d",
9012 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00009013
glennrpd71e86a2011-02-24 01:28:37 +00009014 else
9015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9016 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00009017
glennrpd71e86a2011-02-24 01:28:37 +00009018 if (number_semitransparent < 257)
9019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9020 " number_semitransparent = %d",
9021 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00009022
glennrpd71e86a2011-02-24 01:28:37 +00009023 else
9024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9025 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00009026
glennrpd71e86a2011-02-24 01:28:37 +00009027 if (ping_have_non_bw == MagickFalse)
9028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9029 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00009030
glennrpd71e86a2011-02-24 01:28:37 +00009031 else if (ping_have_color == MagickFalse)
9032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9033 " All pixels and the background are gray");
9034
9035 else
9036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9037 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00009038
glennrp03812ae2010-12-24 01:31:34 +00009039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9040 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00009041 }
glennrpfd05d622011-02-25 04:10:33 +00009042
glennrpc8c2f062011-02-25 19:00:33 +00009043 if (mng_info->write_png8 == MagickFalse)
9044 break;
glennrpfd05d622011-02-25 04:10:33 +00009045
glennrpc8c2f062011-02-25 19:00:33 +00009046 /* Make any reductions necessary for the PNG8 format */
9047 if (image_colors <= 256 &&
9048 image_colors != 0 && image->colormap != NULL &&
9049 number_semitransparent == 0 &&
9050 number_transparent <= 1)
9051 break;
9052
9053 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00009054 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9055 * transparent color so if more than one is transparent we merge
9056 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00009057 */
glennrp130fc452011-08-20 03:43:18 +00009058 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00009059 {
9060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9061 " Thresholding the alpha channel to binary");
9062
9063 for (y=0; y < (ssize_t) image->rows; y++)
9064 {
cristy16ea1392012-03-21 20:38:41 +00009065 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00009066
cristy16ea1392012-03-21 20:38:41 +00009067 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00009068 break;
9069
9070 for (x=0; x < (ssize_t) image->columns; x++)
9071 {
cristy16ea1392012-03-21 20:38:41 +00009072 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00009073 {
cristy16ea1392012-03-21 20:38:41 +00009074 SetPixelInfoPixel(image,&image->background_color,r);
9075 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00009076 }
9077 else
cristy16ea1392012-03-21 20:38:41 +00009078 SetPixelAlpha(image,OpaqueAlpha,r);
9079 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00009080 }
glennrpbb4f99d2011-05-22 11:13:17 +00009081
glennrpc8c2f062011-02-25 19:00:33 +00009082 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9083 break;
9084
9085 if (image_colors != 0 && image_colors <= 256 &&
9086 image->colormap != NULL)
9087 for (i=0; i<image_colors; i++)
cristy16ea1392012-03-21 20:38:41 +00009088 image->colormap[i].alpha =
9089 (image->colormap[i].alpha > TransparentAlpha/2 ?
9090 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00009091 }
9092 continue;
9093 }
9094
9095 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00009096 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9097 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9098 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00009099 */
glennrpd3371642011-03-22 19:42:23 +00009100 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9101 {
9102 if (logging != MagickFalse)
9103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9104 " Quantizing the background color to 4-4-4");
9105
9106 tried_444 = MagickTrue;
9107
glennrp91d99252011-06-25 14:30:13 +00009108 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00009109
9110 if (logging != MagickFalse)
9111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9112 " Quantizing the pixel colors to 4-4-4");
9113
9114 if (image->colormap == NULL)
9115 {
9116 for (y=0; y < (ssize_t) image->rows; y++)
9117 {
cristy16ea1392012-03-21 20:38:41 +00009118 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00009119
cristy16ea1392012-03-21 20:38:41 +00009120 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00009121 break;
9122
9123 for (x=0; x < (ssize_t) image->columns; x++)
9124 {
cristy16ea1392012-03-21 20:38:41 +00009125 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00009126 LBR04PixelRGB(r);
cristy16ea1392012-03-21 20:38:41 +00009127 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00009128 }
glennrpbb4f99d2011-05-22 11:13:17 +00009129
glennrpd3371642011-03-22 19:42:23 +00009130 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9131 break;
9132 }
9133 }
9134
9135 else /* Should not reach this; colormap already exists and
9136 must be <= 256 */
9137 {
9138 if (logging != MagickFalse)
9139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9140 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00009141
glennrpd3371642011-03-22 19:42:23 +00009142 for (i=0; i<image_colors; i++)
9143 {
glennrp91d99252011-06-25 14:30:13 +00009144 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00009145 }
9146 }
9147 continue;
9148 }
9149
glennrp82b3c532011-03-22 19:20:54 +00009150 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9151 {
9152 if (logging != MagickFalse)
9153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9154 " Quantizing the background color to 3-3-3");
9155
9156 tried_333 = MagickTrue;
9157
glennrp91d99252011-06-25 14:30:13 +00009158 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00009159
9160 if (logging != MagickFalse)
9161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009162 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00009163
9164 if (image->colormap == NULL)
9165 {
9166 for (y=0; y < (ssize_t) image->rows; y++)
9167 {
cristy16ea1392012-03-21 20:38:41 +00009168 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00009169
cristy16ea1392012-03-21 20:38:41 +00009170 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00009171 break;
9172
9173 for (x=0; x < (ssize_t) image->columns; x++)
9174 {
cristy16ea1392012-03-21 20:38:41 +00009175 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9176 LBR03RGB(r);
9177 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00009178 }
glennrpbb4f99d2011-05-22 11:13:17 +00009179
glennrp82b3c532011-03-22 19:20:54 +00009180 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9181 break;
9182 }
9183 }
9184
9185 else /* Should not reach this; colormap already exists and
9186 must be <= 256 */
9187 {
9188 if (logging != MagickFalse)
9189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009190 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00009191 for (i=0; i<image_colors; i++)
9192 {
glennrp91d99252011-06-25 14:30:13 +00009193 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00009194 }
glennrpd3371642011-03-22 19:42:23 +00009195 }
9196 continue;
glennrp82b3c532011-03-22 19:20:54 +00009197 }
glennrpc8c2f062011-02-25 19:00:33 +00009198
glennrp8ca51ad2011-05-12 21:22:32 +00009199 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00009200 {
9201 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00009202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00009203 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00009204
glennrp8ca51ad2011-05-12 21:22:32 +00009205 tried_332 = MagickTrue;
9206
glennrp3faa9a32011-04-23 14:00:25 +00009207 /* Red and green were already done so we only quantize the blue
9208 * channel
9209 */
9210
glennrp91d99252011-06-25 14:30:13 +00009211 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00009212
glennrpc8c2f062011-02-25 19:00:33 +00009213 if (logging != MagickFalse)
9214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009215 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00009216
glennrpc8c2f062011-02-25 19:00:33 +00009217 if (image->colormap == NULL)
9218 {
9219 for (y=0; y < (ssize_t) image->rows; y++)
9220 {
cristy16ea1392012-03-21 20:38:41 +00009221 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00009222
cristy16ea1392012-03-21 20:38:41 +00009223 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00009224 break;
9225
9226 for (x=0; x < (ssize_t) image->columns; x++)
9227 {
cristy16ea1392012-03-21 20:38:41 +00009228 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00009229 LBR02PixelBlue(r);
cristy16ea1392012-03-21 20:38:41 +00009230 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00009231 }
glennrpbb4f99d2011-05-22 11:13:17 +00009232
glennrpc8c2f062011-02-25 19:00:33 +00009233 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9234 break;
9235 }
9236 }
glennrpfd05d622011-02-25 04:10:33 +00009237
glennrpc8c2f062011-02-25 19:00:33 +00009238 else /* Should not reach this; colormap already exists and
9239 must be <= 256 */
9240 {
9241 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00009242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009243 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00009244 for (i=0; i<image_colors; i++)
9245 {
glennrp91d99252011-06-25 14:30:13 +00009246 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00009247 }
9248 }
9249 continue;
9250 }
9251 break;
glennrp8ca51ad2011-05-12 21:22:32 +00009252
9253 if (image_colors == 0 || image_colors > 256)
9254 {
9255 /* Take care of special case with 256 colors + 1 transparent
9256 * color. We don't need to quantize to 2-3-2-1; we only need to
9257 * eliminate one color, so we'll merge the two darkest red
9258 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9259 */
9260 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9261 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9262 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9263 {
9264 image->background_color.red=ScaleCharToQuantum(0x24);
9265 }
glennrpbb4f99d2011-05-22 11:13:17 +00009266
glennrp8ca51ad2011-05-12 21:22:32 +00009267 if (image->colormap == NULL)
9268 {
9269 for (y=0; y < (ssize_t) image->rows; y++)
9270 {
cristy16ea1392012-03-21 20:38:41 +00009271 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00009272
cristy16ea1392012-03-21 20:38:41 +00009273 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00009274 break;
glennrpbb4f99d2011-05-22 11:13:17 +00009275
glennrp8ca51ad2011-05-12 21:22:32 +00009276 for (x=0; x < (ssize_t) image->columns; x++)
9277 {
cristy16ea1392012-03-21 20:38:41 +00009278 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9279 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9280 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9281 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00009282 {
cristy16ea1392012-03-21 20:38:41 +00009283 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00009284 }
cristy16ea1392012-03-21 20:38:41 +00009285 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00009286 }
glennrpbb4f99d2011-05-22 11:13:17 +00009287
glennrp8ca51ad2011-05-12 21:22:32 +00009288 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9289 break;
glennrpbb4f99d2011-05-22 11:13:17 +00009290
glennrp8ca51ad2011-05-12 21:22:32 +00009291 }
9292 }
9293
9294 else
9295 {
9296 for (i=0; i<image_colors; i++)
9297 {
9298 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9299 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9300 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9301 {
9302 image->colormap[i].red=ScaleCharToQuantum(0x24);
9303 }
9304 }
9305 }
9306 }
glennrpd71e86a2011-02-24 01:28:37 +00009307 }
glennrpa8036d62012-11-04 01:46:06 +00009308 }
glennrpfd05d622011-02-25 04:10:33 +00009309 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00009310
glennrpfd05d622011-02-25 04:10:33 +00009311 /* If we are excluding the tRNS chunk and there is transparency,
9312 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9313 * PNG.
glennrp8d579662011-02-23 02:05:02 +00009314 */
glennrp0e8ea192010-12-24 18:00:33 +00009315 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9316 (number_transparent != 0 || number_semitransparent != 0))
9317 {
glennrpd17915c2011-04-29 14:24:22 +00009318 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00009319
9320 if (ping_have_color == MagickFalse)
9321 mng_info->write_png_colortype = 5;
9322
9323 else
9324 mng_info->write_png_colortype = 7;
9325
glennrp8d579662011-02-23 02:05:02 +00009326 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00009327 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00009328 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00009329
glennrp0e8ea192010-12-24 18:00:33 +00009330 }
9331
glennrpfd05d622011-02-25 04:10:33 +00009332 /* See if cheap transparency is possible. It is only possible
9333 * when there is a single transparent color, no semitransparent
9334 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00009335 * as the transparent color. We only need this information if
9336 * we are writing a PNG with colortype 0 or 2, and we have not
9337 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00009338 */
glennrp5a39f372011-02-25 04:52:16 +00009339 if (number_transparent == 1 &&
9340 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00009341 {
9342 ping_have_cheap_transparency = MagickTrue;
9343
9344 if (number_semitransparent != 0)
9345 ping_have_cheap_transparency = MagickFalse;
9346
9347 else if (image_colors == 0 || image_colors > 256 ||
9348 image->colormap == NULL)
9349 {
cristy16ea1392012-03-21 20:38:41 +00009350 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00009351 *q;
9352
glennrpfd05d622011-02-25 04:10:33 +00009353 for (y=0; y < (ssize_t) image->rows; y++)
9354 {
9355 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9356
cristy16ea1392012-03-21 20:38:41 +00009357 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00009358 break;
9359
9360 for (x=0; x < (ssize_t) image->columns; x++)
9361 {
cristy16ea1392012-03-21 20:38:41 +00009362 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9363 (unsigned short) GetPixelRed(image,q) ==
9364 ping_trans_color.red &&
9365 (unsigned short) GetPixelGreen(image,q) ==
9366 ping_trans_color.green &&
9367 (unsigned short) GetPixelBlue(image,q) ==
9368 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00009369 {
9370 ping_have_cheap_transparency = MagickFalse;
9371 break;
9372 }
9373
cristy16ea1392012-03-21 20:38:41 +00009374 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00009375 }
glennrpbb4f99d2011-05-22 11:13:17 +00009376
glennrpfd05d622011-02-25 04:10:33 +00009377 if (ping_have_cheap_transparency == MagickFalse)
9378 break;
9379 }
9380 }
9381 else
9382 {
glennrp67b9c1a2011-04-22 18:47:36 +00009383 /* Assuming that image->colormap[0] is the one transparent color
9384 * and that all others are opaque.
9385 */
glennrpfd05d622011-02-25 04:10:33 +00009386 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00009387 for (i=1; i<image_colors; i++)
9388 if (image->colormap[i].red == image->colormap[0].red &&
9389 image->colormap[i].green == image->colormap[0].green &&
9390 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00009391 {
glennrp67b9c1a2011-04-22 18:47:36 +00009392 ping_have_cheap_transparency = MagickFalse;
9393 break;
glennrpfd05d622011-02-25 04:10:33 +00009394 }
9395 }
glennrpbb4f99d2011-05-22 11:13:17 +00009396
glennrpfd05d622011-02-25 04:10:33 +00009397 if (logging != MagickFalse)
9398 {
9399 if (ping_have_cheap_transparency == MagickFalse)
9400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9401 " Cheap transparency is not possible.");
9402
9403 else
9404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9405 " Cheap transparency is possible.");
9406 }
9407 }
9408 else
9409 ping_have_cheap_transparency = MagickFalse;
9410
glennrp8640fb52010-11-23 15:48:26 +00009411 image_depth=image->depth;
9412
glennrp26c990a2010-11-23 02:23:20 +00009413 quantum_info = (QuantumInfo *) NULL;
9414 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00009415 image_colors=(int) image->colors;
cristyb0a657e2012-08-29 00:45:37 +00009416 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
glennrp26c990a2010-11-23 02:23:20 +00009417
glennrp0fe50b42010-11-16 03:52:51 +00009418 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00009419 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00009420
glennrp52a479c2011-02-26 21:14:38 +00009421 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9422 (image->colors == 0 || image->colormap == NULL))
9423 {
cristy16ea1392012-03-21 20:38:41 +00009424 image_info=DestroyImageInfo(image_info);
9425 image=DestroyImage(image);
9426 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00009427 "Cannot write PNG8 or color-type 3; colormap is NULL",
cristy16ea1392012-03-21 20:38:41 +00009428 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00009429 return(MagickFalse);
9430 }
9431
cristy3ed852e2009-09-05 21:47:34 +00009432 /*
9433 Allocate the PNG structures
9434 */
9435#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00009436 error_info.image=image;
9437 error_info.exception=exception;
9438 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009439 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9440 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00009441
cristy3ed852e2009-09-05 21:47:34 +00009442#else
cristy16ea1392012-03-21 20:38:41 +00009443 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009444 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00009445
cristy3ed852e2009-09-05 21:47:34 +00009446#endif
9447 if (ping == (png_struct *) NULL)
9448 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009449
cristy3ed852e2009-09-05 21:47:34 +00009450 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00009451
cristy3ed852e2009-09-05 21:47:34 +00009452 if (ping_info == (png_info *) NULL)
9453 {
9454 png_destroy_write_struct(&ping,(png_info **) NULL);
9455 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9456 }
glennrp0fe50b42010-11-16 03:52:51 +00009457
cristy3ed852e2009-09-05 21:47:34 +00009458 png_set_write_fn(ping,image,png_put_data,png_flush_data);
cristy09973322013-06-30 21:06:30 +00009459 pixel_info=(MemoryInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00009460
glennrp5af765f2010-03-30 11:12:18 +00009461 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009462 {
9463 /*
9464 PNG write failed.
9465 */
9466#ifdef PNG_DEBUG
9467 if (image_info->verbose)
9468 (void) printf("PNG write has failed.\n");
9469#endif
9470 png_destroy_write_struct(&ping,&ping_info);
glennrpedaa0382012-04-12 14:16:21 +00009471#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00009472 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009473#endif
glennrpedaa0382012-04-12 14:16:21 +00009474
cristy09973322013-06-30 21:06:30 +00009475 if (pixel_info != (MemoryInfo *) NULL)
9476 pixel_info=RelinquishVirtualMemory(pixel_info);
glennrpedaa0382012-04-12 14:16:21 +00009477
9478 if (quantum_info != (QuantumInfo *) NULL)
9479 quantum_info=DestroyQuantumInfo(quantum_info);
9480
cristy16ea1392012-03-21 20:38:41 +00009481 if (ping_have_blob != MagickFalse)
9482 (void) CloseBlob(image);
9483 image_info=DestroyImageInfo(image_info);
9484 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009485 return(MagickFalse);
9486 }
glennrpedaa0382012-04-12 14:16:21 +00009487
9488 /* { For navigation to end of SETJMP-protected block. Within this
9489 * block, use png_error() instead of Throwing an Exception, to ensure
9490 * that libpng is able to clean up, and that the semaphore is unlocked.
9491 */
9492
9493#ifdef PNG_SETJMP_NOT_THREAD_SAFE
9494 LockSemaphoreInfo(ping_semaphore);
9495#endif
9496
glennrp943b7d32013-04-21 00:40:38 +00009497#ifdef PNG_BENIGN_ERRORS_SUPPORTED
glennrpa3a5f952013-04-17 12:58:15 +00009498 /* Allow benign errors */
9499 png_set_benign_errors(ping, 1);
9500#endif
9501
cristy3ed852e2009-09-05 21:47:34 +00009502 /*
9503 Prepare PNG for writing.
9504 */
glennrp9bf97b62012-06-06 21:03:14 +00009505
cristy3ed852e2009-09-05 21:47:34 +00009506#if defined(PNG_MNG_FEATURES_SUPPORTED)
9507 if (mng_info->write_mng)
glennrp25024a62012-06-07 11:38:34 +00009508 {
cristy3ed852e2009-09-05 21:47:34 +00009509 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp25024a62012-06-07 11:38:34 +00009510# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9511 /* Disable new libpng-1.5.10 feature when writing a MNG because
9512 * zero-length PLTE is OK
9513 */
9514 png_set_check_for_invalid_index (ping, 0);
9515# endif
9516 }
glennrp2b013e42010-11-24 16:55:50 +00009517
cristy3ed852e2009-09-05 21:47:34 +00009518#else
9519# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9520 if (mng_info->write_mng)
9521 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00009522
cristy3ed852e2009-09-05 21:47:34 +00009523# endif
9524#endif
glennrp2b013e42010-11-24 16:55:50 +00009525
cristy3ed852e2009-09-05 21:47:34 +00009526 x=0;
glennrp2b013e42010-11-24 16:55:50 +00009527
cristy4e5bc842010-06-09 13:56:01 +00009528 ping_width=(png_uint_32) image->columns;
9529 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00009530
cristy3ed852e2009-09-05 21:47:34 +00009531 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9532 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009533
glennrpfd164d22013-01-26 21:10:22 +00009534 if (mng_info->write_png48 || mng_info->write_png64)
9535 image_depth=16;
9536
cristy3ed852e2009-09-05 21:47:34 +00009537 if (mng_info->write_png_depth != 0)
9538 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009539
cristy3ed852e2009-09-05 21:47:34 +00009540 /* Adjust requested depth to next higher valid depth if necessary */
9541 if (image_depth > 8)
9542 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009543
cristy3ed852e2009-09-05 21:47:34 +00009544 if ((image_depth > 4) && (image_depth < 8))
9545 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009546
cristy3ed852e2009-09-05 21:47:34 +00009547 if (image_depth == 3)
9548 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00009549
cristy3ed852e2009-09-05 21:47:34 +00009550 if (logging != MagickFalse)
9551 {
9552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009553 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00009554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009555 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00009556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00009557 " image_matte=%.20g",(double) image->alpha_trait);
cristy3ed852e2009-09-05 21:47:34 +00009558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009559 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00009560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009561 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00009562 }
glennrp8640fb52010-11-23 15:48:26 +00009563
cristy3ed852e2009-09-05 21:47:34 +00009564 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00009565 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00009566
glennrp26f37912010-12-23 16:22:42 +00009567
cristy3ed852e2009-09-05 21:47:34 +00009568#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00009569 if (ping_exclude_pHYs == MagickFalse)
9570 {
cristy16ea1392012-03-21 20:38:41 +00009571 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00009572 (!mng_info->write_mng || !mng_info->equal_physs))
9573 {
glennrp0fe50b42010-11-16 03:52:51 +00009574 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9576 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00009577
9578 if (image->units == PixelsPerInchResolution)
9579 {
glennrpdfd70802010-11-14 01:23:35 +00009580 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009581 ping_pHYs_x_resolution=
cristy16ea1392012-03-21 20:38:41 +00009582 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00009583 ping_pHYs_y_resolution=
cristy16ea1392012-03-21 20:38:41 +00009584 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009585 }
glennrpdfd70802010-11-14 01:23:35 +00009586
cristy3ed852e2009-09-05 21:47:34 +00009587 else if (image->units == PixelsPerCentimeterResolution)
9588 {
glennrpdfd70802010-11-14 01:23:35 +00009589 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy16ea1392012-03-21 20:38:41 +00009590 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9591 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009592 }
glennrp991d11d2010-11-12 21:55:28 +00009593
cristy3ed852e2009-09-05 21:47:34 +00009594 else
9595 {
glennrpdfd70802010-11-14 01:23:35 +00009596 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy16ea1392012-03-21 20:38:41 +00009597 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9598 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009599 }
glennrp991d11d2010-11-12 21:55:28 +00009600
glennrp823b55c2011-03-14 18:46:46 +00009601 if (logging != MagickFalse)
9602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9603 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9604 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9605 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009606 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009607 }
glennrp26f37912010-12-23 16:22:42 +00009608 }
cristy3ed852e2009-09-05 21:47:34 +00009609#endif
glennrpa521b2f2010-10-29 04:11:03 +00009610
glennrp26f37912010-12-23 16:22:42 +00009611 if (ping_exclude_bKGD == MagickFalse)
9612 {
glennrpa521b2f2010-10-29 04:11:03 +00009613 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009614 {
glennrpa521b2f2010-10-29 04:11:03 +00009615 unsigned int
9616 mask;
cristy3ed852e2009-09-05 21:47:34 +00009617
glennrpa521b2f2010-10-29 04:11:03 +00009618 mask=0xffff;
9619 if (ping_bit_depth == 8)
9620 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009621
glennrpa521b2f2010-10-29 04:11:03 +00009622 if (ping_bit_depth == 4)
9623 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009624
glennrpa521b2f2010-10-29 04:11:03 +00009625 if (ping_bit_depth == 2)
9626 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009627
glennrpa521b2f2010-10-29 04:11:03 +00009628 if (ping_bit_depth == 1)
9629 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009630
glennrpa521b2f2010-10-29 04:11:03 +00009631 ping_background.red=(png_uint_16)
9632 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009633
glennrpa521b2f2010-10-29 04:11:03 +00009634 ping_background.green=(png_uint_16)
9635 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009636
glennrpa521b2f2010-10-29 04:11:03 +00009637 ping_background.blue=(png_uint_16)
9638 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009639
9640 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009641 }
cristy3ed852e2009-09-05 21:47:34 +00009642
glennrp0fe50b42010-11-16 03:52:51 +00009643 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009644 {
9645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9646 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9648 " background_color index is %d",
9649 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009650
9651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9652 " ping_bit_depth=%d",ping_bit_depth);
9653 }
glennrp0fe50b42010-11-16 03:52:51 +00009654
9655 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009656 }
glennrp0fe50b42010-11-16 03:52:51 +00009657
cristy3ed852e2009-09-05 21:47:34 +00009658 /*
9659 Select the color type.
9660 */
9661 matte=image_matte;
9662 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009663
glennrp1273f7b2011-02-24 03:20:30 +00009664 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009665 {
glennrp0fe50b42010-11-16 03:52:51 +00009666
glennrpfd05d622011-02-25 04:10:33 +00009667 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009668 for reducing the sample depth from 8. */
9669
glennrp0fe50b42010-11-16 03:52:51 +00009670 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009671
glennrp8bb3a022010-12-13 20:40:04 +00009672 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009673
9674 /*
9675 Set image palette.
9676 */
9677 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9678
glennrp0fe50b42010-11-16 03:52:51 +00009679 if (logging != MagickFalse)
9680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9681 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009682 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009683
9684 for (i=0; i < (ssize_t) number_colors; i++)
9685 {
9686 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9687 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9688 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9689 if (logging != MagickFalse)
9690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009691#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009692 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009693#else
9694 " %5ld (%5d,%5d,%5d)",
9695#endif
glennrp0fe50b42010-11-16 03:52:51 +00009696 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9697
9698 }
glennrp2b013e42010-11-24 16:55:50 +00009699
glennrp8bb3a022010-12-13 20:40:04 +00009700 ping_have_PLTE=MagickTrue;
9701 image_depth=ping_bit_depth;
9702 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009703
glennrp58e01762011-01-07 15:28:54 +00009704 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009705 {
glennrp0fe50b42010-11-16 03:52:51 +00009706 /*
9707 Identify which colormap entry is transparent.
9708 */
9709 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009710 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009711
glennrp8bb3a022010-12-13 20:40:04 +00009712 for (i=0; i < (ssize_t) number_transparent; i++)
9713 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009714
glennrp0fe50b42010-11-16 03:52:51 +00009715
glennrp2cc891a2010-12-24 13:44:32 +00009716 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009717 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009718
9719 if (ping_num_trans == 0)
9720 ping_have_tRNS=MagickFalse;
9721
glennrp8bb3a022010-12-13 20:40:04 +00009722 else
9723 ping_have_tRNS=MagickTrue;
9724 }
glennrp0fe50b42010-11-16 03:52:51 +00009725
glennrp1273f7b2011-02-24 03:20:30 +00009726 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009727 {
glennrp1273f7b2011-02-24 03:20:30 +00009728 /*
9729 * Identify which colormap entry is the background color.
9730 */
9731
glennrp4f25bd02011-01-01 18:51:28 +00009732 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9733 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9734 break;
glennrp0fe50b42010-11-16 03:52:51 +00009735
glennrp4f25bd02011-01-01 18:51:28 +00009736 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009737
9738 if (logging != MagickFalse)
9739 {
9740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9741 " background_color index is %d",
9742 (int) ping_background.index);
9743 }
glennrp4f25bd02011-01-01 18:51:28 +00009744 }
cristy3ed852e2009-09-05 21:47:34 +00009745 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009746
glennrpfd164d22013-01-26 21:10:22 +00009747 else if (mng_info->write_png_colortype == 1)
9748 {
9749 image_matte=MagickFalse;
9750 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9751 }
9752
9753 else if (mng_info->write_png24 || mng_info->write_png48 ||
9754 mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009755 {
9756 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009757 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009758 }
glennrp0fe50b42010-11-16 03:52:51 +00009759
glennrpfd164d22013-01-26 21:10:22 +00009760 else if (mng_info->write_png32 || mng_info->write_png64 ||
9761 mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009762 {
9763 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009764 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009765 }
glennrp0fe50b42010-11-16 03:52:51 +00009766
glennrp8bb3a022010-12-13 20:40:04 +00009767 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009768 {
glennrp5af765f2010-03-30 11:12:18 +00009769 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009770
glennrp8bb3a022010-12-13 20:40:04 +00009771 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009772 {
glennrp5af765f2010-03-30 11:12:18 +00009773 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009774
glennrp5af765f2010-03-30 11:12:18 +00009775 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9776 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009777 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009778
glennrp8bb3a022010-12-13 20:40:04 +00009779 else
9780 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009781
9782 if (logging != MagickFalse)
9783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9784 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009785 }
glennrp0fe50b42010-11-16 03:52:51 +00009786
glennrp7c4c9e62011-03-21 20:23:32 +00009787 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009788 {
9789 if (logging != MagickFalse)
9790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009791 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009792
glennrpd6bf1612010-12-17 17:28:54 +00009793 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009794 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009795
glennrpd6bf1612010-12-17 17:28:54 +00009796 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009797 {
glennrp5af765f2010-03-30 11:12:18 +00009798 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009799 image_matte=MagickFalse;
9800 }
glennrp0fe50b42010-11-16 03:52:51 +00009801
glennrpd6bf1612010-12-17 17:28:54 +00009802 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009803 {
glennrp5af765f2010-03-30 11:12:18 +00009804 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009805 image_matte=MagickTrue;
9806 }
glennrp0fe50b42010-11-16 03:52:51 +00009807
glennrp5aa37f62011-01-02 03:07:57 +00009808 if (image_info->type == PaletteType ||
9809 image_info->type == PaletteMatteType)
9810 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9811
glennrp7c4c9e62011-03-21 20:23:32 +00009812 if (mng_info->write_png_colortype == 0 &&
9813 (image_info->type == UndefinedType ||
9814 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009815 {
glennrp5aa37f62011-01-02 03:07:57 +00009816 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009817 {
glennrp5aa37f62011-01-02 03:07:57 +00009818 if (image_matte == MagickFalse)
9819 {
9820 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9821 image_matte=MagickFalse;
9822 }
glennrp0fe50b42010-11-16 03:52:51 +00009823
glennrp0b206f52011-01-07 04:55:32 +00009824 else
glennrp5aa37f62011-01-02 03:07:57 +00009825 {
9826 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9827 image_matte=MagickTrue;
9828 }
9829 }
9830 else
glennrp8bb3a022010-12-13 20:40:04 +00009831 {
glennrp5aa37f62011-01-02 03:07:57 +00009832 if (image_matte == MagickFalse)
9833 {
9834 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9835 image_matte=MagickFalse;
9836 }
glennrp8bb3a022010-12-13 20:40:04 +00009837
glennrp0b206f52011-01-07 04:55:32 +00009838 else
glennrp5aa37f62011-01-02 03:07:57 +00009839 {
9840 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9841 image_matte=MagickTrue;
9842 }
9843 }
glennrp0fe50b42010-11-16 03:52:51 +00009844 }
glennrp5aa37f62011-01-02 03:07:57 +00009845
cristy3ed852e2009-09-05 21:47:34 +00009846 }
glennrp0fe50b42010-11-16 03:52:51 +00009847
cristy3ed852e2009-09-05 21:47:34 +00009848 if (logging != MagickFalse)
9849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009850 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009851
glennrp5af765f2010-03-30 11:12:18 +00009852 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009853 {
9854 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9855 ping_color_type == PNG_COLOR_TYPE_RGB ||
9856 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9857 ping_bit_depth=8;
9858 }
cristy3ed852e2009-09-05 21:47:34 +00009859
glennrpd6bf1612010-12-17 17:28:54 +00009860 old_bit_depth=ping_bit_depth;
9861
glennrp5af765f2010-03-30 11:12:18 +00009862 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009863 {
cristy8a46d822012-08-28 23:32:39 +00009864 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00009865 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009866 }
glennrp8640fb52010-11-23 15:48:26 +00009867
glennrp5af765f2010-03-30 11:12:18 +00009868 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009869 {
cristy35ef8242010-06-03 16:24:13 +00009870 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009871 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009872
9873 if (image->colors == 0)
9874 {
glennrp0fe50b42010-11-16 03:52:51 +00009875 /* DO SOMETHING */
glennrpedaa0382012-04-12 14:16:21 +00009876 png_error(ping,"image has 0 colors");
glennrp0f111982010-07-07 20:18:33 +00009877 }
9878
cristy35ef8242010-06-03 16:24:13 +00009879 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009880 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009881 }
glennrp2b013e42010-11-24 16:55:50 +00009882
glennrpd6bf1612010-12-17 17:28:54 +00009883 if (logging != MagickFalse)
9884 {
9885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9886 " Number of colors: %.20g",(double) image_colors);
9887
9888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9889 " Tentative PNG bit depth: %d",ping_bit_depth);
9890 }
9891
9892 if (ping_bit_depth < (int) mng_info->write_png_depth)
9893 ping_bit_depth = mng_info->write_png_depth;
9894 }
glennrp2cc891a2010-12-24 13:44:32 +00009895
glennrp5af765f2010-03-30 11:12:18 +00009896 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009897
cristy3ed852e2009-09-05 21:47:34 +00009898 if (logging != MagickFalse)
9899 {
9900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +00009901 " Tentative PNG color type: %s (%.20g)",
9902 PngColorTypeToString(ping_color_type),
9903 (double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009904
cristy3ed852e2009-09-05 21:47:34 +00009905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009906 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009907
cristy3ed852e2009-09-05 21:47:34 +00009908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009909 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009910
cristy3ed852e2009-09-05 21:47:34 +00009911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009912
glennrp8640fb52010-11-23 15:48:26 +00009913 " image->depth: %.20g",(double) image->depth);
9914
9915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009916 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009917 }
9918
glennrp58e01762011-01-07 15:28:54 +00009919 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009920 {
glennrp4f25bd02011-01-01 18:51:28 +00009921 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009922 {
glennrp7c4c9e62011-03-21 20:23:32 +00009923 if (mng_info->write_png_colortype == 0)
9924 {
9925 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009926
glennrp7c4c9e62011-03-21 20:23:32 +00009927 if (ping_have_color != MagickFalse)
9928 ping_color_type=PNG_COLOR_TYPE_RGBA;
9929 }
glennrp4f25bd02011-01-01 18:51:28 +00009930
9931 /*
9932 * Determine if there is any transparent color.
9933 */
9934 if (number_transparent + number_semitransparent == 0)
9935 {
9936 /*
9937 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9938 */
glennrpa6a06632011-01-19 15:15:34 +00009939
glennrp4f25bd02011-01-01 18:51:28 +00009940 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009941
9942 if (mng_info->write_png_colortype == 0)
9943 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009944 }
9945
9946 else
9947 {
9948 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009949 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009950
9951 mask=0xffff;
9952
9953 if (ping_bit_depth == 8)
9954 mask=0x00ff;
9955
9956 if (ping_bit_depth == 4)
9957 mask=0x000f;
9958
9959 if (ping_bit_depth == 2)
9960 mask=0x0003;
9961
9962 if (ping_bit_depth == 1)
9963 mask=0x0001;
9964
9965 ping_trans_color.red=(png_uint_16)
9966 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9967
9968 ping_trans_color.green=(png_uint_16)
9969 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9970
9971 ping_trans_color.blue=(png_uint_16)
9972 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9973
9974 ping_trans_color.gray=(png_uint_16)
cristy16ea1392012-03-21 20:38:41 +00009975 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009976 image->colormap)) & mask);
9977
9978 ping_trans_color.index=(png_byte) 0;
9979
9980 ping_have_tRNS=MagickTrue;
9981 }
9982
9983 if (ping_have_tRNS != MagickFalse)
9984 {
9985 /*
glennrpfd05d622011-02-25 04:10:33 +00009986 * Determine if there is one and only one transparent color
9987 * and if so if it is fully transparent.
9988 */
9989 if (ping_have_cheap_transparency == MagickFalse)
9990 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009991 }
9992
9993 if (ping_have_tRNS != MagickFalse)
9994 {
glennrp7c4c9e62011-03-21 20:23:32 +00009995 if (mng_info->write_png_colortype == 0)
9996 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009997
9998 if (image_depth == 8)
9999 {
10000 ping_trans_color.red&=0xff;
10001 ping_trans_color.green&=0xff;
10002 ping_trans_color.blue&=0xff;
10003 ping_trans_color.gray&=0xff;
10004 }
10005 }
10006 }
cristy3ed852e2009-09-05 21:47:34 +000010007 else
10008 {
cristy3ed852e2009-09-05 21:47:34 +000010009 if (image_depth == 8)
10010 {
glennrp5af765f2010-03-30 11:12:18 +000010011 ping_trans_color.red&=0xff;
10012 ping_trans_color.green&=0xff;
10013 ping_trans_color.blue&=0xff;
10014 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +000010015 }
10016 }
10017 }
glennrp8640fb52010-11-23 15:48:26 +000010018
cristy3ed852e2009-09-05 21:47:34 +000010019 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +000010020
glennrp2e09f552010-11-14 00:38:48 +000010021 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010022 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010023
glennrp39992b42010-11-14 00:03:43 +000010024 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +000010025 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +000010026 ping_have_color == MagickFalse &&
10027 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +000010028 {
cristy35ef8242010-06-03 16:24:13 +000010029 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +000010030
cristy3ed852e2009-09-05 21:47:34 +000010031 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010032 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +000010033
glennrp7c4c9e62011-03-21 20:23:32 +000010034 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +000010035 {
glennrp5af765f2010-03-30 11:12:18 +000010036 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +000010037
cristy3ed852e2009-09-05 21:47:34 +000010038 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +000010039 {
10040 if (logging != MagickFalse)
10041 {
10042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10043 " Scaling ping_trans_color (0)");
10044 }
10045 ping_trans_color.gray*=0x0101;
10046 }
cristy3ed852e2009-09-05 21:47:34 +000010047 }
glennrp0fe50b42010-11-16 03:52:51 +000010048
cristy3ed852e2009-09-05 21:47:34 +000010049 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10050 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +000010051
glennrp136ee3a2011-04-27 15:47:45 +000010052 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +000010053 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +000010054 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010055
cristy3ed852e2009-09-05 21:47:34 +000010056 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +000010057 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +000010058
cristy3ed852e2009-09-05 21:47:34 +000010059 else
10060 {
glennrp5af765f2010-03-30 11:12:18 +000010061 ping_bit_depth=8;
10062 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +000010063 {
10064 if(!mng_info->write_png_depth)
10065 {
glennrp5af765f2010-03-30 11:12:18 +000010066 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +000010067
cristy35ef8242010-06-03 16:24:13 +000010068 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +000010069 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +000010070 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +000010071 }
10072 }
glennrp2b013e42010-11-24 16:55:50 +000010073
glennrp0fe50b42010-11-16 03:52:51 +000010074 else if (ping_color_type ==
10075 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +000010076 mng_info->IsPalette)
10077 {
cristy3ed852e2009-09-05 21:47:34 +000010078 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +000010079
cristy3ed852e2009-09-05 21:47:34 +000010080 int
10081 depth_4_ok=MagickTrue,
10082 depth_2_ok=MagickTrue,
10083 depth_1_ok=MagickTrue;
10084
cristybb503372010-05-27 20:51:26 +000010085 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000010086 {
10087 unsigned char
10088 intensity;
10089
10090 intensity=ScaleQuantumToChar(image->colormap[i].red);
10091
10092 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10093 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10094 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10095 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +000010096 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +000010097 depth_1_ok=MagickFalse;
10098 }
glennrp2b013e42010-11-24 16:55:50 +000010099
cristy3ed852e2009-09-05 21:47:34 +000010100 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +000010101 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +000010102
cristy3ed852e2009-09-05 21:47:34 +000010103 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +000010104 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +000010105
cristy3ed852e2009-09-05 21:47:34 +000010106 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +000010107 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +000010108 }
10109 }
glennrp2b013e42010-11-24 16:55:50 +000010110
glennrp5af765f2010-03-30 11:12:18 +000010111 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +000010112 }
glennrp0fe50b42010-11-16 03:52:51 +000010113
cristy3ed852e2009-09-05 21:47:34 +000010114 else
glennrp0fe50b42010-11-16 03:52:51 +000010115
cristy3ed852e2009-09-05 21:47:34 +000010116 if (mng_info->IsPalette)
10117 {
glennrp17a14852010-05-10 03:01:59 +000010118 number_colors=image_colors;
10119
cristy3ed852e2009-09-05 21:47:34 +000010120 if (image_depth <= 8)
10121 {
cristy3ed852e2009-09-05 21:47:34 +000010122 /*
10123 Set image palette.
10124 */
glennrp5af765f2010-03-30 11:12:18 +000010125 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +000010126
glennrp3d627862013-02-26 00:19:34 +000010127 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +000010128 {
cristybb503372010-05-27 20:51:26 +000010129 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000010130 {
10131 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10132 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10133 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10134 }
glennrp0fe50b42010-11-16 03:52:51 +000010135
glennrp3b51f0e2010-11-27 18:14:08 +000010136 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +000010138 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +000010139 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010140
glennrp39992b42010-11-14 00:03:43 +000010141 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010142 }
glennrp0fe50b42010-11-16 03:52:51 +000010143
cristy3ed852e2009-09-05 21:47:34 +000010144 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +000010145 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +000010146 {
cristybefe4d22010-06-07 01:18:58 +000010147 size_t
10148 one;
10149
glennrp5af765f2010-03-30 11:12:18 +000010150 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +000010151 one=1;
glennrp0fe50b42010-11-16 03:52:51 +000010152
cristy16ea1392012-03-21 20:38:41 +000010153 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +000010154 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +000010155 }
glennrp0fe50b42010-11-16 03:52:51 +000010156
glennrp5af765f2010-03-30 11:12:18 +000010157 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +000010158
glennrp58e01762011-01-07 15:28:54 +000010159 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010160 {
glennrp0fe50b42010-11-16 03:52:51 +000010161 /*
glennrpd6bf1612010-12-17 17:28:54 +000010162 * Set up trans_colors array.
10163 */
glennrp0fe50b42010-11-16 03:52:51 +000010164 assert(number_colors <= 256);
10165
glennrpd6bf1612010-12-17 17:28:54 +000010166 ping_num_trans=(unsigned short) (number_transparent +
10167 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +000010168
10169 if (ping_num_trans == 0)
10170 ping_have_tRNS=MagickFalse;
10171
glennrpd6bf1612010-12-17 17:28:54 +000010172 else
glennrp0fe50b42010-11-16 03:52:51 +000010173 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010174 if (logging != MagickFalse)
10175 {
10176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10177 " Scaling ping_trans_color (1)");
10178 }
glennrpd6bf1612010-12-17 17:28:54 +000010179 ping_have_tRNS=MagickTrue;
10180
10181 for (i=0; i < ping_num_trans; i++)
10182 {
glennrp750105b2012-04-25 16:20:45 +000010183 ping_trans_alpha[i]= (png_byte)
cristy16ea1392012-03-21 20:38:41 +000010184 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +000010185 }
glennrp0fe50b42010-11-16 03:52:51 +000010186 }
10187 }
cristy3ed852e2009-09-05 21:47:34 +000010188 }
10189 }
glennrp0fe50b42010-11-16 03:52:51 +000010190
cristy3ed852e2009-09-05 21:47:34 +000010191 else
10192 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010193
cristy3ed852e2009-09-05 21:47:34 +000010194 if (image_depth < 8)
10195 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010196
cristy3ed852e2009-09-05 21:47:34 +000010197 if ((save_image_depth == 16) && (image_depth == 8))
10198 {
glennrp4f25bd02011-01-01 18:51:28 +000010199 if (logging != MagickFalse)
10200 {
10201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10202 " Scaling ping_trans_color from (%d,%d,%d)",
10203 (int) ping_trans_color.red,
10204 (int) ping_trans_color.green,
10205 (int) ping_trans_color.blue);
10206 }
10207
glennrp5af765f2010-03-30 11:12:18 +000010208 ping_trans_color.red*=0x0101;
10209 ping_trans_color.green*=0x0101;
10210 ping_trans_color.blue*=0x0101;
10211 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +000010212
10213 if (logging != MagickFalse)
10214 {
10215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10216 " to (%d,%d,%d)",
10217 (int) ping_trans_color.red,
10218 (int) ping_trans_color.green,
10219 (int) ping_trans_color.blue);
10220 }
cristy3ed852e2009-09-05 21:47:34 +000010221 }
10222 }
10223
cristy4383ec82011-01-05 15:42:32 +000010224 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10225 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +000010226
cristy3ed852e2009-09-05 21:47:34 +000010227 /*
10228 Adjust background and transparency samples in sub-8-bit grayscale files.
10229 */
glennrp5af765f2010-03-30 11:12:18 +000010230 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +000010231 PNG_COLOR_TYPE_GRAY)
10232 {
10233 png_uint_16
10234 maxval;
10235
cristy35ef8242010-06-03 16:24:13 +000010236 size_t
10237 one=1;
10238
cristy22ffd972010-06-03 16:51:47 +000010239 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +000010240
glennrp4f25bd02011-01-01 18:51:28 +000010241 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010242 {
cristy3ed852e2009-09-05 21:47:34 +000010243
cristy16ea1392012-03-21 20:38:41 +000010244 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10245 (ScaleQuantumToShort(((GetPixelInfoIntensity(
10246 &image->background_color))) +.5)));
10247
cristy3ed852e2009-09-05 21:47:34 +000010248 if (logging != MagickFalse)
10249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +000010250 " Setting up bKGD chunk (2)");
10251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10252 " background_color index is %d",
10253 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +000010254
glennrp991d11d2010-11-12 21:55:28 +000010255 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010256 }
cristy3ed852e2009-09-05 21:47:34 +000010257
glennrp3e3e20f2011-06-09 04:21:43 +000010258 if (logging != MagickFalse)
10259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10260 " Scaling ping_trans_color.gray from %d",
10261 (int)ping_trans_color.gray);
10262
glennrp9be9b1c2011-06-09 12:21:45 +000010263 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +000010264 ping_trans_color.gray)+.5);
10265
10266 if (logging != MagickFalse)
10267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10268 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +000010269 }
glennrp17a14852010-05-10 03:01:59 +000010270
glennrp26f37912010-12-23 16:22:42 +000010271 if (ping_exclude_bKGD == MagickFalse)
10272 {
glennrp1273f7b2011-02-24 03:20:30 +000010273 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +000010274 {
10275 /*
10276 Identify which colormap entry is the background color.
10277 */
10278
glennrp17a14852010-05-10 03:01:59 +000010279 number_colors=image_colors;
10280
glennrpa521b2f2010-10-29 04:11:03 +000010281 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10282 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +000010283 break;
10284
10285 ping_background.index=(png_byte) i;
10286
glennrp3b51f0e2010-11-27 18:14:08 +000010287 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +000010288 {
10289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +000010290 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +000010291 }
glennrp0fe50b42010-11-16 03:52:51 +000010292
cristy13d07042010-11-21 20:56:18 +000010293 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +000010294 {
10295 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +000010296
10297 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010298 {
10299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10300 " background =(%d,%d,%d)",
10301 (int) ping_background.red,
10302 (int) ping_background.green,
10303 (int) ping_background.blue);
10304 }
10305 }
glennrpa521b2f2010-10-29 04:11:03 +000010306
glennrpd6bf1612010-12-17 17:28:54 +000010307 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +000010308 {
glennrp3b51f0e2010-11-27 18:14:08 +000010309 if (logging != MagickFalse)
10310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10311 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +000010312 ping_have_bKGD = MagickFalse;
10313 }
glennrp17a14852010-05-10 03:01:59 +000010314 }
glennrp26f37912010-12-23 16:22:42 +000010315 }
glennrp17a14852010-05-10 03:01:59 +000010316
cristy3ed852e2009-09-05 21:47:34 +000010317 if (logging != MagickFalse)
10318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +000010319 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10320 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010321 /*
10322 Initialize compression level and filtering.
10323 */
10324 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010325 {
10326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10327 " Setting up deflate compression");
10328
10329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10330 " Compression buffer size: 32768");
10331 }
10332
cristy3ed852e2009-09-05 21:47:34 +000010333 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +000010334
cristy3ed852e2009-09-05 21:47:34 +000010335 if (logging != MagickFalse)
10336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10337 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +000010338
cristy4054bfb2011-08-29 23:41:39 +000010339 png_set_compression_mem_level(ping, 9);
10340
glennrp10d739e2011-06-29 18:00:52 +000010341 /* Untangle the "-quality" setting:
10342
10343 Undefined is 0; the default is used.
10344 Default is 75
10345
10346 10's digit:
10347
10348 0: Use Z_HUFFMAN_ONLY strategy with the
10349 zlib default compression level
10350
10351 1-9: the zlib compression level
10352
10353 1's digit:
10354
10355 0-4: the PNG filter method
10356
10357 5: libpng adaptive filtering if compression level > 5
10358 libpng filter type "none" if compression level <= 5
10359 or if image is grayscale or palette
glennrp750105b2012-04-25 16:20:45 +000010360
glennrp10d739e2011-06-29 18:00:52 +000010361 6: libpng adaptive filtering
10362
10363 7: "LOCO" filtering (intrapixel differing) if writing
10364 a MNG, othewise "none". Did not work in IM-6.7.0-9
10365 and earlier because of a missing "else".
10366
10367 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +000010368 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +000010369
10370 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +000010371 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +000010372
10373 Note that using the -quality option, not all combinations of
10374 PNG filter type, zlib compression level, and zlib compression
cristy16ea1392012-03-21 20:38:41 +000010375 strategy are possible. This will be addressed soon in a
10376 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +000010377
10378 */
10379
cristy3ed852e2009-09-05 21:47:34 +000010380 quality=image->quality == UndefinedCompressionQuality ? 75UL :
10381 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +000010382
glennrp18682582011-06-30 18:11:47 +000010383 if (quality <= 9)
10384 {
10385 if (mng_info->write_png_compression_strategy == 0)
10386 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10387 }
glennrp750105b2012-04-25 16:20:45 +000010388
glennrp18682582011-06-30 18:11:47 +000010389 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +000010390 {
10391 int
10392 level;
10393
cristybb503372010-05-27 20:51:26 +000010394 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +000010395
glennrp18682582011-06-30 18:11:47 +000010396 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +000010397 }
glennrp0fe50b42010-11-16 03:52:51 +000010398
glennrp18682582011-06-30 18:11:47 +000010399 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +000010400 {
glennrp18682582011-06-30 18:11:47 +000010401 if ((quality %10) == 8 || (quality %10) == 9)
glennrpa24b2452012-06-27 11:38:38 +000010402#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10403 mng_info->write_png_compression_strategy=Z_RLE+1;
10404#else
10405 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10406#endif
cristy3ed852e2009-09-05 21:47:34 +000010407 }
glennrp0fe50b42010-11-16 03:52:51 +000010408
glennrp18682582011-06-30 18:11:47 +000010409 if (mng_info->write_png_compression_filter == 0)
10410 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10411
cristy3ed852e2009-09-05 21:47:34 +000010412 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010413 {
glennrp18682582011-06-30 18:11:47 +000010414 if (mng_info->write_png_compression_level)
10415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10416 " Compression level: %d",
10417 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +000010418
glennrp18682582011-06-30 18:11:47 +000010419 if (mng_info->write_png_compression_strategy)
10420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10421 " Compression strategy: %d",
10422 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +000010423
glennrp18682582011-06-30 18:11:47 +000010424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10425 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +000010426
cristy4054bfb2011-08-29 23:41:39 +000010427 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +000010428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10429 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +000010430 else if (mng_info->write_png_compression_filter == 0 ||
10431 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +000010432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10433 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +000010434 else
10435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10436 " Base filter method: %d",
10437 (int) mng_info->write_png_compression_filter-1);
10438 }
glennrp2b013e42010-11-24 16:55:50 +000010439
glennrp18682582011-06-30 18:11:47 +000010440 if (mng_info->write_png_compression_level != 0)
10441 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10442
10443 if (mng_info->write_png_compression_filter == 6)
10444 {
10445 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10446 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10447 (quality < 50))
10448 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10449 else
10450 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10451 }
cristy4054bfb2011-08-29 23:41:39 +000010452 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +000010453 mng_info->write_png_compression_filter == 10)
10454 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10455
10456 else if (mng_info->write_png_compression_filter == 8)
10457 {
10458#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10459 if (mng_info->write_mng)
10460 {
10461 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10462 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10463 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10464 }
10465#endif
cristy4054bfb2011-08-29 23:41:39 +000010466 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +000010467 }
10468
10469 else if (mng_info->write_png_compression_filter == 9)
10470 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10471
10472 else if (mng_info->write_png_compression_filter != 0)
10473 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10474 mng_info->write_png_compression_filter-1);
10475
10476 if (mng_info->write_png_compression_strategy != 0)
10477 png_set_compression_strategy(ping,
10478 mng_info->write_png_compression_strategy-1);
10479
glennrpdec72c92013-02-26 17:42:47 +000010480 ping_interlace_method=image_info->interlace != NoInterlace;
10481
10482 if (mng_info->write_mng)
10483 png_set_sig_bytes(ping,8);
10484
10485 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10486
10487 if (mng_info->write_png_colortype != 0)
10488 {
10489 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10490 if (ping_have_color != MagickFalse)
10491 {
10492 ping_color_type = PNG_COLOR_TYPE_RGB;
10493
10494 if (ping_bit_depth < 8)
10495 ping_bit_depth=8;
10496 }
10497
10498 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10499 if (ping_have_color != MagickFalse)
10500 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10501 }
10502
10503 if (ping_need_colortype_warning != MagickFalse ||
10504 ((mng_info->write_png_depth &&
10505 (int) mng_info->write_png_depth != ping_bit_depth) ||
10506 (mng_info->write_png_colortype &&
10507 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10508 mng_info->write_png_colortype != 7 &&
10509 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10510 {
10511 if (logging != MagickFalse)
10512 {
10513 if (ping_need_colortype_warning != MagickFalse)
10514 {
10515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10516 " Image has transparency but tRNS chunk was excluded");
10517 }
10518
10519 if (mng_info->write_png_depth)
10520 {
10521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10522 " Defined png:bit-depth=%u, Computed depth=%u",
10523 mng_info->write_png_depth,
10524 ping_bit_depth);
10525 }
10526
10527 if (mng_info->write_png_colortype)
10528 {
10529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10530 " Defined png:color-type=%u, Computed color type=%u",
10531 mng_info->write_png_colortype-1,
10532 ping_color_type);
10533 }
10534 }
10535
10536 png_warning(ping,
10537 "Cannot write image with defined png:bit-depth or png:color-type.");
10538 }
10539
10540 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10541 {
10542 /* Add an opaque matte channel */
10543 image->alpha_trait = BlendPixelTrait;
10544 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10545
10546 if (logging != MagickFalse)
10547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10548 " Added an opaque matte channel");
10549 }
10550
10551 if (number_transparent != 0 || number_semitransparent != 0)
10552 {
10553 if (ping_color_type < 4)
10554 {
10555 ping_have_tRNS=MagickTrue;
10556 if (logging != MagickFalse)
10557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10558 " Setting ping_have_tRNS=MagickTrue.");
10559 }
10560 }
10561
10562 if (logging != MagickFalse)
10563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10564 " Writing PNG header chunks");
10565
10566 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10567 ping_bit_depth,ping_color_type,
10568 ping_interlace_method,ping_compression_method,
10569 ping_filter_method);
10570
10571 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10572 {
10573 png_set_PLTE(ping,ping_info,palette,number_colors);
10574
10575 if (logging != MagickFalse)
10576 {
10577 for (i=0; i< (ssize_t) number_colors; i++)
10578 {
10579 if (i < ping_num_trans)
10580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10581 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10582 (int) i,
10583 (int) palette[i].red,
10584 (int) palette[i].green,
10585 (int) palette[i].blue,
10586 (int) i,
10587 (int) ping_trans_alpha[i]);
10588 else
10589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10590 " PLTE[%d] = (%d,%d,%d)",
10591 (int) i,
10592 (int) palette[i].red,
10593 (int) palette[i].green,
10594 (int) palette[i].blue);
10595 }
10596 }
10597 }
10598
cristy0d57eec2011-09-04 22:13:56 +000010599 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10600 if (ping_exclude_sRGB != MagickFalse ||
glennrp3d627862013-02-26 00:19:34 +000010601 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy0d57eec2011-09-04 22:13:56 +000010602 {
10603 if ((ping_exclude_tEXt == MagickFalse ||
10604 ping_exclude_zTXt == MagickFalse) &&
10605 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +000010606 {
10607 ResetImageProfileIterator(image);
10608 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +000010609 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010610 profile=GetImageProfile(image,name);
10611
10612 if (profile != (StringInfo *) NULL)
10613 {
glennrp5af765f2010-03-30 11:12:18 +000010614#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +000010615 if ((LocaleCompare(name,"ICC") == 0) ||
10616 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +000010617 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010618
10619 if (ping_exclude_iCCP == MagickFalse)
10620 {
glennrpecab7d72013-05-14 22:50:32 +000010621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10622 " Setting up iCCP chunk");
10623
cristy16ea1392012-03-21 20:38:41 +000010624 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +000010625#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +000010626 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +000010627#else
10628 (png_const_bytep) GetStringInfoDatum(profile),
10629#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010630 (png_uint_32) GetStringInfoLength(profile));
glennrp918b9dc2013-04-03 13:41:41 +000010631 ping_have_iCCP = MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010632 }
glennrp26f37912010-12-23 16:22:42 +000010633 }
glennrp0fe50b42010-11-16 03:52:51 +000010634
glennrpc8cbc5d2011-01-01 00:12:34 +000010635 else
cristy3ed852e2009-09-05 21:47:34 +000010636#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010637 if (ping_exclude_zCCP == MagickFalse)
10638 {
glennrpecab7d72013-05-14 22:50:32 +000010639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10640 " Setting up zTXT chunk with uuencoded ICC");
glennrpcf002022011-01-30 02:38:15 +000010641 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +000010642 (unsigned char *) name,(unsigned char *) name,
10643 GetStringInfoDatum(profile),
10644 (png_uint_32) GetStringInfoLength(profile));
glennrpecab7d72013-05-14 22:50:32 +000010645 ping_have_iCCP = MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010646 }
10647 }
glennrp0b206f52011-01-07 04:55:32 +000010648
glennrpc8cbc5d2011-01-01 00:12:34 +000010649 if (logging != MagickFalse)
10650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10651 " Setting up text chunk with %s profile",name);
10652
10653 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +000010654 }
cristy0d57eec2011-09-04 22:13:56 +000010655 }
cristy3ed852e2009-09-05 21:47:34 +000010656 }
10657
10658#if defined(PNG_WRITE_sRGB_SUPPORTED)
10659 if ((mng_info->have_write_global_srgb == 0) &&
glennrpecab7d72013-05-14 22:50:32 +000010660 ping_have_iCCP != MagickTrue &&
10661 (ping_have_sRGB != MagickFalse ||
10662 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +000010663 {
glennrp26f37912010-12-23 16:22:42 +000010664 if (ping_exclude_sRGB == MagickFalse)
10665 {
10666 /*
10667 Note image rendering intent.
10668 */
10669 if (logging != MagickFalse)
10670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10671 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +000010672
glennrp26f37912010-12-23 16:22:42 +000010673 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +000010674 Magick_RenderingIntent_to_PNG_RenderingIntent(
10675 image->rendering_intent)));
glennrp918b9dc2013-04-03 13:41:41 +000010676
10677 ping_have_sRGB = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010678 }
cristy3ed852e2009-09-05 21:47:34 +000010679 }
glennrp26f37912010-12-23 16:22:42 +000010680
glennrp5af765f2010-03-30 11:12:18 +000010681 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +000010682#endif
10683 {
glennrp2cc891a2010-12-24 13:44:32 +000010684 if (ping_exclude_gAMA == MagickFalse &&
glennrp918b9dc2013-04-03 13:41:41 +000010685 ping_have_iCCP == MagickFalse &&
10686 ping_have_sRGB == MagickFalse &&
glennrp2cc891a2010-12-24 13:44:32 +000010687 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +000010688 (image->gamma < .45 || image->gamma > .46)))
10689 {
cristy3ed852e2009-09-05 21:47:34 +000010690 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10691 {
10692 /*
10693 Note image gamma.
10694 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10695 */
10696 if (logging != MagickFalse)
10697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10698 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010699
cristy3ed852e2009-09-05 21:47:34 +000010700 png_set_gAMA(ping,ping_info,image->gamma);
10701 }
glennrp26f37912010-12-23 16:22:42 +000010702 }
glennrp2b013e42010-11-24 16:55:50 +000010703
glennrp918b9dc2013-04-03 13:41:41 +000010704 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010705 {
glennrp26f37912010-12-23 16:22:42 +000010706 if ((mng_info->have_write_global_chrm == 0) &&
10707 (image->chromaticity.red_primary.x != 0.0))
10708 {
10709 /*
10710 Note image chromaticity.
glennrp918b9dc2013-04-03 13:41:41 +000010711 Note: if cHRM+gAMA == sRGB write sRGB instead.
glennrp26f37912010-12-23 16:22:42 +000010712 */
10713 PrimaryInfo
10714 bp,
10715 gp,
10716 rp,
10717 wp;
cristy3ed852e2009-09-05 21:47:34 +000010718
glennrp26f37912010-12-23 16:22:42 +000010719 wp=image->chromaticity.white_point;
10720 rp=image->chromaticity.red_primary;
10721 gp=image->chromaticity.green_primary;
10722 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010723
glennrp26f37912010-12-23 16:22:42 +000010724 if (logging != MagickFalse)
10725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10726 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010727
glennrp26f37912010-12-23 16:22:42 +000010728 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10729 bp.x,bp.y);
10730 }
10731 }
cristy3ed852e2009-09-05 21:47:34 +000010732 }
glennrpdfd70802010-11-14 01:23:35 +000010733
glennrp26f37912010-12-23 16:22:42 +000010734 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010735 {
glennrp26f37912010-12-23 16:22:42 +000010736 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010737 {
glennrp26f37912010-12-23 16:22:42 +000010738 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010739 if (logging)
10740 {
10741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10742 " Setting up bKGD chunk");
10743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10744 " background color = (%d,%d,%d)",
10745 (int) ping_background.red,
10746 (int) ping_background.green,
10747 (int) ping_background.blue);
10748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10749 " index = %d, gray=%d",
10750 (int) ping_background.index,
10751 (int) ping_background.gray);
10752 }
10753 }
glennrp26f37912010-12-23 16:22:42 +000010754 }
10755
10756 if (ping_exclude_pHYs == MagickFalse)
10757 {
10758 if (ping_have_pHYs != MagickFalse)
10759 {
10760 png_set_pHYs(ping,ping_info,
10761 ping_pHYs_x_resolution,
10762 ping_pHYs_y_resolution,
10763 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010764
10765 if (logging)
10766 {
10767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10768 " Setting up pHYs chunk");
10769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10770 " x_resolution=%lu",
10771 (unsigned long) ping_pHYs_x_resolution);
10772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10773 " y_resolution=%lu",
10774 (unsigned long) ping_pHYs_y_resolution);
10775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10776 " unit_type=%lu",
10777 (unsigned long) ping_pHYs_unit_type);
10778 }
glennrp26f37912010-12-23 16:22:42 +000010779 }
glennrpdfd70802010-11-14 01:23:35 +000010780 }
10781
10782#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010783 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010784 {
glennrp26f37912010-12-23 16:22:42 +000010785 if (image->page.x || image->page.y)
10786 {
10787 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10788 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010789
glennrp26f37912010-12-23 16:22:42 +000010790 if (logging != MagickFalse)
10791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10792 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10793 (int) image->page.x, (int) image->page.y);
10794 }
glennrpdfd70802010-11-14 01:23:35 +000010795 }
10796#endif
10797
glennrpda8f3a72011-02-27 23:54:12 +000010798 if (mng_info->need_blob != MagickFalse)
10799 {
cristy16ea1392012-03-21 20:38:41 +000010800 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010801 MagickFalse)
10802 png_error(ping,"WriteBlob Failed");
10803
10804 ping_have_blob=MagickTrue;
10805 }
10806
cristy3ed852e2009-09-05 21:47:34 +000010807 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010808
glennrp39992b42010-11-14 00:03:43 +000010809 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010810 {
glennrp3b51f0e2010-11-27 18:14:08 +000010811 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010812 {
10813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10814 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10815 }
10816
10817 if (ping_color_type == 3)
10818 (void) png_set_tRNS(ping, ping_info,
10819 ping_trans_alpha,
10820 ping_num_trans,
10821 NULL);
10822
10823 else
10824 {
10825 (void) png_set_tRNS(ping, ping_info,
10826 NULL,
10827 0,
10828 &ping_trans_color);
10829
glennrp3b51f0e2010-11-27 18:14:08 +000010830 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010831 {
10832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010833 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010834 (int) ping_trans_color.red,
10835 (int) ping_trans_color.green,
10836 (int) ping_trans_color.blue);
10837 }
10838 }
glennrp991d11d2010-11-12 21:55:28 +000010839 }
10840
cristy3ed852e2009-09-05 21:47:34 +000010841 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010842 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010843
cristy3ed852e2009-09-05 21:47:34 +000010844 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010845
cristy3ed852e2009-09-05 21:47:34 +000010846 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010847 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010848
glennrp26f37912010-12-23 16:22:42 +000010849 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010850 {
glennrp4f25bd02011-01-01 18:51:28 +000010851 if ((image->page.width != 0 && image->page.width != image->columns) ||
10852 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010853 {
10854 unsigned char
10855 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010856
glennrp26f37912010-12-23 16:22:42 +000010857 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10858 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010859 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010860 PNGLong(chunk+4,(png_uint_32) image->page.width);
10861 PNGLong(chunk+8,(png_uint_32) image->page.height);
10862 chunk[12]=0; /* unit = pixels */
10863 (void) WriteBlob(image,13,chunk);
10864 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10865 }
cristy3ed852e2009-09-05 21:47:34 +000010866 }
10867
10868#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010869 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010870#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010871 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010872#undef PNG_HAVE_IDAT
10873#endif
10874
10875 png_set_packing(ping);
10876 /*
10877 Allocate memory.
10878 */
10879 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010880 if (image_depth > 8)
10881 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010882 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010883 {
glennrpb4a13412010-05-05 12:47:19 +000010884 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010885 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010886 break;
glennrp0fe50b42010-11-16 03:52:51 +000010887
glennrpb4a13412010-05-05 12:47:19 +000010888 case PNG_COLOR_TYPE_GRAY_ALPHA:
10889 rowbytes*=2;
10890 break;
glennrp0fe50b42010-11-16 03:52:51 +000010891
glennrpb4a13412010-05-05 12:47:19 +000010892 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010893 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010894 break;
glennrp0fe50b42010-11-16 03:52:51 +000010895
glennrpb4a13412010-05-05 12:47:19 +000010896 default:
10897 break;
cristy3ed852e2009-09-05 21:47:34 +000010898 }
glennrp3b51f0e2010-11-27 18:14:08 +000010899
10900 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010901 {
10902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10903 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010904
glennrpb4a13412010-05-05 12:47:19 +000010905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010906 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010907 }
cristy09973322013-06-30 21:06:30 +000010908 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10909 if (pixel_info == (MemoryInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010910 png_error(ping,"Allocation of memory for pixels failed");
cristy09973322013-06-30 21:06:30 +000010911 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
glennrp0fe50b42010-11-16 03:52:51 +000010912
cristy3ed852e2009-09-05 21:47:34 +000010913 /*
10914 Initialize image scanlines.
10915 */
cristyed552522009-10-16 14:04:35 +000010916 quantum_info=AcquireQuantumInfo(image_info,image);
10917 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010918 png_error(ping,"Memory allocation for quantum_info failed");
cristy3ed852e2009-09-05 21:47:34 +000010919 quantum_info->format=UndefinedQuantumFormat;
10920 quantum_info->depth=image_depth;
glennrp4b840d72012-11-22 16:01:16 +000010921 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
cristy3ed852e2009-09-05 21:47:34 +000010922 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010923
cristy3ed852e2009-09-05 21:47:34 +000010924 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrpfd164d22013-01-26 21:10:22 +000010925 !mng_info->write_png48 && !mng_info->write_png64 &&
glennrp8bb3a022010-12-13 20:40:04 +000010926 !mng_info->write_png32) &&
10927 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010928 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010929 image_matte == MagickFalse &&
10930 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010931 {
glennrp8bb3a022010-12-13 20:40:04 +000010932 /* Palette, Bilevel, or Opaque Monochrome */
cristy16ea1392012-03-21 20:38:41 +000010933 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010934 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010935
cristy3ed852e2009-09-05 21:47:34 +000010936 quantum_info->depth=8;
10937 for (pass=0; pass < num_passes; pass++)
10938 {
10939 /*
10940 Convert PseudoClass image to a PNG monochrome image.
10941 */
cristybb503372010-05-27 20:51:26 +000010942 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010943 {
glennrpd71e86a2011-02-24 01:28:37 +000010944 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10946 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010947
cristy16ea1392012-03-21 20:38:41 +000010948 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010949
cristy16ea1392012-03-21 20:38:41 +000010950 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010951 break;
glennrp0fe50b42010-11-16 03:52:51 +000010952
cristy3ed852e2009-09-05 21:47:34 +000010953 if (mng_info->IsPalette)
10954 {
cristy16ea1392012-03-21 20:38:41 +000010955 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10956 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010957 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10958 mng_info->write_png_depth &&
10959 mng_info->write_png_depth != old_bit_depth)
10960 {
10961 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010962 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010963 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010964 >> (8-old_bit_depth));
10965 }
10966 }
glennrp0fe50b42010-11-16 03:52:51 +000010967
cristy3ed852e2009-09-05 21:47:34 +000010968 else
10969 {
cristy16ea1392012-03-21 20:38:41 +000010970 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10971 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010972 }
glennrp0fe50b42010-11-16 03:52:51 +000010973
cristy3ed852e2009-09-05 21:47:34 +000010974 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010975 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010976 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010977 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010978
glennrp3b51f0e2010-11-27 18:14:08 +000010979 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10981 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010982
glennrpcf002022011-01-30 02:38:15 +000010983 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010984 }
10985 if (image->previous == (Image *) NULL)
10986 {
10987 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10988 if (status == MagickFalse)
10989 break;
10990 }
10991 }
10992 }
glennrp0fe50b42010-11-16 03:52:51 +000010993
glennrp8bb3a022010-12-13 20:40:04 +000010994 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010995 {
glennrp0fe50b42010-11-16 03:52:51 +000010996 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrpfd164d22013-01-26 21:10:22 +000010997 !mng_info->write_png48 && !mng_info->write_png64 &&
10998 !mng_info->write_png32) && (image_matte != MagickFalse ||
10999 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11000 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000011001 {
cristy16ea1392012-03-21 20:38:41 +000011002 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000011003 *p;
glennrp0fe50b42010-11-16 03:52:51 +000011004
glennrp8bb3a022010-12-13 20:40:04 +000011005 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000011006 {
glennrp8bb3a022010-12-13 20:40:04 +000011007
cristybb503372010-05-27 20:51:26 +000011008 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000011009 {
cristy16ea1392012-03-21 20:38:41 +000011010 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011011
cristy16ea1392012-03-21 20:38:41 +000011012 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000011013 break;
glennrp2cc891a2010-12-24 13:44:32 +000011014
glennrp5af765f2010-03-30 11:12:18 +000011015 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000011016 {
glennrp8bb3a022010-12-13 20:40:04 +000011017 if (mng_info->IsPalette)
cristy16ea1392012-03-21 20:38:41 +000011018 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11019 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011020
glennrp8bb3a022010-12-13 20:40:04 +000011021 else
cristy16ea1392012-03-21 20:38:41 +000011022 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11023 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011024
glennrp3b51f0e2010-11-27 18:14:08 +000011025 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000011026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000011027 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000011028 }
glennrp2cc891a2010-12-24 13:44:32 +000011029
glennrp8bb3a022010-12-13 20:40:04 +000011030 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11031 {
11032 if (logging != MagickFalse && y == 0)
11033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11034 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000011035
cristy16ea1392012-03-21 20:38:41 +000011036 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11037 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000011038 }
glennrp2cc891a2010-12-24 13:44:32 +000011039
glennrp3b51f0e2010-11-27 18:14:08 +000011040 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000011041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000011042 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000011043
glennrpcf002022011-01-30 02:38:15 +000011044 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000011045 }
glennrp2cc891a2010-12-24 13:44:32 +000011046
glennrp8bb3a022010-12-13 20:40:04 +000011047 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000011048 {
glennrp8bb3a022010-12-13 20:40:04 +000011049 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11050 if (status == MagickFalse)
11051 break;
cristy3ed852e2009-09-05 21:47:34 +000011052 }
cristy3ed852e2009-09-05 21:47:34 +000011053 }
11054 }
glennrp8bb3a022010-12-13 20:40:04 +000011055
11056 else
11057 {
cristy16ea1392012-03-21 20:38:41 +000011058 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000011059 *p;
11060
11061 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000011062 {
glennrpfd164d22013-01-26 21:10:22 +000011063 if ((image_depth > 8) ||
11064 mng_info->write_png24 ||
glennrp8bb3a022010-12-13 20:40:04 +000011065 mng_info->write_png32 ||
glennrpfd164d22013-01-26 21:10:22 +000011066 mng_info->write_png48 ||
11067 mng_info->write_png64 ||
11068 (!mng_info->write_png8 && !mng_info->IsPalette))
glennrp8bb3a022010-12-13 20:40:04 +000011069 {
11070 for (y=0; y < (ssize_t) image->rows; y++)
11071 {
cristy862a33c2012-05-17 22:49:37 +000011072 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000011073
cristy16ea1392012-03-21 20:38:41 +000011074 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000011075 break;
glennrp2cc891a2010-12-24 13:44:32 +000011076
glennrp8bb3a022010-12-13 20:40:04 +000011077 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11078 {
11079 if (image->storage_class == DirectClass)
cristy16ea1392012-03-21 20:38:41 +000011080 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11081 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011082
glennrp8bb3a022010-12-13 20:40:04 +000011083 else
cristy16ea1392012-03-21 20:38:41 +000011084 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11085 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000011086 }
glennrp2cc891a2010-12-24 13:44:32 +000011087
glennrp8bb3a022010-12-13 20:40:04 +000011088 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11089 {
cristy16ea1392012-03-21 20:38:41 +000011090 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000011091 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000011092 exception);
glennrp2cc891a2010-12-24 13:44:32 +000011093
glennrp8bb3a022010-12-13 20:40:04 +000011094 if (logging != MagickFalse && y == 0)
11095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11096 " Writing GRAY_ALPHA PNG pixels (3)");
11097 }
glennrp2cc891a2010-12-24 13:44:32 +000011098
glennrp8bb3a022010-12-13 20:40:04 +000011099 else if (image_matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +000011100 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11101 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011102
glennrp8bb3a022010-12-13 20:40:04 +000011103 else
cristy16ea1392012-03-21 20:38:41 +000011104 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11105 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011106
glennrp8bb3a022010-12-13 20:40:04 +000011107 if (logging != MagickFalse && y == 0)
11108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11109 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000011110
glennrpcf002022011-01-30 02:38:15 +000011111 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000011112 }
11113 }
glennrp2cc891a2010-12-24 13:44:32 +000011114
glennrp8bb3a022010-12-13 20:40:04 +000011115 else
glennrpfd164d22013-01-26 21:10:22 +000011116 /* not ((image_depth > 8) ||
11117 mng_info->write_png24 || mng_info->write_png32 ||
11118 mng_info->write_png48 || mng_info->write_png64 ||
11119 (!mng_info->write_png8 && !mng_info->IsPalette))
11120 */
glennrp8bb3a022010-12-13 20:40:04 +000011121 {
11122 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11123 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11124 {
11125 if (logging != MagickFalse)
11126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11127 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000011128
glennrp8bb3a022010-12-13 20:40:04 +000011129 quantum_info->depth=8;
11130 image_depth=8;
11131 }
glennrp2cc891a2010-12-24 13:44:32 +000011132
glennrp8bb3a022010-12-13 20:40:04 +000011133 for (y=0; y < (ssize_t) image->rows; y++)
11134 {
11135 if (logging != MagickFalse && y == 0)
11136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11137 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000011138
cristy16ea1392012-03-21 20:38:41 +000011139 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000011140
cristy16ea1392012-03-21 20:38:41 +000011141 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000011142 break;
glennrp2cc891a2010-12-24 13:44:32 +000011143
glennrp8bb3a022010-12-13 20:40:04 +000011144 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000011145 {
glennrp4bf89732011-03-21 13:48:28 +000011146 quantum_info->depth=image->depth;
11147
cristy16ea1392012-03-21 20:38:41 +000011148 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11149 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000011150 }
glennrp2cc891a2010-12-24 13:44:32 +000011151
glennrp8bb3a022010-12-13 20:40:04 +000011152 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11153 {
11154 if (logging != MagickFalse && y == 0)
11155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11156 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000011157
cristy16ea1392012-03-21 20:38:41 +000011158 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000011159 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000011160 exception);
glennrp8bb3a022010-12-13 20:40:04 +000011161 }
glennrp2cc891a2010-12-24 13:44:32 +000011162
glennrp8bb3a022010-12-13 20:40:04 +000011163 else
glennrp8bb3a022010-12-13 20:40:04 +000011164 {
cristy16ea1392012-03-21 20:38:41 +000011165 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11166 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000011167
11168 if (logging != MagickFalse && y <= 2)
11169 {
11170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000011171 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000011172
11173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11174 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11175 (int)ping_pixels[0],(int)ping_pixels[1]);
11176 }
glennrp8bb3a022010-12-13 20:40:04 +000011177 }
glennrpcf002022011-01-30 02:38:15 +000011178 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000011179 }
11180 }
glennrp2cc891a2010-12-24 13:44:32 +000011181
glennrp8bb3a022010-12-13 20:40:04 +000011182 if (image->previous == (Image *) NULL)
11183 {
11184 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11185 if (status == MagickFalse)
11186 break;
11187 }
cristy3ed852e2009-09-05 21:47:34 +000011188 }
glennrp8bb3a022010-12-13 20:40:04 +000011189 }
11190 }
11191
cristyb32b90a2009-09-07 21:45:48 +000011192 if (quantum_info != (QuantumInfo *) NULL)
11193 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000011194
11195 if (logging != MagickFalse)
11196 {
11197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000011198 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000011199
cristy3ed852e2009-09-05 21:47:34 +000011200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011201 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000011202
cristy3ed852e2009-09-05 21:47:34 +000011203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011204 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000011205
cristy3ed852e2009-09-05 21:47:34 +000011206 if (mng_info->write_png_depth)
11207 {
11208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011209 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011210 }
glennrp0fe50b42010-11-16 03:52:51 +000011211
cristy3ed852e2009-09-05 21:47:34 +000011212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000011213 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011214
cristy3ed852e2009-09-05 21:47:34 +000011215 if (mng_info->write_png_colortype)
11216 {
11217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011218 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011219 }
glennrp0fe50b42010-11-16 03:52:51 +000011220
cristy3ed852e2009-09-05 21:47:34 +000011221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000011222 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011223
cristy3ed852e2009-09-05 21:47:34 +000011224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000011225 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000011226 }
11227 /*
glennrpa0ed0092011-04-18 16:36:29 +000011228 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000011229 */
glennrp823b55c2011-03-14 18:46:46 +000011230 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000011231 {
glennrp26f37912010-12-23 16:22:42 +000011232 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000011233 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000011234 while (property != (const char *) NULL)
11235 {
11236 png_textp
11237 text;
glennrp2cc891a2010-12-24 13:44:32 +000011238
cristy16ea1392012-03-21 20:38:41 +000011239 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000011240
glennrpe4d5faf2013-07-22 00:11:04 +000011241 /* Don't write any "png:" or "jpeg:" properties; those are just for
11242 * "identify" or for passing through to another JPEG
11243 */
11244 if ((LocaleNCompare(property,"png:",4) != 0 &&
11245 LocaleNCompare(property,"jpeg:",5)) &&
11246
glennrpa0ed0092011-04-18 16:36:29 +000011247
11248 /* Suppress density and units if we wrote a pHYs chunk */
11249 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000011250 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000011251 LocaleCompare(property,"units") != 0) &&
11252
11253 /* Suppress the IM-generated Date:create and Date:modify */
11254 (ping_exclude_date == MagickFalse ||
11255 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000011256 {
glennrpc70af4a2011-03-07 00:08:23 +000011257 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000011258 {
cristya865ccd2012-07-28 00:33:10 +000011259
glennrpecab7d72013-05-14 22:50:32 +000011260#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +000011261 text=(png_textp) png_malloc(ping,
11262 (png_alloc_size_t) sizeof(png_text));
11263#else
11264 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11265#endif
glennrpc70af4a2011-03-07 00:08:23 +000011266 text[0].key=(char *) property;
11267 text[0].text=(char *) value;
11268 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000011269
glennrpc70af4a2011-03-07 00:08:23 +000011270 if (ping_exclude_tEXt != MagickFalse)
11271 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11272
11273 else if (ping_exclude_zTXt != MagickFalse)
11274 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11275
11276 else
glennrp26f37912010-12-23 16:22:42 +000011277 {
glennrpc70af4a2011-03-07 00:08:23 +000011278 text[0].compression=image_info->compression == NoCompression ||
11279 (image_info->compression == UndefinedCompression &&
11280 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11281 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000011282 }
glennrp2cc891a2010-12-24 13:44:32 +000011283
glennrpc70af4a2011-03-07 00:08:23 +000011284 if (logging != MagickFalse)
11285 {
11286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11287 " Setting up text chunk");
11288
11289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpcbc92152013-02-04 15:46:22 +000011290 " keyword: '%s'",text[0].key);
glennrpc70af4a2011-03-07 00:08:23 +000011291 }
11292
11293 png_set_text(ping,ping_info,text,1);
11294 png_free(ping,text);
11295 }
glennrp26f37912010-12-23 16:22:42 +000011296 }
11297 property=GetNextImageProperty(image);
11298 }
cristy3ed852e2009-09-05 21:47:34 +000011299 }
11300
11301 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011302 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011303
11304 if (logging != MagickFalse)
11305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11306 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000011307
cristy3ed852e2009-09-05 21:47:34 +000011308 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000011309
cristy3ed852e2009-09-05 21:47:34 +000011310 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11311 {
11312 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000011313 (ping_width != mng_info->page.width) ||
11314 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000011315 {
11316 unsigned char
11317 chunk[32];
11318
11319 /*
11320 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11321 */
11322 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11323 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000011324 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000011325 chunk[4]=4;
11326 chunk[5]=0; /* frame name separator (no name) */
11327 chunk[6]=1; /* flag for changing delay, for next frame only */
11328 chunk[7]=0; /* flag for changing frame timeout */
11329 chunk[8]=1; /* flag for changing frame clipping for next frame */
11330 chunk[9]=0; /* flag for changing frame sync_id */
11331 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11332 chunk[14]=0; /* clipping boundaries delta type */
11333 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11334 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000011335 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000011336 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11337 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000011338 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000011339 (void) WriteBlob(image,31,chunk);
11340 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11341 mng_info->old_framing_mode=4;
11342 mng_info->framing_mode=1;
11343 }
glennrp0fe50b42010-11-16 03:52:51 +000011344
cristy3ed852e2009-09-05 21:47:34 +000011345 else
11346 mng_info->framing_mode=3;
11347 }
11348 if (mng_info->write_mng && !mng_info->need_fram &&
11349 ((int) image->dispose == 3))
glennrpedaa0382012-04-12 14:16:21 +000011350 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
glennrp0fe50b42010-11-16 03:52:51 +000011351
cristy3ed852e2009-09-05 21:47:34 +000011352 /*
11353 Free PNG resources.
11354 */
glennrp5af765f2010-03-30 11:12:18 +000011355
cristy3ed852e2009-09-05 21:47:34 +000011356 png_destroy_write_struct(&ping,&ping_info);
11357
cristy09973322013-06-30 21:06:30 +000011358 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +000011359
cristy16ea1392012-03-21 20:38:41 +000011360 if (ping_have_blob != MagickFalse)
11361 (void) CloseBlob(image);
11362
11363 image_info=DestroyImageInfo(image_info);
11364 image=DestroyImage(image);
11365
glennrpb9cfe272010-12-21 15:08:06 +000011366 /* Store bit depth actually written */
11367 s[0]=(char) ping_bit_depth;
11368 s[1]='\0';
11369
cristy16ea1392012-03-21 20:38:41 +000011370 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000011371
cristy3ed852e2009-09-05 21:47:34 +000011372 if (logging != MagickFalse)
11373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11374 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011375
glennrpedaa0382012-04-12 14:16:21 +000011376#ifdef PNG_SETJMP_NOT_THREAD_SAFE
11377 UnlockSemaphoreInfo(ping_semaphore);
11378#endif
11379
11380 /* } for navigation to beginning of SETJMP-protected block. Revert to
11381 * Throwing an Exception when an error occurs.
11382 */
11383
cristy3ed852e2009-09-05 21:47:34 +000011384 return(MagickTrue);
11385/* End write one PNG image */
glennrpedaa0382012-04-12 14:16:21 +000011386
cristy3ed852e2009-09-05 21:47:34 +000011387}
11388
11389/*
11390%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11391% %
11392% %
11393% %
11394% W r i t e P N G I m a g e %
11395% %
11396% %
11397% %
11398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11399%
11400% WritePNGImage() writes a Portable Network Graphics (PNG) or
11401% Multiple-image Network Graphics (MNG) image file.
11402%
11403% MNG support written by Glenn Randers-Pehrson, glennrp@image...
11404%
11405% The format of the WritePNGImage method is:
11406%
cristy16ea1392012-03-21 20:38:41 +000011407% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11408% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011409%
11410% A description of each parameter follows:
11411%
11412% o image_info: the image info.
11413%
11414% o image: The image.
11415%
cristy16ea1392012-03-21 20:38:41 +000011416% o exception: return any errors or warnings in this structure.
11417%
cristy3ed852e2009-09-05 21:47:34 +000011418% Returns MagickTrue on success, MagickFalse on failure.
11419%
11420% Communicating with the PNG encoder:
11421%
11422% While the datastream written is always in PNG format and normally would
11423% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000011424% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000011425%
glennrp5a39f372011-02-25 04:52:16 +000011426% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11427% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000011428% is present, the tRNS chunk must only have values 0 and 255
11429% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000011430% transparent). If other values are present they will be
11431% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000011432% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000011433% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11434% of any resulting fully-transparent pixels is changed to
11435% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000011436%
11437% If you want better quantization or dithering of the colors
11438% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000011439% PNG encoder. The pixels contain 8-bit indices even if
11440% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000011441% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000011442% PNG grayscale type might be slightly more efficient. Please
11443% note that writing to the PNG8 format may result in loss
11444% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000011445%
11446% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11447% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000011448% one of the colors as transparent. The only loss incurred
11449% is reduction of sample depth to 8. If the image has more
11450% than one transparent color, has semitransparent pixels, or
11451% has an opaque pixel with the same RGB components as the
11452% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000011453%
11454% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11455% transparency is permitted, i.e., the alpha sample for
11456% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000011457% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000011458% The only loss in data is the reduction of the sample depth
11459% to 8.
cristy3ed852e2009-09-05 21:47:34 +000011460%
glennrpfd164d22013-01-26 21:10:22 +000011461% o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11462% chunk can be present to convey binary transparency by naming
11463% one of the colors as transparent. If the image has more
11464% than one transparent color, has semitransparent pixels, or
11465% has an opaque pixel with the same RGB components as the
11466% transparent color, an image is not written.
11467%
11468% o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11469% transparency is permitted, i.e., the alpha sample for
11470% each pixel can have any value from 0 to 65535. The alpha
11471% channel is present even if the image is fully opaque.
11472%
glennrp5830fbc2013-01-27 06:11:45 +000011473% o PNG00: A PNG that inherits its colortype and bit-depth from the input
11474% image, if the input was a PNG, is written. If these values
11475% cannot be found, then "PNG00" falls back to the regular "PNG"
11476% format.
11477%
cristy3ed852e2009-09-05 21:47:34 +000011478% o -define: For more precise control of the PNG output, you can use the
11479% Image options "png:bit-depth" and "png:color-type". These
11480% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000011481% from the application programming interfaces. The options
11482% are case-independent and are converted to lowercase before
11483% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000011484%
11485% png:color-type can be 0, 2, 3, 4, or 6.
11486%
11487% When png:color-type is 0 (Grayscale), png:bit-depth can
11488% be 1, 2, 4, 8, or 16.
11489%
11490% When png:color-type is 2 (RGB), png:bit-depth can
11491% be 8 or 16.
11492%
11493% When png:color-type is 3 (Indexed), png:bit-depth can
11494% be 1, 2, 4, or 8. This refers to the number of bits
11495% used to store the index. The color samples always have
11496% bit-depth 8 in indexed PNG files.
11497%
11498% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11499% png:bit-depth can be 8 or 16.
11500%
glennrpfd164d22013-01-26 21:10:22 +000011501% If the image cannot be written without loss with the
11502% requested bit-depth and color-type, a PNG file will not
11503% be written, a warning will be issued, and the encoder will
11504% return MagickFalse.
glennrp5a39f372011-02-25 04:52:16 +000011505%
cristy3ed852e2009-09-05 21:47:34 +000011506% Since image encoders should not be responsible for the "heavy lifting",
11507% the user should make sure that ImageMagick has already reduced the
11508% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000011509% transparency prior to attempting to write the image with depth, color,
cristy16ea1392012-03-21 20:38:41 +000011510% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000011511%
cristy3ed852e2009-09-05 21:47:34 +000011512% Note that another definition, "png:bit-depth-written" exists, but it
11513% is not intended for external use. It is only used internally by the
11514% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11515%
11516% It is possible to request that the PNG encoder write previously-formatted
11517% ancillary chunks in the output PNG file, using the "-profile" commandline
11518% option as shown below or by setting the profile via a programming
11519% interface:
11520%
11521% -profile PNG-chunk-x:<file>
11522%
11523% where x is a location flag and <file> is a file containing the chunk
11524% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000011525% This encoder will compute the chunk length and CRC, so those must not
11526% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000011527%
11528% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11529% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11530% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000011531% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000011532%
glennrpbb8a7332010-11-13 15:17:35 +000011533% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000011534%
glennrp3241bd02010-12-12 04:36:28 +000011535% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000011536%
glennrpd6afd542010-11-19 01:53:05 +000011537% o 32-bit depth is reduced to 16.
11538% o 16-bit depth is reduced to 8 if all pixels contain samples whose
11539% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000011540% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000011541% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000011542% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000011543% o Grayscale images are reduced to 1, 2, or 4 bit depth if
11544% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000011545% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000011546% o If matte channel is present but only one transparent color is
11547% present, RGB+tRNS is written instead of RGBA
11548% o Opaque matte channel is removed (or added, if color-type 4 or 6
11549% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000011550%
cristy3ed852e2009-09-05 21:47:34 +000011551%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11552*/
11553static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy16ea1392012-03-21 20:38:41 +000011554 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011555{
11556 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011557 excluding,
11558 logging,
11559 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011560 status;
11561
11562 MngInfo
11563 *mng_info;
11564
11565 const char
11566 *value;
11567
11568 int
glennrp21f0e622011-01-07 16:20:57 +000011569 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000011570 source;
11571
cristy3ed852e2009-09-05 21:47:34 +000011572 /*
11573 Open image file.
11574 */
11575 assert(image_info != (const ImageInfo *) NULL);
11576 assert(image_info->signature == MagickSignature);
11577 assert(image != (Image *) NULL);
11578 assert(image->signature == MagickSignature);
11579 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011580 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011581 /*
11582 Allocate a MngInfo structure.
11583 */
11584 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011585 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000011586
cristy3ed852e2009-09-05 21:47:34 +000011587 if (mng_info == (MngInfo *) NULL)
11588 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011589
cristy3ed852e2009-09-05 21:47:34 +000011590 /*
11591 Initialize members of the MngInfo structure.
11592 */
11593 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11594 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000011595 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011596 have_mng_structure=MagickTrue;
11597
11598 /* See if user has requested a specific PNG subformat */
11599
11600 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11601 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11602 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
glennrpfd164d22013-01-26 21:10:22 +000011603 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11604 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
cristy3ed852e2009-09-05 21:47:34 +000011605
cristy092ec8d2013-04-26 13:46:22 +000011606 value=GetImageOption(image_info,"png:format");
glennrpb381a262012-02-11 17:49:49 +000011607
11608 if (value != (char *) NULL)
11609 {
glennrpf70c4d22013-03-19 15:26:48 +000011610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11611 " Format=%s",value);
11612
glennrpfd164d22013-01-26 21:10:22 +000011613 mng_info->write_png8 = MagickFalse;
11614 mng_info->write_png24 = MagickFalse;
11615 mng_info->write_png32 = MagickFalse;
11616 mng_info->write_png48 = MagickFalse;
11617 mng_info->write_png64 = MagickFalse;
11618
glennrpb381a262012-02-11 17:49:49 +000011619 if (LocaleCompare(value,"png8") == 0)
glennrpb381a262012-02-11 17:49:49 +000011620 mng_info->write_png8 = MagickTrue;
glennrpb381a262012-02-11 17:49:49 +000011621
11622 else if (LocaleCompare(value,"png24") == 0)
glennrpb381a262012-02-11 17:49:49 +000011623 mng_info->write_png24 = MagickTrue;
glennrpb381a262012-02-11 17:49:49 +000011624
11625 else if (LocaleCompare(value,"png32") == 0)
glennrpb381a262012-02-11 17:49:49 +000011626 mng_info->write_png32 = MagickTrue;
glennrpfd164d22013-01-26 21:10:22 +000011627
11628 else if (LocaleCompare(value,"png48") == 0)
11629 mng_info->write_png48 = MagickTrue;
11630
11631 else if (LocaleCompare(value,"png64") == 0)
11632 mng_info->write_png64 = MagickTrue;
glennrp5830fbc2013-01-27 06:11:45 +000011633
glennrpf70c4d22013-03-19 15:26:48 +000011634 else if (LocaleCompare(value,"png00") == 0)
glennrp5830fbc2013-01-27 06:11:45 +000011635 {
glennrp3398b5b2013-05-03 23:10:31 +000011636 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11637 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
glennrpf70c4d22013-03-19 15:26:48 +000011638
11639 if (value != (char *) NULL)
11640 {
11641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11642 " png00 inherited bit depth=%s",value);
11643
11644 if (LocaleCompare(value,"1") == 0)
11645 mng_info->write_png_depth = 1;
11646
11647 else if (LocaleCompare(value,"1") == 0)
11648 mng_info->write_png_depth = 2;
11649
11650 else if (LocaleCompare(value,"2") == 0)
11651 mng_info->write_png_depth = 4;
11652
11653 else if (LocaleCompare(value,"8") == 0)
11654 mng_info->write_png_depth = 8;
11655
11656 else if (LocaleCompare(value,"16") == 0)
11657 mng_info->write_png_depth = 16;
11658 }
11659
glennrp3398b5b2013-05-03 23:10:31 +000011660 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
glennrpf70c4d22013-03-19 15:26:48 +000011661
11662 if (value != (char *) NULL)
11663 {
11664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11665 " png00 inherited color type=%s",value);
11666
11667 if (LocaleCompare(value,"0") == 0)
11668 mng_info->write_png_colortype = 1;
11669
11670 else if (LocaleCompare(value,"2") == 0)
11671 mng_info->write_png_colortype = 3;
11672
11673 else if (LocaleCompare(value,"3") == 0)
11674 mng_info->write_png_colortype = 4;
11675
11676 else if (LocaleCompare(value,"4") == 0)
11677 mng_info->write_png_colortype = 5;
11678
11679 else if (LocaleCompare(value,"6") == 0)
11680 mng_info->write_png_colortype = 7;
11681 }
glennrp5830fbc2013-01-27 06:11:45 +000011682 }
11683 }
11684
cristy3ed852e2009-09-05 21:47:34 +000011685 if (mng_info->write_png8)
11686 {
glennrp9c1eb072010-06-06 22:19:15 +000011687 mng_info->write_png_colortype = /* 3 */ 4;
11688 mng_info->write_png_depth = 8;
11689 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011690 }
11691
11692 if (mng_info->write_png24)
11693 {
glennrp9c1eb072010-06-06 22:19:15 +000011694 mng_info->write_png_colortype = /* 2 */ 3;
11695 mng_info->write_png_depth = 8;
11696 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011697
cristy8a46d822012-08-28 23:32:39 +000011698 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +000011699 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011700
glennrp9c1eb072010-06-06 22:19:15 +000011701 else
cristy16ea1392012-03-21 20:38:41 +000011702 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011703
cristy16ea1392012-03-21 20:38:41 +000011704 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011705 }
11706
11707 if (mng_info->write_png32)
11708 {
glennrp9c1eb072010-06-06 22:19:15 +000011709 mng_info->write_png_colortype = /* 6 */ 7;
11710 mng_info->write_png_depth = 8;
11711 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011712
cristy8a46d822012-08-28 23:32:39 +000011713 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +000011714 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011715
glennrp9c1eb072010-06-06 22:19:15 +000011716 else
cristy16ea1392012-03-21 20:38:41 +000011717 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011718
cristy16ea1392012-03-21 20:38:41 +000011719 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011720 }
11721
glennrpfd164d22013-01-26 21:10:22 +000011722 if (mng_info->write_png48)
11723 {
11724 mng_info->write_png_colortype = /* 2 */ 3;
11725 mng_info->write_png_depth = 16;
11726 image->depth = 16;
11727
glennrp4dda64f2013-01-26 21:20:24 +000011728 if (image->alpha_trait == BlendPixelTrait)
11729 (void) SetImageType(image,TrueColorMatteType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011730
11731 else
glennrp4dda64f2013-01-26 21:20:24 +000011732 (void) SetImageType(image,TrueColorType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011733
glennrp4dda64f2013-01-26 21:20:24 +000011734 (void) SyncImage(image,exception);
glennrpfd164d22013-01-26 21:10:22 +000011735 }
11736
11737 if (mng_info->write_png64)
11738 {
11739 mng_info->write_png_colortype = /* 6 */ 7;
11740 mng_info->write_png_depth = 16;
11741 image->depth = 16;
11742
glennrp4dda64f2013-01-26 21:20:24 +000011743 if (image->alpha_trait == BlendPixelTrait)
11744 (void) SetImageType(image,TrueColorMatteType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011745
11746 else
glennrp4dda64f2013-01-26 21:20:24 +000011747 (void) SetImageType(image,TrueColorType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011748
glennrp4dda64f2013-01-26 21:20:24 +000011749 (void) SyncImage(image,exception);
glennrpfd164d22013-01-26 21:10:22 +000011750 }
11751
cristy092ec8d2013-04-26 13:46:22 +000011752 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011753
cristy3ed852e2009-09-05 21:47:34 +000011754 if (value != (char *) NULL)
11755 {
11756 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011757 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011758
cristy3ed852e2009-09-05 21:47:34 +000011759 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011760 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011761
cristy3ed852e2009-09-05 21:47:34 +000011762 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011763 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011764
cristy3ed852e2009-09-05 21:47:34 +000011765 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011766 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011767
cristy3ed852e2009-09-05 21:47:34 +000011768 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011769 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011770
glennrpbb8a7332010-11-13 15:17:35 +000011771 else
cristy16ea1392012-03-21 20:38:41 +000011772 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011773 GetMagickModule(),CoderWarning,
11774 "ignoring invalid defined png:bit-depth",
11775 "=%s",value);
11776
cristy3ed852e2009-09-05 21:47:34 +000011777 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011779 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011780 }
glennrp0fe50b42010-11-16 03:52:51 +000011781
cristy092ec8d2013-04-26 13:46:22 +000011782 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011783
cristy3ed852e2009-09-05 21:47:34 +000011784 if (value != (char *) NULL)
11785 {
11786 /* We must store colortype+1 because 0 is a valid colortype */
11787 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011788 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011789
cristy16ea1392012-03-21 20:38:41 +000011790 else if (LocaleCompare(value,"1") == 0)
11791 mng_info->write_png_colortype = 2;
11792
cristy3ed852e2009-09-05 21:47:34 +000011793 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011794 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011795
cristy3ed852e2009-09-05 21:47:34 +000011796 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011797 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011798
cristy3ed852e2009-09-05 21:47:34 +000011799 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011800 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011801
cristy3ed852e2009-09-05 21:47:34 +000011802 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011803 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011804
glennrpbb8a7332010-11-13 15:17:35 +000011805 else
cristy16ea1392012-03-21 20:38:41 +000011806 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011807 GetMagickModule(),CoderWarning,
11808 "ignoring invalid defined png:color-type",
11809 "=%s",value);
11810
cristy3ed852e2009-09-05 21:47:34 +000011811 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011813 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011814 }
11815
glennrp0e8ea192010-12-24 18:00:33 +000011816 /* Check for chunks to be excluded:
11817 *
glennrp0dff56c2011-01-29 19:10:02 +000011818 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011819 * listed in the "unused_chunks" array, above.
11820 *
cristy5d6fc9c2011-12-27 03:10:42 +000011821 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011822 * define (in the image properties or in the image artifacts)
11823 * or via a mng_info member. For convenience, in addition
11824 * to or instead of a comma-separated list of chunks, the
11825 * "exclude-chunk" string can be simply "all" or "none".
11826 *
11827 * The exclude-chunk define takes priority over the mng_info.
11828 *
cristy5d6fc9c2011-12-27 03:10:42 +000011829 * A "png:include-chunk" define takes priority over both the
11830 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011831 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011832 * well as a comma-separated list. Chunks that are unknown to
11833 * ImageMagick are always excluded, regardless of their "copy-safe"
11834 * status according to the PNG specification, and even if they
glennrpaa192b12012-01-17 21:35:21 +000011835 * appear in the "include-chunk" list. Such defines appearing among
11836 * the image options take priority over those found among the image
11837 * artifacts.
glennrp0e8ea192010-12-24 18:00:33 +000011838 *
11839 * Finally, all chunks listed in the "unused_chunks" array are
11840 * automatically excluded, regardless of the other instructions
11841 * or lack thereof.
11842 *
11843 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11844 * will not be written and the gAMA chunk will only be written if it
11845 * is not between .45 and .46, or approximately (1.0/2.2).
11846 *
11847 * If you exclude tRNS and the image has transparency, the colortype
11848 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11849 *
11850 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011851 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011852 */
11853
glennrp26f37912010-12-23 16:22:42 +000011854 mng_info->ping_exclude_bKGD=MagickFalse;
11855 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011856 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011857 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11858 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011859 mng_info->ping_exclude_iCCP=MagickFalse;
11860 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11861 mng_info->ping_exclude_oFFs=MagickFalse;
11862 mng_info->ping_exclude_pHYs=MagickFalse;
11863 mng_info->ping_exclude_sRGB=MagickFalse;
11864 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011865 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011866 mng_info->ping_exclude_vpAg=MagickFalse;
11867 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11868 mng_info->ping_exclude_zTXt=MagickFalse;
11869
glennrp8d3d6e52011-04-19 04:39:51 +000011870 mng_info->ping_preserve_colormap=MagickFalse;
11871
cristy092ec8d2013-04-26 13:46:22 +000011872 value=GetImageOption(image_info,"png:preserve-colormap");
glennrp8d3d6e52011-04-19 04:39:51 +000011873 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011874 value=GetImageArtifact(image,"png:preserve-colormap");
glennrp8d3d6e52011-04-19 04:39:51 +000011875 if (value != NULL)
11876 mng_info->ping_preserve_colormap=MagickTrue;
11877
glennrpecab7d72013-05-14 22:50:32 +000011878 mng_info->ping_preserve_iCCP=MagickFalse;
11879
11880 value=GetImageOption(image_info,"png:preserve-iCCP");
11881 if (value == NULL)
11882 value=GetImageArtifact(image,"png:preserve-iCCP");
11883 if (value != NULL)
11884 mng_info->ping_preserve_iCCP=MagickTrue;
11885
11886 /* These compression-level, compression-strategy, and compression-filter
glennrp18682582011-06-30 18:11:47 +000011887 * defines take precedence over values from the -quality option.
11888 */
cristy092ec8d2013-04-26 13:46:22 +000011889 value=GetImageOption(image_info,"png:compression-level");
glennrp18682582011-06-30 18:11:47 +000011890 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011891 value=GetImageArtifact(image,"png:compression-level");
glennrp18682582011-06-30 18:11:47 +000011892 if (value != NULL)
11893 {
glennrp18682582011-06-30 18:11:47 +000011894 /* We have to add 1 to everything because 0 is a valid input,
11895 * and we want to use 0 (the default) to mean undefined.
11896 */
11897 if (LocaleCompare(value,"0") == 0)
11898 mng_info->write_png_compression_level = 1;
11899
glennrp0ffb95c2012-01-30 21:16:22 +000011900 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011901 mng_info->write_png_compression_level = 2;
11902
11903 else if (LocaleCompare(value,"2") == 0)
11904 mng_info->write_png_compression_level = 3;
11905
11906 else if (LocaleCompare(value,"3") == 0)
11907 mng_info->write_png_compression_level = 4;
11908
11909 else if (LocaleCompare(value,"4") == 0)
11910 mng_info->write_png_compression_level = 5;
11911
11912 else if (LocaleCompare(value,"5") == 0)
11913 mng_info->write_png_compression_level = 6;
11914
11915 else if (LocaleCompare(value,"6") == 0)
11916 mng_info->write_png_compression_level = 7;
11917
11918 else if (LocaleCompare(value,"7") == 0)
11919 mng_info->write_png_compression_level = 8;
11920
11921 else if (LocaleCompare(value,"8") == 0)
11922 mng_info->write_png_compression_level = 9;
11923
11924 else if (LocaleCompare(value,"9") == 0)
11925 mng_info->write_png_compression_level = 10;
11926
11927 else
cristy16ea1392012-03-21 20:38:41 +000011928 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011929 GetMagickModule(),CoderWarning,
11930 "ignoring invalid defined png:compression-level",
11931 "=%s",value);
11932 }
11933
cristy092ec8d2013-04-26 13:46:22 +000011934 value=GetImageOption(image_info,"png:compression-strategy");
glennrp18682582011-06-30 18:11:47 +000011935 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011936 value=GetImageArtifact(image,"png:compression-strategy");
glennrp18682582011-06-30 18:11:47 +000011937 if (value != NULL)
11938 {
11939
11940 if (LocaleCompare(value,"0") == 0)
11941 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11942
11943 else if (LocaleCompare(value,"1") == 0)
11944 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11945
11946 else if (LocaleCompare(value,"2") == 0)
11947 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11948
11949 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011950#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011951 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011952#else
11953 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11954#endif
glennrp18682582011-06-30 18:11:47 +000011955
11956 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011957#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011958 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011959#else
11960 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11961#endif
glennrp18682582011-06-30 18:11:47 +000011962
11963 else
cristy16ea1392012-03-21 20:38:41 +000011964 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011965 GetMagickModule(),CoderWarning,
11966 "ignoring invalid defined png:compression-strategy",
11967 "=%s",value);
11968 }
11969
cristy092ec8d2013-04-26 13:46:22 +000011970 value=GetImageOption(image_info,"png:compression-filter");
glennrp18682582011-06-30 18:11:47 +000011971 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011972 value=GetImageArtifact(image,"png:compression-filter");
glennrp18682582011-06-30 18:11:47 +000011973 if (value != NULL)
11974 {
11975
11976 /* To do: combinations of filters allowed by libpng
11977 * masks 0x08 through 0xf8
11978 *
11979 * Implement this as a comma-separated list of 0,1,2,3,4,5
11980 * where 5 is a special case meaning PNG_ALL_FILTERS.
11981 */
11982
11983 if (LocaleCompare(value,"0") == 0)
11984 mng_info->write_png_compression_filter = 1;
11985
cristyb19b8122012-10-22 11:03:30 +000011986 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011987 mng_info->write_png_compression_filter = 2;
11988
11989 else if (LocaleCompare(value,"2") == 0)
11990 mng_info->write_png_compression_filter = 3;
11991
11992 else if (LocaleCompare(value,"3") == 0)
11993 mng_info->write_png_compression_filter = 4;
11994
11995 else if (LocaleCompare(value,"4") == 0)
11996 mng_info->write_png_compression_filter = 5;
11997
11998 else if (LocaleCompare(value,"5") == 0)
11999 mng_info->write_png_compression_filter = 6;
12000
glennrp18682582011-06-30 18:11:47 +000012001 else
cristy16ea1392012-03-21 20:38:41 +000012002 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000012003 GetMagickModule(),CoderWarning,
12004 "ignoring invalid defined png:compression-filter",
12005 "=%s",value);
12006 }
12007
glennrp03812ae2010-12-24 01:31:34 +000012008 excluding=MagickFalse;
12009
glennrp5c7cf4e2010-12-24 00:30:00 +000012010 for (source=0; source<1; source++)
12011 {
12012 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000012013 {
cristy092ec8d2013-04-26 13:46:22 +000012014 value=GetImageOption(image_info,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000012015
12016 if (value == NULL)
12017 value=GetImageArtifact(image,"png:exclude-chunks");
12018 }
glennrp5c7cf4e2010-12-24 00:30:00 +000012019 else
glennrpacba0042010-12-24 14:27:26 +000012020 {
cristy092ec8d2013-04-26 13:46:22 +000012021 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000012022
glennrpacba0042010-12-24 14:27:26 +000012023 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000012024 value=GetImageArtifact(image,"png:exclude-chunks");
glennrpacba0042010-12-24 14:27:26 +000012025 }
12026
glennrp03812ae2010-12-24 01:31:34 +000012027 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000012028 {
glennrp03812ae2010-12-24 01:31:34 +000012029
12030 size_t
12031 last;
12032
12033 excluding=MagickTrue;
12034
12035 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000012036 {
12037 if (source == 0)
12038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12039 " png:exclude-chunk=%s found in image artifacts.\n", value);
12040 else
12041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12042 " png:exclude-chunk=%s found in image properties.\n", value);
12043 }
glennrp03812ae2010-12-24 01:31:34 +000012044
12045 last=strlen(value);
12046
12047 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000012048 {
glennrp03812ae2010-12-24 01:31:34 +000012049
glennrp03812ae2010-12-24 01:31:34 +000012050 if (LocaleNCompare(value+i,"none",4) == 0)
12051 {
12052 mng_info->ping_exclude_bKGD=MagickFalse;
12053 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000012054 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000012055 mng_info->ping_exclude_EXIF=MagickFalse;
12056 mng_info->ping_exclude_gAMA=MagickFalse;
12057 mng_info->ping_exclude_iCCP=MagickFalse;
12058 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12059 mng_info->ping_exclude_oFFs=MagickFalse;
12060 mng_info->ping_exclude_pHYs=MagickFalse;
12061 mng_info->ping_exclude_sRGB=MagickFalse;
12062 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000012063 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000012064 mng_info->ping_exclude_vpAg=MagickFalse;
12065 mng_info->ping_exclude_zCCP=MagickFalse;
12066 mng_info->ping_exclude_zTXt=MagickFalse;
12067 }
glennrp2cc891a2010-12-24 13:44:32 +000012068
glennrp03812ae2010-12-24 01:31:34 +000012069 if (LocaleNCompare(value+i,"bkgd",4) == 0)
12070 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000012071
glennrp03812ae2010-12-24 01:31:34 +000012072 if (LocaleNCompare(value+i,"chrm",4) == 0)
12073 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000012074
glennrpa0ed0092011-04-18 16:36:29 +000012075 if (LocaleNCompare(value+i,"date",4) == 0)
12076 mng_info->ping_exclude_date=MagickTrue;
12077
glennrp03812ae2010-12-24 01:31:34 +000012078 if (LocaleNCompare(value+i,"exif",4) == 0)
12079 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000012080
glennrp03812ae2010-12-24 01:31:34 +000012081 if (LocaleNCompare(value+i,"gama",4) == 0)
12082 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000012083
glennrp03812ae2010-12-24 01:31:34 +000012084 if (LocaleNCompare(value+i,"iccp",4) == 0)
12085 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000012086
glennrp03812ae2010-12-24 01:31:34 +000012087 /*
12088 if (LocaleNCompare(value+i,"itxt",4) == 0)
12089 mng_info->ping_exclude_iTXt=MagickTrue;
12090 */
glennrp2cc891a2010-12-24 13:44:32 +000012091
glennrp03812ae2010-12-24 01:31:34 +000012092 if (LocaleNCompare(value+i,"gama",4) == 0)
12093 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000012094
glennrp03812ae2010-12-24 01:31:34 +000012095 if (LocaleNCompare(value+i,"offs",4) == 0)
12096 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000012097
glennrp03812ae2010-12-24 01:31:34 +000012098 if (LocaleNCompare(value+i,"phys",4) == 0)
12099 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000012100
glennrpa1e3b7b2010-12-24 16:37:33 +000012101 if (LocaleNCompare(value+i,"srgb",4) == 0)
12102 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000012103
glennrp03812ae2010-12-24 01:31:34 +000012104 if (LocaleNCompare(value+i,"text",4) == 0)
12105 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000012106
glennrpa1e3b7b2010-12-24 16:37:33 +000012107 if (LocaleNCompare(value+i,"trns",4) == 0)
12108 mng_info->ping_exclude_tRNS=MagickTrue;
12109
glennrp03812ae2010-12-24 01:31:34 +000012110 if (LocaleNCompare(value+i,"vpag",4) == 0)
12111 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000012112
glennrp03812ae2010-12-24 01:31:34 +000012113 if (LocaleNCompare(value+i,"zccp",4) == 0)
12114 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000012115
glennrp03812ae2010-12-24 01:31:34 +000012116 if (LocaleNCompare(value+i,"ztxt",4) == 0)
12117 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000012118
glennrp97d1b572013-05-24 01:41:32 +000012119 if (LocaleNCompare(value+i,"all",3) == 0)
12120 {
12121 mng_info->ping_exclude_bKGD=MagickTrue;
12122 mng_info->ping_exclude_cHRM=MagickTrue;
12123 mng_info->ping_exclude_date=MagickTrue;
12124 mng_info->ping_exclude_EXIF=MagickTrue;
12125 mng_info->ping_exclude_gAMA=MagickTrue;
12126 mng_info->ping_exclude_iCCP=MagickTrue;
12127 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12128 mng_info->ping_exclude_oFFs=MagickTrue;
12129 mng_info->ping_exclude_pHYs=MagickTrue;
12130 mng_info->ping_exclude_sRGB=MagickTrue;
12131 mng_info->ping_exclude_tEXt=MagickTrue;
12132 mng_info->ping_exclude_tRNS=MagickTrue;
12133 mng_info->ping_exclude_vpAg=MagickTrue;
12134 mng_info->ping_exclude_zCCP=MagickTrue;
12135 mng_info->ping_exclude_zTXt=MagickTrue;
12136 i--;
12137 }
glennrp03812ae2010-12-24 01:31:34 +000012138 }
glennrpce91ed52010-12-23 22:37:49 +000012139 }
glennrp26f37912010-12-23 16:22:42 +000012140 }
12141
glennrp5c7cf4e2010-12-24 00:30:00 +000012142 for (source=0; source<1; source++)
12143 {
12144 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000012145 {
cristy092ec8d2013-04-26 13:46:22 +000012146 value=GetImageOption(image_info,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000012147
12148 if (value == NULL)
12149 value=GetImageArtifact(image,"png:include-chunks");
12150 }
glennrp5c7cf4e2010-12-24 00:30:00 +000012151 else
glennrpacba0042010-12-24 14:27:26 +000012152 {
cristy092ec8d2013-04-26 13:46:22 +000012153 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000012154
glennrpacba0042010-12-24 14:27:26 +000012155 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000012156 value=GetImageArtifact(image,"png:include-chunks");
glennrpacba0042010-12-24 14:27:26 +000012157 }
12158
glennrp03812ae2010-12-24 01:31:34 +000012159 if (value != NULL)
12160 {
12161 size_t
12162 last;
glennrp26f37912010-12-23 16:22:42 +000012163
glennrp03812ae2010-12-24 01:31:34 +000012164 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000012165
glennrp03812ae2010-12-24 01:31:34 +000012166 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000012167 {
12168 if (source == 0)
12169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12170 " png:include-chunk=%s found in image artifacts.\n", value);
12171 else
12172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12173 " png:include-chunk=%s found in image properties.\n", value);
12174 }
glennrp03812ae2010-12-24 01:31:34 +000012175
12176 last=strlen(value);
12177
12178 for (i=0; i<(int) last; i+=5)
12179 {
glennrp03812ae2010-12-24 01:31:34 +000012180 if (LocaleNCompare(value+i,"none",4) == 0)
12181 {
12182 mng_info->ping_exclude_bKGD=MagickTrue;
12183 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012184 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000012185 mng_info->ping_exclude_EXIF=MagickTrue;
12186 mng_info->ping_exclude_gAMA=MagickTrue;
12187 mng_info->ping_exclude_iCCP=MagickTrue;
12188 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12189 mng_info->ping_exclude_oFFs=MagickTrue;
12190 mng_info->ping_exclude_pHYs=MagickTrue;
12191 mng_info->ping_exclude_sRGB=MagickTrue;
12192 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012193 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000012194 mng_info->ping_exclude_vpAg=MagickTrue;
12195 mng_info->ping_exclude_zCCP=MagickTrue;
12196 mng_info->ping_exclude_zTXt=MagickTrue;
12197 }
glennrp2cc891a2010-12-24 13:44:32 +000012198
glennrp03812ae2010-12-24 01:31:34 +000012199 if (LocaleNCompare(value+i,"bkgd",4) == 0)
12200 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000012201
glennrp03812ae2010-12-24 01:31:34 +000012202 if (LocaleNCompare(value+i,"chrm",4) == 0)
12203 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000012204
glennrpa0ed0092011-04-18 16:36:29 +000012205 if (LocaleNCompare(value+i,"date",4) == 0)
12206 mng_info->ping_exclude_date=MagickFalse;
12207
glennrp03812ae2010-12-24 01:31:34 +000012208 if (LocaleNCompare(value+i,"exif",4) == 0)
12209 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000012210
glennrp03812ae2010-12-24 01:31:34 +000012211 if (LocaleNCompare(value+i,"gama",4) == 0)
12212 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000012213
glennrp03812ae2010-12-24 01:31:34 +000012214 if (LocaleNCompare(value+i,"iccp",4) == 0)
12215 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000012216
glennrp03812ae2010-12-24 01:31:34 +000012217 /*
12218 if (LocaleNCompare(value+i,"itxt",4) == 0)
12219 mng_info->ping_exclude_iTXt=MagickFalse;
12220 */
glennrp2cc891a2010-12-24 13:44:32 +000012221
glennrp03812ae2010-12-24 01:31:34 +000012222 if (LocaleNCompare(value+i,"gama",4) == 0)
12223 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000012224
glennrp03812ae2010-12-24 01:31:34 +000012225 if (LocaleNCompare(value+i,"offs",4) == 0)
12226 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000012227
glennrp03812ae2010-12-24 01:31:34 +000012228 if (LocaleNCompare(value+i,"phys",4) == 0)
12229 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000012230
glennrpa1e3b7b2010-12-24 16:37:33 +000012231 if (LocaleNCompare(value+i,"srgb",4) == 0)
12232 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000012233
glennrp03812ae2010-12-24 01:31:34 +000012234 if (LocaleNCompare(value+i,"text",4) == 0)
12235 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000012236
glennrpa1e3b7b2010-12-24 16:37:33 +000012237 if (LocaleNCompare(value+i,"trns",4) == 0)
12238 mng_info->ping_exclude_tRNS=MagickFalse;
12239
glennrp03812ae2010-12-24 01:31:34 +000012240 if (LocaleNCompare(value+i,"vpag",4) == 0)
12241 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000012242
glennrp03812ae2010-12-24 01:31:34 +000012243 if (LocaleNCompare(value+i,"zccp",4) == 0)
12244 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000012245
glennrp03812ae2010-12-24 01:31:34 +000012246 if (LocaleNCompare(value+i,"ztxt",4) == 0)
12247 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000012248
glennrp97d1b572013-05-24 01:41:32 +000012249 if (LocaleNCompare(value+i,"all",3) == 0)
12250 {
12251 mng_info->ping_exclude_bKGD=MagickFalse;
12252 mng_info->ping_exclude_cHRM=MagickFalse;
12253 mng_info->ping_exclude_date=MagickFalse;
12254 mng_info->ping_exclude_EXIF=MagickFalse;
12255 mng_info->ping_exclude_gAMA=MagickFalse;
12256 mng_info->ping_exclude_iCCP=MagickFalse;
12257 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12258 mng_info->ping_exclude_oFFs=MagickFalse;
12259 mng_info->ping_exclude_pHYs=MagickFalse;
12260 mng_info->ping_exclude_sRGB=MagickFalse;
12261 mng_info->ping_exclude_tEXt=MagickFalse;
12262 mng_info->ping_exclude_tRNS=MagickFalse;
12263 mng_info->ping_exclude_vpAg=MagickFalse;
12264 mng_info->ping_exclude_zCCP=MagickFalse;
12265 mng_info->ping_exclude_zTXt=MagickFalse;
12266 i--;
12267 }
glennrp03812ae2010-12-24 01:31:34 +000012268 }
glennrpce91ed52010-12-23 22:37:49 +000012269 }
glennrp26f37912010-12-23 16:22:42 +000012270 }
12271
glennrp03812ae2010-12-24 01:31:34 +000012272 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000012273 {
12274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000012275 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000012276 if (mng_info->ping_exclude_bKGD != MagickFalse)
12277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12278 " bKGD");
12279 if (mng_info->ping_exclude_cHRM != MagickFalse)
12280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12281 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000012282 if (mng_info->ping_exclude_date != MagickFalse)
12283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12284 " date");
glennrp26f37912010-12-23 16:22:42 +000012285 if (mng_info->ping_exclude_EXIF != MagickFalse)
12286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12287 " EXIF");
12288 if (mng_info->ping_exclude_gAMA != MagickFalse)
12289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12290 " gAMA");
12291 if (mng_info->ping_exclude_iCCP != MagickFalse)
12292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12293 " iCCP");
12294/*
12295 if (mng_info->ping_exclude_iTXt != MagickFalse)
12296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12297 " iTXt");
12298*/
12299 if (mng_info->ping_exclude_oFFs != MagickFalse)
12300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12301 " oFFs");
12302 if (mng_info->ping_exclude_pHYs != MagickFalse)
12303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12304 " pHYs");
12305 if (mng_info->ping_exclude_sRGB != MagickFalse)
12306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12307 " sRGB");
12308 if (mng_info->ping_exclude_tEXt != MagickFalse)
12309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12310 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000012311 if (mng_info->ping_exclude_tRNS != MagickFalse)
12312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12313 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000012314 if (mng_info->ping_exclude_vpAg != MagickFalse)
12315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12316 " vpAg");
12317 if (mng_info->ping_exclude_zCCP != MagickFalse)
12318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12319 " zCCP");
12320 if (mng_info->ping_exclude_zTXt != MagickFalse)
12321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12322 " zTXt");
12323 }
12324
glennrpb9cfe272010-12-21 15:08:06 +000012325 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000012326
cristy16ea1392012-03-21 20:38:41 +000012327 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012328
12329 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012330
cristy3ed852e2009-09-05 21:47:34 +000012331 if (logging != MagickFalse)
12332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012333
cristy3ed852e2009-09-05 21:47:34 +000012334 return(status);
12335}
12336
12337#if defined(JNG_SUPPORTED)
12338
12339/* Write one JNG image */
12340static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +000012341 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012342{
12343 Image
12344 *jpeg_image;
12345
12346 ImageInfo
12347 *jpeg_image_info;
12348
12349 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000012350 logging,
cristy3ed852e2009-09-05 21:47:34 +000012351 status;
12352
12353 size_t
12354 length;
12355
12356 unsigned char
12357 *blob,
12358 chunk[80],
12359 *p;
12360
12361 unsigned int
12362 jng_alpha_compression_method,
12363 jng_alpha_sample_depth,
12364 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000012365 transparent;
12366
cristybb503372010-05-27 20:51:26 +000012367 size_t
glennrp59575fa2011-12-31 21:31:39 +000012368 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000012369 jng_quality;
12370
12371 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000012372 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012373
12374 blob=(unsigned char *) NULL;
12375 jpeg_image=(Image *) NULL;
12376 jpeg_image_info=(ImageInfo *) NULL;
12377
12378 status=MagickTrue;
12379 transparent=image_info->type==GrayscaleMatteType ||
cristy8a46d822012-08-28 23:32:39 +000012380 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +000012381
glennrp4b917592013-06-20 19:53:59 +000012382 jng_alpha_sample_depth = 0;
12383
glennrp59575fa2011-12-31 21:31:39 +000012384 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12385
12386 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12387
glennrp750105b2012-04-25 16:20:45 +000012388 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
glennrp59575fa2011-12-31 21:31:39 +000012389 image_info->quality;
12390
12391 if (jng_alpha_quality >= 1000)
12392 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000012393
12394 if (transparent)
12395 {
12396 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000012397
cristy3ed852e2009-09-05 21:47:34 +000012398 /* Create JPEG blob, image, and image_info */
12399 if (logging != MagickFalse)
12400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +000012401 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000012402
cristy3ed852e2009-09-05 21:47:34 +000012403 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000012404
cristy3ed852e2009-09-05 21:47:34 +000012405 if (jpeg_image_info == (ImageInfo *) NULL)
12406 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000012407
cristy3ed852e2009-09-05 21:47:34 +000012408 if (logging != MagickFalse)
12409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12410 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000012411
cristy16ea1392012-03-21 20:38:41 +000012412 jpeg_image=SeparateImage(image,AlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +000012413 if (jpeg_image == (Image *) NULL)
12414 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12415 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy8a46d822012-08-28 23:32:39 +000012416 jpeg_image->alpha_trait=UndefinedPixelTrait;
glennrp8f77fdc2012-03-21 15:16:37 +000012417 jpeg_image->quality=jng_alpha_quality;
cristy16ea1392012-03-21 20:38:41 +000012418 jpeg_image_info->type=GrayscaleType;
12419 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000012420 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012421 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000012422 "%s",jpeg_image->filename);
12423 }
glennrp59575fa2011-12-31 21:31:39 +000012424 else
12425 {
12426 jng_alpha_compression_method=0;
12427 jng_color_type=10;
12428 jng_alpha_sample_depth=0;
12429 }
cristy3ed852e2009-09-05 21:47:34 +000012430
12431 /* To do: check bit depth of PNG alpha channel */
12432
12433 /* Check if image is grayscale. */
12434 if (image_info->type != TrueColorMatteType && image_info->type !=
cristy7fb26522012-06-21 13:02:48 +000012435 TrueColorType && IsImageGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000012436 jng_color_type-=2;
12437
glennrp59575fa2011-12-31 21:31:39 +000012438 if (logging != MagickFalse)
12439 {
12440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12441 " JNG Quality = %d",(int) jng_quality);
12442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12443 " JNG Color Type = %d",jng_color_type);
12444 if (transparent)
12445 {
12446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12447 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12449 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12451 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12452 }
12453 }
12454
cristy3ed852e2009-09-05 21:47:34 +000012455 if (transparent)
12456 {
12457 if (jng_alpha_compression_method==0)
12458 {
12459 const char
12460 *value;
12461
cristy16ea1392012-03-21 20:38:41 +000012462 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000012463 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012464 exception);
cristy3ed852e2009-09-05 21:47:34 +000012465 if (logging != MagickFalse)
12466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12467 " Creating PNG blob.");
12468 length=0;
12469
12470 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12471 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12472 jpeg_image_info->interlace=NoInterlace;
12473
glennrpcc5d45b2012-01-06 04:06:10 +000012474 /* Exclude all ancillary chunks */
12475 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12476
cristy3ed852e2009-09-05 21:47:34 +000012477 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000012478 exception);
cristy3ed852e2009-09-05 21:47:34 +000012479
12480 /* Retrieve sample depth used */
cristy16ea1392012-03-21 20:38:41 +000012481 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000012482 if (value != (char *) NULL)
12483 jng_alpha_sample_depth= (unsigned int) value[0];
12484 }
12485 else
12486 {
cristy16ea1392012-03-21 20:38:41 +000012487 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000012488
12489 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012490 exception);
cristy3ed852e2009-09-05 21:47:34 +000012491
12492 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12493 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12494 jpeg_image_info->interlace=NoInterlace;
12495 if (logging != MagickFalse)
12496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12497 " Creating blob.");
12498 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000012499 exception);
cristy3ed852e2009-09-05 21:47:34 +000012500 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000012501
cristy3ed852e2009-09-05 21:47:34 +000012502 if (logging != MagickFalse)
12503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012504 " Successfully read jpeg_image into a blob, length=%.20g.",
12505 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012506
12507 }
12508 /* Destroy JPEG image and image_info */
12509 jpeg_image=DestroyImage(jpeg_image);
12510 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12511 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12512 }
12513
12514 /* Write JHDR chunk */
12515 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12516 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000012517 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000012518 PNGLong(chunk+4,(png_uint_32) image->columns);
12519 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012520 chunk[12]=jng_color_type;
12521 chunk[13]=8; /* sample depth */
12522 chunk[14]=8; /*jng_image_compression_method */
12523 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12524 chunk[16]=jng_alpha_sample_depth;
12525 chunk[17]=jng_alpha_compression_method;
12526 chunk[18]=0; /*jng_alpha_filter_method */
12527 chunk[19]=0; /*jng_alpha_interlace_method */
12528 (void) WriteBlob(image,20,chunk);
12529 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12530 if (logging != MagickFalse)
12531 {
12532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000012533 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000012534
cristy3ed852e2009-09-05 21:47:34 +000012535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000012536 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000012537
cristy3ed852e2009-09-05 21:47:34 +000012538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12539 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000012540
cristy3ed852e2009-09-05 21:47:34 +000012541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12542 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000012543
cristy3ed852e2009-09-05 21:47:34 +000012544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12545 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000012546
cristy3ed852e2009-09-05 21:47:34 +000012547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12548 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000012549
cristy3ed852e2009-09-05 21:47:34 +000012550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12551 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000012552
cristy3ed852e2009-09-05 21:47:34 +000012553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12554 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000012555
cristy3ed852e2009-09-05 21:47:34 +000012556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12557 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000012558
cristy3ed852e2009-09-05 21:47:34 +000012559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12560 " JNG alpha interlace:%5d",0);
12561 }
12562
glennrp0fe50b42010-11-16 03:52:51 +000012563 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000012564 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000012565
12566 /*
12567 Write leading ancillary chunks
12568 */
12569
12570 if (transparent)
12571 {
12572 /*
12573 Write JNG bKGD chunk
12574 */
12575
12576 unsigned char
12577 blue,
12578 green,
12579 red;
12580
cristybb503372010-05-27 20:51:26 +000012581 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012582 num_bytes;
12583
12584 if (jng_color_type == 8 || jng_color_type == 12)
12585 num_bytes=6L;
12586 else
12587 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000012588 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012589 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012590 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012591 red=ScaleQuantumToChar(image->background_color.red);
12592 green=ScaleQuantumToChar(image->background_color.green);
12593 blue=ScaleQuantumToChar(image->background_color.blue);
12594 *(chunk+4)=0;
12595 *(chunk+5)=red;
12596 *(chunk+6)=0;
12597 *(chunk+7)=green;
12598 *(chunk+8)=0;
12599 *(chunk+9)=blue;
12600 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12601 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12602 }
12603
12604 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12605 {
12606 /*
12607 Write JNG sRGB chunk
12608 */
12609 (void) WriteBlobMSBULong(image,1L);
12610 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012611 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012612
cristy3ed852e2009-09-05 21:47:34 +000012613 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012614 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012615 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012616 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012617
cristy3ed852e2009-09-05 21:47:34 +000012618 else
glennrpe610a072010-08-05 17:08:46 +000012619 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012620 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012621 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012622
cristy3ed852e2009-09-05 21:47:34 +000012623 (void) WriteBlob(image,5,chunk);
12624 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12625 }
12626 else
12627 {
12628 if (image->gamma != 0.0)
12629 {
12630 /*
12631 Write JNG gAMA chunk
12632 */
12633 (void) WriteBlobMSBULong(image,4L);
12634 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012635 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012636 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012637 (void) WriteBlob(image,8,chunk);
12638 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12639 }
glennrp0fe50b42010-11-16 03:52:51 +000012640
cristy3ed852e2009-09-05 21:47:34 +000012641 if ((mng_info->equal_chrms == MagickFalse) &&
12642 (image->chromaticity.red_primary.x != 0.0))
12643 {
12644 PrimaryInfo
12645 primary;
12646
12647 /*
12648 Write JNG cHRM chunk
12649 */
12650 (void) WriteBlobMSBULong(image,32L);
12651 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012652 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012653 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012654 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12655 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012656 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012657 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12658 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012659 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012660 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12661 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012662 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012663 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12664 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012665 (void) WriteBlob(image,36,chunk);
12666 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12667 }
12668 }
glennrp0fe50b42010-11-16 03:52:51 +000012669
cristy16ea1392012-03-21 20:38:41 +000012670 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012671 {
12672 /*
12673 Write JNG pHYs chunk
12674 */
12675 (void) WriteBlobMSBULong(image,9L);
12676 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012677 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000012678 if (image->units == PixelsPerInchResolution)
12679 {
cristy35ef8242010-06-03 16:24:13 +000012680 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012681 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012682
cristy35ef8242010-06-03 16:24:13 +000012683 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012684 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012685
cristy3ed852e2009-09-05 21:47:34 +000012686 chunk[12]=1;
12687 }
glennrp0fe50b42010-11-16 03:52:51 +000012688
cristy3ed852e2009-09-05 21:47:34 +000012689 else
12690 {
12691 if (image->units == PixelsPerCentimeterResolution)
12692 {
cristy35ef8242010-06-03 16:24:13 +000012693 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012694 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012695
cristy35ef8242010-06-03 16:24:13 +000012696 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012697 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012698
cristy3ed852e2009-09-05 21:47:34 +000012699 chunk[12]=1;
12700 }
glennrp0fe50b42010-11-16 03:52:51 +000012701
cristy3ed852e2009-09-05 21:47:34 +000012702 else
12703 {
cristy16ea1392012-03-21 20:38:41 +000012704 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12705 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012706 chunk[12]=0;
12707 }
12708 }
12709 (void) WriteBlob(image,13,chunk);
12710 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12711 }
12712
12713 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12714 {
12715 /*
12716 Write JNG oFFs chunk
12717 */
12718 (void) WriteBlobMSBULong(image,9L);
12719 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000012720 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000012721 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12722 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000012723 chunk[12]=0;
12724 (void) WriteBlob(image,13,chunk);
12725 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12726 }
12727 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12728 {
12729 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12730 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000012731 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012732 PNGLong(chunk+4,(png_uint_32) image->page.width);
12733 PNGLong(chunk+8,(png_uint_32) image->page.height);
12734 chunk[12]=0; /* unit = pixels */
12735 (void) WriteBlob(image,13,chunk);
12736 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12737 }
12738
12739
12740 if (transparent)
12741 {
12742 if (jng_alpha_compression_method==0)
12743 {
cristybb503372010-05-27 20:51:26 +000012744 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012745 i;
12746
cristybb503372010-05-27 20:51:26 +000012747 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012748 len;
12749
12750 /* Write IDAT chunk header */
12751 if (logging != MagickFalse)
12752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012753 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012754 length);
cristy3ed852e2009-09-05 21:47:34 +000012755
12756 /* Copy IDAT chunks */
12757 len=0;
12758 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012759 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012760 {
12761 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12762 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012763
cristy3ed852e2009-09-05 21:47:34 +000012764 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12765 {
12766 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012767 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012768 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012769 (void) WriteBlob(image,(size_t) len+4,p);
12770 (void) WriteBlobMSBULong(image,
12771 crc32(0,p,(uInt) len+4));
12772 }
glennrp0fe50b42010-11-16 03:52:51 +000012773
cristy3ed852e2009-09-05 21:47:34 +000012774 else
12775 {
12776 if (logging != MagickFalse)
12777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012778 " Skipping %c%c%c%c chunk, length=%.20g.",
12779 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012780 }
12781 p+=(8+len);
12782 }
12783 }
12784 else
12785 {
12786 /* Write JDAA chunk header */
12787 if (logging != MagickFalse)
12788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012789 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012790 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012791 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012792 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012793 /* Write JDAT chunk(s) data */
12794 (void) WriteBlob(image,4,chunk);
12795 (void) WriteBlob(image,length,blob);
12796 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12797 (uInt) length));
12798 }
12799 blob=(unsigned char *) RelinquishMagickMemory(blob);
12800 }
12801
12802 /* Encode image as a JPEG blob */
12803 if (logging != MagickFalse)
12804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12805 " Creating jpeg_image_info.");
12806 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12807 if (jpeg_image_info == (ImageInfo *) NULL)
12808 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12809
12810 if (logging != MagickFalse)
12811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12812 " Creating jpeg_image.");
12813
cristy16ea1392012-03-21 20:38:41 +000012814 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012815 if (jpeg_image == (Image *) NULL)
12816 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12817 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12818
12819 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012820 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012821 jpeg_image->filename);
12822
12823 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012824 exception);
cristy3ed852e2009-09-05 21:47:34 +000012825
12826 if (logging != MagickFalse)
12827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012828 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12829 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012830
12831 if (jng_color_type == 8 || jng_color_type == 12)
12832 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012833
glennrp59575fa2011-12-31 21:31:39 +000012834 jpeg_image_info->quality=jng_quality;
12835 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012836 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12837 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012838
cristy3ed852e2009-09-05 21:47:34 +000012839 if (logging != MagickFalse)
12840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12841 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012842
cristy16ea1392012-03-21 20:38:41 +000012843 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012844
cristy3ed852e2009-09-05 21:47:34 +000012845 if (logging != MagickFalse)
12846 {
12847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012848 " Successfully read jpeg_image into a blob, length=%.20g.",
12849 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012850
12851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012852 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012853 }
glennrp0fe50b42010-11-16 03:52:51 +000012854
cristy3ed852e2009-09-05 21:47:34 +000012855 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012856 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012857 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012858 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012859 (void) WriteBlob(image,4,chunk);
12860 (void) WriteBlob(image,length,blob);
12861 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12862
12863 jpeg_image=DestroyImage(jpeg_image);
12864 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12865 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12866 blob=(unsigned char *) RelinquishMagickMemory(blob);
12867
12868 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012869 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012870
12871 /* Write IEND chunk */
12872 (void) WriteBlobMSBULong(image,0L);
12873 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012874 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012875 (void) WriteBlob(image,4,chunk);
12876 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12877
12878 if (logging != MagickFalse)
12879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12880 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012881
cristy3ed852e2009-09-05 21:47:34 +000012882 return(status);
12883}
12884
12885
12886/*
12887%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12888% %
12889% %
12890% %
12891% W r i t e J N G I m a g e %
12892% %
12893% %
12894% %
12895%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12896%
12897% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12898%
12899% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12900%
12901% The format of the WriteJNGImage method is:
12902%
cristy16ea1392012-03-21 20:38:41 +000012903% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12904% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012905%
12906% A description of each parameter follows:
12907%
12908% o image_info: the image info.
12909%
12910% o image: The image.
12911%
cristy16ea1392012-03-21 20:38:41 +000012912% o exception: return any errors or warnings in this structure.
12913%
cristy3ed852e2009-09-05 21:47:34 +000012914%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12915*/
cristy16ea1392012-03-21 20:38:41 +000012916static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12917 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012918{
12919 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012920 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012921 logging,
cristy3ed852e2009-09-05 21:47:34 +000012922 status;
12923
12924 MngInfo
12925 *mng_info;
12926
cristy3ed852e2009-09-05 21:47:34 +000012927 /*
12928 Open image file.
12929 */
12930 assert(image_info != (const ImageInfo *) NULL);
12931 assert(image_info->signature == MagickSignature);
12932 assert(image != (Image *) NULL);
12933 assert(image->signature == MagickSignature);
12934 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012935 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012936 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012937 if (status == MagickFalse)
12938 return(status);
12939
12940 /*
12941 Allocate a MngInfo structure.
12942 */
12943 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012944 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012945 if (mng_info == (MngInfo *) NULL)
12946 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12947 /*
12948 Initialize members of the MngInfo structure.
12949 */
12950 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12951 mng_info->image=image;
12952 have_mng_structure=MagickTrue;
12953
12954 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12955
cristy16ea1392012-03-21 20:38:41 +000012956 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012957 (void) CloseBlob(image);
12958
12959 (void) CatchImageException(image);
12960 MngInfoFreeStruct(mng_info,&have_mng_structure);
12961 if (logging != MagickFalse)
12962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12963 return(status);
12964}
12965#endif
12966
cristy16ea1392012-03-21 20:38:41 +000012967static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12968 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012969{
12970 const char
12971 *option;
12972
12973 Image
12974 *next_image;
12975
12976 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012977 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012978 status;
12979
glennrp03812ae2010-12-24 01:31:34 +000012980 volatile MagickBooleanType
12981 logging;
12982
cristy3ed852e2009-09-05 21:47:34 +000012983 MngInfo
12984 *mng_info;
12985
12986 int
cristy3ed852e2009-09-05 21:47:34 +000012987 image_count,
12988 need_iterations,
12989 need_matte;
12990
12991 volatile int
12992#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12993 defined(PNG_MNG_FEATURES_SUPPORTED)
12994 need_local_plte,
12995#endif
12996 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012997 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012998 use_global_plte;
12999
cristybb503372010-05-27 20:51:26 +000013000 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000013001 i;
13002
13003 unsigned char
13004 chunk[800];
13005
13006 volatile unsigned int
13007 write_jng,
13008 write_mng;
13009
cristybb503372010-05-27 20:51:26 +000013010 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000013011 scene;
13012
cristybb503372010-05-27 20:51:26 +000013013 size_t
cristy3ed852e2009-09-05 21:47:34 +000013014 final_delay=0,
13015 initial_delay;
13016
glennrpd5045b42010-03-24 12:40:35 +000013017#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000013018 if (image_info->verbose)
13019 printf("Your PNG library (libpng-%s) is rather old.\n",
13020 PNG_LIBPNG_VER_STRING);
13021#endif
13022
13023 /*
13024 Open image file.
13025 */
13026 assert(image_info != (const ImageInfo *) NULL);
13027 assert(image_info->signature == MagickSignature);
13028 assert(image != (Image *) NULL);
13029 assert(image->signature == MagickSignature);
13030 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000013031 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy16ea1392012-03-21 20:38:41 +000013032 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000013033 if (status == MagickFalse)
13034 return(status);
13035
13036 /*
13037 Allocate a MngInfo structure.
13038 */
13039 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000013040 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000013041 if (mng_info == (MngInfo *) NULL)
13042 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13043 /*
13044 Initialize members of the MngInfo structure.
13045 */
13046 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13047 mng_info->image=image;
13048 have_mng_structure=MagickTrue;
13049 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13050
13051 /*
13052 * See if user has requested a specific PNG subformat to be used
13053 * for all of the PNGs in the MNG being written, e.g.,
13054 *
13055 * convert *.png png8:animation.mng
13056 *
13057 * To do: check -define png:bit_depth and png:color_type as well,
13058 * or perhaps use mng:bit_depth and mng:color_type instead for
13059 * global settings.
13060 */
13061
13062 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13063 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13064 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13065
13066 write_jng=MagickFalse;
13067 if (image_info->compression == JPEGCompression)
13068 write_jng=MagickTrue;
13069
13070 mng_info->adjoin=image_info->adjoin &&
13071 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13072
cristy3ed852e2009-09-05 21:47:34 +000013073 if (logging != MagickFalse)
13074 {
13075 /* Log some info about the input */
13076 Image
13077 *p;
13078
13079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13080 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000013081
cristy3ed852e2009-09-05 21:47:34 +000013082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013083 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000013084
cristy3ed852e2009-09-05 21:47:34 +000013085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13086 " Type: %d",image_info->type);
13087
13088 scene=0;
13089 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13090 {
13091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013092 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000013093
cristy3ed852e2009-09-05 21:47:34 +000013094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013095 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000013096
cristydc2d3272013-02-12 14:00:44 +000013097 if (p->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000013098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13099 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000013100
cristy3ed852e2009-09-05 21:47:34 +000013101 else
13102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13103 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000013104
cristy3ed852e2009-09-05 21:47:34 +000013105 if (p->storage_class == PseudoClass)
13106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13107 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000013108
cristy3ed852e2009-09-05 21:47:34 +000013109 else
13110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13111 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000013112
cristy3ed852e2009-09-05 21:47:34 +000013113 if (p->colors)
13114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013115 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000013116
cristy3ed852e2009-09-05 21:47:34 +000013117 else
13118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13119 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000013120
cristy3ed852e2009-09-05 21:47:34 +000013121 if (mng_info->adjoin == MagickFalse)
13122 break;
13123 }
13124 }
13125
cristy3ed852e2009-09-05 21:47:34 +000013126 use_global_plte=MagickFalse;
13127 all_images_are_gray=MagickFalse;
13128#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13129 need_local_plte=MagickTrue;
13130#endif
13131 need_defi=MagickFalse;
13132 need_matte=MagickFalse;
13133 mng_info->framing_mode=1;
13134 mng_info->old_framing_mode=1;
13135
13136 if (write_mng)
13137 if (image_info->page != (char *) NULL)
13138 {
13139 /*
13140 Determine image bounding box.
13141 */
13142 SetGeometry(image,&mng_info->page);
13143 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13144 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13145 }
13146 if (write_mng)
13147 {
13148 unsigned int
13149 need_geom;
13150
13151 unsigned short
13152 red,
13153 green,
13154 blue;
13155
13156 mng_info->page=image->page;
13157 need_geom=MagickTrue;
13158 if (mng_info->page.width || mng_info->page.height)
13159 need_geom=MagickFalse;
13160 /*
13161 Check all the scenes.
13162 */
13163 initial_delay=image->delay;
13164 need_iterations=MagickFalse;
13165 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13166 mng_info->equal_physs=MagickTrue,
13167 mng_info->equal_gammas=MagickTrue;
13168 mng_info->equal_srgbs=MagickTrue;
13169 mng_info->equal_backgrounds=MagickTrue;
13170 image_count=0;
13171#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13172 defined(PNG_MNG_FEATURES_SUPPORTED)
13173 all_images_are_gray=MagickTrue;
13174 mng_info->equal_palettes=MagickFalse;
13175 need_local_plte=MagickFalse;
13176#endif
13177 for (next_image=image; next_image != (Image *) NULL; )
13178 {
13179 if (need_geom)
13180 {
13181 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13182 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000013183
cristy3ed852e2009-09-05 21:47:34 +000013184 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13185 mng_info->page.height=next_image->rows+next_image->page.y;
13186 }
glennrp0fe50b42010-11-16 03:52:51 +000013187
cristy3ed852e2009-09-05 21:47:34 +000013188 if (next_image->page.x || next_image->page.y)
13189 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013190
cristydc2d3272013-02-12 14:00:44 +000013191 if (next_image->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000013192 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013193
cristy3ed852e2009-09-05 21:47:34 +000013194 if ((int) next_image->dispose >= BackgroundDispose)
cristydc2d3272013-02-12 14:00:44 +000013195 if ((next_image->alpha_trait == BlendPixelTrait) ||
13196 next_image->page.x || next_image->page.y ||
cristy3ed852e2009-09-05 21:47:34 +000013197 ((next_image->columns < mng_info->page.width) &&
13198 (next_image->rows < mng_info->page.height)))
13199 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013200
cristy3ed852e2009-09-05 21:47:34 +000013201 if (next_image->iterations)
13202 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013203
cristy3ed852e2009-09-05 21:47:34 +000013204 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000013205
cristy3ed852e2009-09-05 21:47:34 +000013206 if (final_delay != initial_delay || final_delay > 1UL*
13207 next_image->ticks_per_second)
13208 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000013209
cristy3ed852e2009-09-05 21:47:34 +000013210#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13211 defined(PNG_MNG_FEATURES_SUPPORTED)
13212 /*
13213 check for global palette possibility.
13214 */
cristy8a46d822012-08-28 23:32:39 +000013215 if (image->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000013216 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013217
cristy3ed852e2009-09-05 21:47:34 +000013218 if (need_local_plte == 0)
13219 {
cristy7fb26522012-06-21 13:02:48 +000013220 if (IsImageGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013221 all_images_are_gray=MagickFalse;
13222 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13223 if (use_global_plte == 0)
13224 use_global_plte=mng_info->equal_palettes;
13225 need_local_plte=!mng_info->equal_palettes;
13226 }
13227#endif
13228 if (GetNextImageInList(next_image) != (Image *) NULL)
13229 {
13230 if (next_image->background_color.red !=
13231 next_image->next->background_color.red ||
13232 next_image->background_color.green !=
13233 next_image->next->background_color.green ||
13234 next_image->background_color.blue !=
13235 next_image->next->background_color.blue)
13236 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013237
cristy3ed852e2009-09-05 21:47:34 +000013238 if (next_image->gamma != next_image->next->gamma)
13239 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013240
cristy3ed852e2009-09-05 21:47:34 +000013241 if (next_image->rendering_intent !=
13242 next_image->next->rendering_intent)
13243 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013244
cristy3ed852e2009-09-05 21:47:34 +000013245 if ((next_image->units != next_image->next->units) ||
cristy16ea1392012-03-21 20:38:41 +000013246 (next_image->resolution.x != next_image->next->resolution.x) ||
13247 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000013248 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013249
cristy3ed852e2009-09-05 21:47:34 +000013250 if (mng_info->equal_chrms)
13251 {
13252 if (next_image->chromaticity.red_primary.x !=
13253 next_image->next->chromaticity.red_primary.x ||
13254 next_image->chromaticity.red_primary.y !=
13255 next_image->next->chromaticity.red_primary.y ||
13256 next_image->chromaticity.green_primary.x !=
13257 next_image->next->chromaticity.green_primary.x ||
13258 next_image->chromaticity.green_primary.y !=
13259 next_image->next->chromaticity.green_primary.y ||
13260 next_image->chromaticity.blue_primary.x !=
13261 next_image->next->chromaticity.blue_primary.x ||
13262 next_image->chromaticity.blue_primary.y !=
13263 next_image->next->chromaticity.blue_primary.y ||
13264 next_image->chromaticity.white_point.x !=
13265 next_image->next->chromaticity.white_point.x ||
13266 next_image->chromaticity.white_point.y !=
13267 next_image->next->chromaticity.white_point.y)
13268 mng_info->equal_chrms=MagickFalse;
13269 }
13270 }
13271 image_count++;
13272 next_image=GetNextImageInList(next_image);
13273 }
13274 if (image_count < 2)
13275 {
13276 mng_info->equal_backgrounds=MagickFalse;
13277 mng_info->equal_chrms=MagickFalse;
13278 mng_info->equal_gammas=MagickFalse;
13279 mng_info->equal_srgbs=MagickFalse;
13280 mng_info->equal_physs=MagickFalse;
13281 use_global_plte=MagickFalse;
13282#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13283 need_local_plte=MagickTrue;
13284#endif
13285 need_iterations=MagickFalse;
13286 }
glennrp0fe50b42010-11-16 03:52:51 +000013287
cristy3ed852e2009-09-05 21:47:34 +000013288 if (mng_info->need_fram == MagickFalse)
13289 {
13290 /*
13291 Only certain framing rates 100/n are exactly representable without
13292 the FRAM chunk but we'll allow some slop in VLC files
13293 */
13294 if (final_delay == 0)
13295 {
13296 if (need_iterations != MagickFalse)
13297 {
13298 /*
13299 It's probably a GIF with loop; don't run it *too* fast.
13300 */
glennrp02617122010-07-28 13:07:35 +000013301 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000013302 {
13303 final_delay=10;
cristy16ea1392012-03-21 20:38:41 +000013304 (void) ThrowMagickException(exception,GetMagickModule(),
13305 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000013306 "input has zero delay between all frames; assuming",
13307 " 10 cs `%s'","");
13308 }
cristy3ed852e2009-09-05 21:47:34 +000013309 }
13310 else
13311 mng_info->ticks_per_second=0;
13312 }
13313 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000013314 mng_info->ticks_per_second=(png_uint_32)
13315 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000013316 if (final_delay > 50)
13317 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000013318
cristy3ed852e2009-09-05 21:47:34 +000013319 if (final_delay > 75)
13320 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000013321
cristy3ed852e2009-09-05 21:47:34 +000013322 if (final_delay > 125)
13323 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013324
cristy3ed852e2009-09-05 21:47:34 +000013325 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13326 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13327 (final_delay != 25) && (final_delay != 50) && (final_delay !=
13328 1UL*image->ticks_per_second))
13329 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13330 }
glennrp0fe50b42010-11-16 03:52:51 +000013331
cristy3ed852e2009-09-05 21:47:34 +000013332 if (mng_info->need_fram != MagickFalse)
13333 mng_info->ticks_per_second=1UL*image->ticks_per_second;
13334 /*
13335 If pseudocolor, we should also check to see if all the
13336 palettes are identical and write a global PLTE if they are.
13337 ../glennrp Feb 99.
13338 */
13339 /*
13340 Write the MNG version 1.0 signature and MHDR chunk.
13341 */
13342 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13343 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13344 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000013345 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000013346 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13347 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000013348 PNGLong(chunk+12,mng_info->ticks_per_second);
13349 PNGLong(chunk+16,0L); /* layer count=unknown */
13350 PNGLong(chunk+20,0L); /* frame count=unknown */
13351 PNGLong(chunk+24,0L); /* play time=unknown */
13352 if (write_jng)
13353 {
13354 if (need_matte)
13355 {
13356 if (need_defi || mng_info->need_fram || use_global_plte)
13357 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000013358
cristy3ed852e2009-09-05 21:47:34 +000013359 else
13360 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13361 }
glennrp0fe50b42010-11-16 03:52:51 +000013362
cristy3ed852e2009-09-05 21:47:34 +000013363 else
13364 {
13365 if (need_defi || mng_info->need_fram || use_global_plte)
13366 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000013367
cristy3ed852e2009-09-05 21:47:34 +000013368 else
13369 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13370 }
13371 }
glennrp0fe50b42010-11-16 03:52:51 +000013372
cristy3ed852e2009-09-05 21:47:34 +000013373 else
13374 {
13375 if (need_matte)
13376 {
13377 if (need_defi || mng_info->need_fram || use_global_plte)
13378 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000013379
cristy3ed852e2009-09-05 21:47:34 +000013380 else
13381 PNGLong(chunk+28,9L); /* simplicity=VLC */
13382 }
glennrp0fe50b42010-11-16 03:52:51 +000013383
cristy3ed852e2009-09-05 21:47:34 +000013384 else
13385 {
13386 if (need_defi || mng_info->need_fram || use_global_plte)
13387 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000013388
cristy3ed852e2009-09-05 21:47:34 +000013389 else
13390 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13391 }
13392 }
13393 (void) WriteBlob(image,32,chunk);
13394 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
cristy092ec8d2013-04-26 13:46:22 +000013395 option=GetImageOption(image_info,"mng:need-cacheoff");
cristy3ed852e2009-09-05 21:47:34 +000013396 if (option != (const char *) NULL)
13397 {
13398 size_t
13399 length;
13400
13401 /*
13402 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13403 */
13404 PNGType(chunk,mng_nEED);
13405 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000013406 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000013407 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000013408 length+=4;
13409 (void) WriteBlob(image,length,chunk);
13410 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13411 }
13412 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13413 (GetNextImageInList(image) != (Image *) NULL) &&
13414 (image->iterations != 1))
13415 {
13416 /*
13417 Write MNG TERM chunk
13418 */
13419 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13420 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000013421 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013422 chunk[4]=3; /* repeat animation */
13423 chunk[5]=0; /* show last frame when done */
13424 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13425 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000013426
cristy3ed852e2009-09-05 21:47:34 +000013427 if (image->iterations == 0)
13428 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000013429
cristy3ed852e2009-09-05 21:47:34 +000013430 else
13431 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000013432
cristy3ed852e2009-09-05 21:47:34 +000013433 if (logging != MagickFalse)
13434 {
13435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013436 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13437 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000013438
cristy3ed852e2009-09-05 21:47:34 +000013439 if (image->iterations == 0)
13440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013441 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000013442
cristy3ed852e2009-09-05 21:47:34 +000013443 else
13444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013445 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000013446 }
13447 (void) WriteBlob(image,14,chunk);
13448 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13449 }
13450 /*
13451 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13452 */
13453 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13454 mng_info->equal_srgbs)
13455 {
13456 /*
13457 Write MNG sRGB chunk
13458 */
13459 (void) WriteBlobMSBULong(image,1L);
13460 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000013461 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000013462
cristy3ed852e2009-09-05 21:47:34 +000013463 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000013464 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000013465 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000013466 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000013467
cristy3ed852e2009-09-05 21:47:34 +000013468 else
glennrpe610a072010-08-05 17:08:46 +000013469 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000013470 Magick_RenderingIntent_to_PNG_RenderingIntent(
13471 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000013472
cristy3ed852e2009-09-05 21:47:34 +000013473 (void) WriteBlob(image,5,chunk);
13474 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13475 mng_info->have_write_global_srgb=MagickTrue;
13476 }
glennrp0fe50b42010-11-16 03:52:51 +000013477
cristy3ed852e2009-09-05 21:47:34 +000013478 else
13479 {
13480 if (image->gamma && mng_info->equal_gammas)
13481 {
13482 /*
13483 Write MNG gAMA chunk
13484 */
13485 (void) WriteBlobMSBULong(image,4L);
13486 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000013487 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000013488 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013489 (void) WriteBlob(image,8,chunk);
13490 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13491 mng_info->have_write_global_gama=MagickTrue;
13492 }
13493 if (mng_info->equal_chrms)
13494 {
13495 PrimaryInfo
13496 primary;
13497
13498 /*
13499 Write MNG cHRM chunk
13500 */
13501 (void) WriteBlobMSBULong(image,32L);
13502 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000013503 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000013504 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000013505 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13506 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013507 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000013508 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13509 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013510 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000013511 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13512 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013513 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000013514 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13515 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013516 (void) WriteBlob(image,36,chunk);
13517 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13518 mng_info->have_write_global_chrm=MagickTrue;
13519 }
13520 }
cristy16ea1392012-03-21 20:38:41 +000013521 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000013522 {
13523 /*
13524 Write MNG pHYs chunk
13525 */
13526 (void) WriteBlobMSBULong(image,9L);
13527 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000013528 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000013529
cristy3ed852e2009-09-05 21:47:34 +000013530 if (image->units == PixelsPerInchResolution)
13531 {
cristy35ef8242010-06-03 16:24:13 +000013532 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013533 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013534
cristy35ef8242010-06-03 16:24:13 +000013535 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013536 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013537
cristy3ed852e2009-09-05 21:47:34 +000013538 chunk[12]=1;
13539 }
glennrp0fe50b42010-11-16 03:52:51 +000013540
cristy3ed852e2009-09-05 21:47:34 +000013541 else
13542 {
13543 if (image->units == PixelsPerCentimeterResolution)
13544 {
cristy35ef8242010-06-03 16:24:13 +000013545 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013546 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013547
cristy35ef8242010-06-03 16:24:13 +000013548 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013549 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013550
cristy3ed852e2009-09-05 21:47:34 +000013551 chunk[12]=1;
13552 }
glennrp0fe50b42010-11-16 03:52:51 +000013553
cristy3ed852e2009-09-05 21:47:34 +000013554 else
13555 {
cristy16ea1392012-03-21 20:38:41 +000013556 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13557 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013558 chunk[12]=0;
13559 }
13560 }
13561 (void) WriteBlob(image,13,chunk);
13562 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13563 }
13564 /*
13565 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13566 or does not cover the entire frame.
13567 */
cristydc2d3272013-02-12 14:00:44 +000013568 if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13569 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
cristy3ed852e2009-09-05 21:47:34 +000013570 (image->page.width+image->page.x < mng_info->page.width))
13571 || (image->page.height && (image->page.height+image->page.y
13572 < mng_info->page.height))))
13573 {
13574 (void) WriteBlobMSBULong(image,6L);
13575 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000013576 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000013577 red=ScaleQuantumToShort(image->background_color.red);
13578 green=ScaleQuantumToShort(image->background_color.green);
13579 blue=ScaleQuantumToShort(image->background_color.blue);
13580 PNGShort(chunk+4,red);
13581 PNGShort(chunk+6,green);
13582 PNGShort(chunk+8,blue);
13583 (void) WriteBlob(image,10,chunk);
13584 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13585 if (mng_info->equal_backgrounds)
13586 {
13587 (void) WriteBlobMSBULong(image,6L);
13588 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000013589 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000013590 (void) WriteBlob(image,10,chunk);
13591 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13592 }
13593 }
13594
13595#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13596 if ((need_local_plte == MagickFalse) &&
13597 (image->storage_class == PseudoClass) &&
13598 (all_images_are_gray == MagickFalse))
13599 {
cristybb503372010-05-27 20:51:26 +000013600 size_t
cristy3ed852e2009-09-05 21:47:34 +000013601 data_length;
13602
13603 /*
13604 Write MNG PLTE chunk
13605 */
13606 data_length=3*image->colors;
13607 (void) WriteBlobMSBULong(image,data_length);
13608 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013609 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013610
cristybb503372010-05-27 20:51:26 +000013611 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013612 {
cristy16ea1392012-03-21 20:38:41 +000013613 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13614 image->colormap[i].red) & 0xff);
13615 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13616 image->colormap[i].green) & 0xff);
13617 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13618 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000013619 }
glennrp0fe50b42010-11-16 03:52:51 +000013620
cristy3ed852e2009-09-05 21:47:34 +000013621 (void) WriteBlob(image,data_length+4,chunk);
13622 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13623 mng_info->have_write_global_plte=MagickTrue;
13624 }
13625#endif
13626 }
13627 scene=0;
13628 mng_info->delay=0;
13629#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13630 defined(PNG_MNG_FEATURES_SUPPORTED)
13631 mng_info->equal_palettes=MagickFalse;
13632#endif
13633 do
13634 {
13635 if (mng_info->adjoin)
13636 {
13637#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13638 defined(PNG_MNG_FEATURES_SUPPORTED)
13639 /*
13640 If we aren't using a global palette for the entire MNG, check to
13641 see if we can use one for two or more consecutive images.
13642 */
13643 if (need_local_plte && use_global_plte && !all_images_are_gray)
13644 {
13645 if (mng_info->IsPalette)
13646 {
13647 /*
13648 When equal_palettes is true, this image has the same palette
13649 as the previous PseudoClass image
13650 */
13651 mng_info->have_write_global_plte=mng_info->equal_palettes;
13652 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13653 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13654 {
13655 /*
13656 Write MNG PLTE chunk
13657 */
cristybb503372010-05-27 20:51:26 +000013658 size_t
cristy3ed852e2009-09-05 21:47:34 +000013659 data_length;
13660
13661 data_length=3*image->colors;
13662 (void) WriteBlobMSBULong(image,data_length);
13663 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013664 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013665
cristybb503372010-05-27 20:51:26 +000013666 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013667 {
13668 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13669 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13670 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13671 }
glennrp0fe50b42010-11-16 03:52:51 +000013672
cristy3ed852e2009-09-05 21:47:34 +000013673 (void) WriteBlob(image,data_length+4,chunk);
13674 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13675 (uInt) (data_length+4)));
13676 mng_info->have_write_global_plte=MagickTrue;
13677 }
13678 }
13679 else
13680 mng_info->have_write_global_plte=MagickFalse;
13681 }
13682#endif
13683 if (need_defi)
13684 {
cristybb503372010-05-27 20:51:26 +000013685 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000013686 previous_x,
13687 previous_y;
13688
13689 if (scene)
13690 {
13691 previous_x=mng_info->page.x;
13692 previous_y=mng_info->page.y;
13693 }
13694 else
13695 {
13696 previous_x=0;
13697 previous_y=0;
13698 }
13699 mng_info->page=image->page;
13700 if ((mng_info->page.x != previous_x) ||
13701 (mng_info->page.y != previous_y))
13702 {
13703 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13704 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000013705 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000013706 chunk[4]=0; /* object 0 MSB */
13707 chunk[5]=0; /* object 0 LSB */
13708 chunk[6]=0; /* visible */
13709 chunk[7]=0; /* abstract */
13710 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13711 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13712 (void) WriteBlob(image,16,chunk);
13713 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13714 }
13715 }
13716 }
13717
13718 mng_info->write_mng=write_mng;
13719
13720 if ((int) image->dispose >= 3)
13721 mng_info->framing_mode=3;
13722
13723 if (mng_info->need_fram && mng_info->adjoin &&
13724 ((image->delay != mng_info->delay) ||
13725 (mng_info->framing_mode != mng_info->old_framing_mode)))
13726 {
13727 if (image->delay == mng_info->delay)
13728 {
13729 /*
13730 Write a MNG FRAM chunk with the new framing mode.
13731 */
13732 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13733 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013734 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013735 chunk[4]=(unsigned char) mng_info->framing_mode;
13736 (void) WriteBlob(image,5,chunk);
13737 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13738 }
13739 else
13740 {
13741 /*
13742 Write a MNG FRAM chunk with the delay.
13743 */
13744 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13745 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013746 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013747 chunk[4]=(unsigned char) mng_info->framing_mode;
13748 chunk[5]=0; /* frame name separator (no name) */
13749 chunk[6]=2; /* flag for changing default delay */
13750 chunk[7]=0; /* flag for changing frame timeout */
13751 chunk[8]=0; /* flag for changing frame clipping */
13752 chunk[9]=0; /* flag for changing frame sync_id */
13753 PNGLong(chunk+10,(png_uint_32)
13754 ((mng_info->ticks_per_second*
13755 image->delay)/MagickMax(image->ticks_per_second,1)));
13756 (void) WriteBlob(image,14,chunk);
13757 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013758 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013759 }
13760 mng_info->old_framing_mode=mng_info->framing_mode;
13761 }
13762
13763#if defined(JNG_SUPPORTED)
13764 if (image_info->compression == JPEGCompression)
13765 {
13766 ImageInfo
13767 *write_info;
13768
13769 if (logging != MagickFalse)
13770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13771 " Writing JNG object.");
13772 /* To do: specify the desired alpha compression method. */
13773 write_info=CloneImageInfo(image_info);
13774 write_info->compression=UndefinedCompression;
cristy16ea1392012-03-21 20:38:41 +000013775 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013776 write_info=DestroyImageInfo(write_info);
13777 }
13778 else
13779#endif
13780 {
13781 if (logging != MagickFalse)
13782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13783 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013784
glennrpb9cfe272010-12-21 15:08:06 +000013785 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013786 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013787
13788 /* We don't want any ancillary chunks written */
13789 mng_info->ping_exclude_bKGD=MagickTrue;
13790 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013791 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013792 mng_info->ping_exclude_EXIF=MagickTrue;
13793 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013794 mng_info->ping_exclude_iCCP=MagickTrue;
13795 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13796 mng_info->ping_exclude_oFFs=MagickTrue;
13797 mng_info->ping_exclude_pHYs=MagickTrue;
13798 mng_info->ping_exclude_sRGB=MagickTrue;
13799 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013800 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013801 mng_info->ping_exclude_vpAg=MagickTrue;
13802 mng_info->ping_exclude_zCCP=MagickTrue;
13803 mng_info->ping_exclude_zTXt=MagickTrue;
13804
cristy16ea1392012-03-21 20:38:41 +000013805 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013806 }
13807
13808 if (status == MagickFalse)
13809 {
13810 MngInfoFreeStruct(mng_info,&have_mng_structure);
13811 (void) CloseBlob(image);
13812 return(MagickFalse);
13813 }
13814 (void) CatchImageException(image);
13815 if (GetNextImageInList(image) == (Image *) NULL)
13816 break;
13817 image=SyncNextImageInList(image);
13818 status=SetImageProgress(image,SaveImagesTag,scene++,
13819 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013820
cristy3ed852e2009-09-05 21:47:34 +000013821 if (status == MagickFalse)
13822 break;
glennrp0fe50b42010-11-16 03:52:51 +000013823
cristy3ed852e2009-09-05 21:47:34 +000013824 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013825
cristy3ed852e2009-09-05 21:47:34 +000013826 if (write_mng)
13827 {
13828 while (GetPreviousImageInList(image) != (Image *) NULL)
13829 image=GetPreviousImageInList(image);
13830 /*
13831 Write the MEND chunk.
13832 */
13833 (void) WriteBlobMSBULong(image,0x00000000L);
13834 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013835 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013836 (void) WriteBlob(image,4,chunk);
13837 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13838 }
13839 /*
13840 Relinquish resources.
13841 */
13842 (void) CloseBlob(image);
13843 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013844
cristy3ed852e2009-09-05 21:47:34 +000013845 if (logging != MagickFalse)
13846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013847
cristy3ed852e2009-09-05 21:47:34 +000013848 return(MagickTrue);
13849}
glennrpd5045b42010-03-24 12:40:35 +000013850#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013851
cristy3ed852e2009-09-05 21:47:34 +000013852static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13853{
glennrp3bd393f2011-12-21 18:54:53 +000013854 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013855 printf("Your PNG library is too old: You have libpng-%s\n",
13856 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013857
cristy3ed852e2009-09-05 21:47:34 +000013858 ThrowBinaryException(CoderError,"PNG library is too old",
13859 image_info->filename);
13860}
glennrp39992b42010-11-14 00:03:43 +000013861
cristy3ed852e2009-09-05 21:47:34 +000013862static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13863{
13864 return(WritePNGImage(image_info,image));
13865}
glennrpd5045b42010-03-24 12:40:35 +000013866#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013867#endif