blob: 7a6485107f400daa787e6c92383d85493f82a4cd [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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristyfe676ee2013-11-18 13:03:38 +000021% Copyright 1999-2014 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*/
glennrp868fff32014-03-16 22:09:06 +0000631#ifdef PNG_SETJMP_SUPPORTED
632# ifndef IMPNG_SETJMP_IS_THREAD_SAFE
633# define IMPNG_SETJMP_NOT_THREAD_SAFE
634# endif
cristy3ed852e2009-09-05 21:47:34 +0000635
glennrp868fff32014-03-16 22:09:06 +0000636# ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
cristy3ed852e2009-09-05 21:47:34 +0000637static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000638 *ping_semaphore = (SemaphoreInfo *) NULL;
glennrp868fff32014-03-16 22:09:06 +0000639# endif
cristy3ed852e2009-09-05 21:47:34 +0000640#endif
641
642/*
643 This temporary until I set up malloc'ed object attributes array.
644 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
645 waste more memory.
646*/
647#define MNG_MAX_OBJECTS 256
648
649/*
650 If this not defined, spec is interpreted strictly. If it is
651 defined, an attempt will be made to recover from some errors,
652 including
653 o global PLTE too short
654*/
655#undef MNG_LOOSE
656
657/*
658 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
659 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
660 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
661 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
662 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
663 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
664 will be enabled by default in libpng-1.2.0.
665*/
cristy3ed852e2009-09-05 21:47:34 +0000666#ifdef PNG_MNG_FEATURES_SUPPORTED
667# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
668# define PNG_READ_EMPTY_PLTE_SUPPORTED
669# endif
670# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
671# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
672# endif
673#endif
674
675/*
cristybb503372010-05-27 20:51:26 +0000676 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000677 This macro is only defined in libpng-1.0.3 and later.
678 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
679*/
680#ifndef PNG_UINT_31_MAX
681#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
682#endif
683
684/*
685 Constant strings for known chunk types. If you need to add a chunk,
686 add a string holding the name here. To make the code more
687 portable, we use ASCII numbers like this, not characters.
688*/
689
glennrp85dcf872011-12-07 02:51:47 +0000690static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
691static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
692static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
693static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
694static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
695static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
696static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
697static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
698static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
699static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
700static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
701static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
702static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
703static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
704static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
705static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
706static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
707static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
708static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
709static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
710static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
711static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
712static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
713static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
714static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
715static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
716static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
717static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
718static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
719static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
720static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
721static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
722static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
723static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000724
725#if defined(JNG_SUPPORTED)
glennrp85dcf872011-12-07 02:51:47 +0000726static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
727static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
728static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
729static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
730static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
731static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000732#endif
733
glennrp689efa22014-07-25 00:00:15 +0000734#if 0
735/* Other known chunks that are not yet supported by ImageMagick: */
glennrp85dcf872011-12-07 02:51:47 +0000736static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
glennrp85dcf872011-12-07 02:51:47 +0000737static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
738static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
739static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
740static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
741static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
742static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
glennrp689efa22014-07-25 00:00:15 +0000743#endif
cristy3ed852e2009-09-05 21:47:34 +0000744
745typedef struct _MngBox
746{
cristy8182b072010-05-30 20:10:53 +0000747 long
cristy3ed852e2009-09-05 21:47:34 +0000748 left,
749 right,
750 top,
751 bottom;
752} MngBox;
753
754typedef struct _MngPair
755{
cristy8182b072010-05-30 20:10:53 +0000756 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000757 a,
758 b;
759} MngPair;
760
761#ifdef MNG_OBJECT_BUFFERS
762typedef struct _MngBuffer
763{
764
cristybb503372010-05-27 20:51:26 +0000765 size_t
cristy3ed852e2009-09-05 21:47:34 +0000766 height,
767 width;
768
769 Image
770 *image;
771
772 png_color
773 plte[256];
774
775 int
776 reference_count;
777
778 unsigned char
779 alpha_sample_depth,
780 compression_method,
781 color_type,
782 concrete,
783 filter_method,
784 frozen,
785 image_type,
786 interlace_method,
787 pixel_sample_depth,
788 plte_length,
789 sample_depth,
790 viewable;
791} MngBuffer;
792#endif
793
794typedef struct _MngInfo
795{
796
797#ifdef MNG_OBJECT_BUFFERS
798 MngBuffer
799 *ob[MNG_MAX_OBJECTS];
800#endif
801
802 Image *
803 image;
804
805 RectangleInfo
806 page;
807
808 int
809 adjoin,
810#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
811 bytes_in_read_buffer,
812 found_empty_plte,
813#endif
814 equal_backgrounds,
815 equal_chrms,
816 equal_gammas,
817#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
818 defined(PNG_MNG_FEATURES_SUPPORTED)
819 equal_palettes,
820#endif
821 equal_physs,
822 equal_srgbs,
823 framing_mode,
824 have_global_bkgd,
825 have_global_chrm,
826 have_global_gama,
827 have_global_phys,
828 have_global_sbit,
829 have_global_srgb,
830 have_saved_bkgd_index,
831 have_write_global_chrm,
832 have_write_global_gama,
833 have_write_global_plte,
834 have_write_global_srgb,
835 need_fram,
836 object_id,
837 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000838 saved_bkgd_index;
839
840 int
841 new_number_colors;
842
cristybb503372010-05-27 20:51:26 +0000843 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000844 image_found,
845 loop_count[256],
846 loop_iteration[256],
847 scenes_found,
848 x_off[MNG_MAX_OBJECTS],
849 y_off[MNG_MAX_OBJECTS];
850
851 MngBox
852 clip,
853 frame,
854 image_box,
855 object_clip[MNG_MAX_OBJECTS];
856
857 unsigned char
858 /* These flags could be combined into one byte */
859 exists[MNG_MAX_OBJECTS],
860 frozen[MNG_MAX_OBJECTS],
861 loop_active[256],
862 invisible[MNG_MAX_OBJECTS],
863 viewable[MNG_MAX_OBJECTS];
864
865 MagickOffsetType
866 loop_jump[256];
867
868 png_colorp
869 global_plte;
870
871 png_color_8
872 global_sbit;
873
874 png_byte
875#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
876 read_buffer[8],
877#endif
878 global_trns[256];
879
880 float
881 global_gamma;
882
883 ChromaticityInfo
884 global_chrm;
885
886 RenderingIntent
887 global_srgb_intent;
888
cristy35ef8242010-06-03 16:24:13 +0000889 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000890 delay,
891 global_plte_length,
892 global_trns_length,
893 global_x_pixels_per_unit,
894 global_y_pixels_per_unit,
895 mng_width,
896 mng_height,
897 ticks_per_second;
898
glennrpb9cfe272010-12-21 15:08:06 +0000899 MagickBooleanType
900 need_blob;
901
cristy3ed852e2009-09-05 21:47:34 +0000902 unsigned int
903 IsPalette,
904 global_phys_unit_type,
905 basi_warning,
906 clon_warning,
907 dhdr_warning,
908 jhdr_warning,
909 magn_warning,
910 past_warning,
911 phyg_warning,
912 phys_warning,
913 sbit_warning,
914 show_warning,
915 mng_type,
916 write_mng,
917 write_png_colortype,
918 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000919 write_png_compression_level,
920 write_png_compression_strategy,
921 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000922 write_png8,
923 write_png24,
glennrpfd164d22013-01-26 21:10:22 +0000924 write_png32,
925 write_png48,
926 write_png64;
cristy3ed852e2009-09-05 21:47:34 +0000927
928#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000929 size_t
cristy3ed852e2009-09-05 21:47:34 +0000930 basi_width,
931 basi_height;
932
933 unsigned int
934 basi_depth,
935 basi_color_type,
936 basi_compression_method,
937 basi_filter_type,
938 basi_interlace_method,
939 basi_red,
940 basi_green,
941 basi_blue,
942 basi_alpha,
943 basi_viewable;
944#endif
945
946 png_uint_16
947 magn_first,
948 magn_last,
949 magn_mb,
950 magn_ml,
951 magn_mr,
952 magn_mt,
953 magn_mx,
954 magn_my,
955 magn_methx,
956 magn_methy;
957
cristy16ea1392012-03-21 20:38:41 +0000958 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000959 mng_global_bkgd;
960
glennrp26f37912010-12-23 16:22:42 +0000961 /* Added at version 6.6.6-7 */
962 MagickBooleanType
963 ping_exclude_bKGD,
964 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000965 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000966 ping_exclude_EXIF,
967 ping_exclude_gAMA,
968 ping_exclude_iCCP,
969 /* ping_exclude_iTXt, */
970 ping_exclude_oFFs,
971 ping_exclude_pHYs,
972 ping_exclude_sRGB,
973 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000974 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000975 ping_exclude_vpAg,
976 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000977 ping_exclude_zTXt,
glennrpecab7d72013-05-14 22:50:32 +0000978 ping_preserve_colormap,
979 /* Added at version 6.8.5-7 */
980 ping_preserve_iCCP;
glennrp26f37912010-12-23 16:22:42 +0000981
cristy3ed852e2009-09-05 21:47:34 +0000982} MngInfo;
983#endif /* VER */
984
985/*
986 Forward declarations.
987*/
988static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000989 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000990
cristy3ed852e2009-09-05 21:47:34 +0000991static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000992 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000993
cristy3ed852e2009-09-05 21:47:34 +0000994#if defined(JNG_SUPPORTED)
995static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000996 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000997#endif
998
glennrp0c3e06b2010-11-19 13:45:02 +0000999#if PNG_LIBPNG_VER > 10011
1000
glennrpfd05d622011-02-25 04:10:33 +00001001
glennrp0c3e06b2010-11-19 13:45:02 +00001002#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
1003static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +00001004LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
glennrp0c3e06b2010-11-19 13:45:02 +00001005{
glennrp67b9c1a2011-04-22 18:47:36 +00001006 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
1007 *
1008 * This is true if the high byte and the next highest byte of
1009 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +00001010 * are equal to each other. We check this by seeing if the samples
1011 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +00001012 *
1013 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +00001014 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +00001015 */
1016
glennrp3faa9a32011-04-23 14:00:25 +00001017#define QuantumToCharToQuantumEqQuantum(quantum) \
1018 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
1019
glennrp0c3e06b2010-11-19 13:45:02 +00001020 MagickBooleanType
1021 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001022
glennrp03e11f62011-04-22 13:30:16 +00001023 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +00001024 {
1025
cristy16ea1392012-03-21 20:38:41 +00001026 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +00001027 *p;
1028
1029 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +00001030 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
1031 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
1032 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
1033 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001034
1035 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
1036 {
1037 int indx;
1038
1039 for (indx=0; indx < (ssize_t) image->colors; indx++)
1040 {
glennrp3faa9a32011-04-23 14:00:25 +00001041 ok_to_reduce=(
1042 QuantumToCharToQuantumEqQuantum(
1043 image->colormap[indx].red) &&
1044 QuantumToCharToQuantumEqQuantum(
1045 image->colormap[indx].green) &&
1046 QuantumToCharToQuantumEqQuantum(
1047 image->colormap[indx].blue)) ?
1048 MagickTrue : MagickFalse;
1049
glennrp0c3e06b2010-11-19 13:45:02 +00001050 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00001051 break;
glennrp0c3e06b2010-11-19 13:45:02 +00001052 }
1053 }
1054
1055 if ((ok_to_reduce != MagickFalse) &&
1056 (image->storage_class != PseudoClass))
1057 {
1058 ssize_t
1059 y;
1060
1061 register ssize_t
1062 x;
1063
1064 for (y=0; y < (ssize_t) image->rows; y++)
1065 {
cristy16ea1392012-03-21 20:38:41 +00001066 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0c3e06b2010-11-19 13:45:02 +00001067
cristy16ea1392012-03-21 20:38:41 +00001068 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001069 {
1070 ok_to_reduce = MagickFalse;
1071 break;
1072 }
1073
1074 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1075 {
glennrp3faa9a32011-04-23 14:00:25 +00001076 ok_to_reduce=
cristy16ea1392012-03-21 20:38:41 +00001077 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1078 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1079 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001080 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001081
1082 if (ok_to_reduce == MagickFalse)
1083 break;
1084
cristy16ea1392012-03-21 20:38:41 +00001085 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001086 }
glennrp8640fb52010-11-23 15:48:26 +00001087 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001088 break;
1089 }
1090 }
1091
1092 if (ok_to_reduce != MagickFalse)
1093 {
glennrp0c3e06b2010-11-19 13:45:02 +00001094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001095 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001096 }
glennrpa6a06632011-01-19 15:15:34 +00001097 else
1098 {
1099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001100 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001101 }
glennrp0c3e06b2010-11-19 13:45:02 +00001102 }
1103
1104 return ok_to_reduce;
1105}
1106#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1107
glennrp1a56e9c2012-04-25 17:06:57 +00001108static const char* PngColorTypeToString(const unsigned int color_type)
1109{
1110 const char
1111 *result = "Unknown";
1112
1113 switch (color_type)
1114 {
1115 case PNG_COLOR_TYPE_GRAY:
1116 result = "Gray";
1117 break;
1118 case PNG_COLOR_TYPE_GRAY_ALPHA:
1119 result = "Gray+Alpha";
1120 break;
1121 case PNG_COLOR_TYPE_PALETTE:
1122 result = "Palette";
1123 break;
1124 case PNG_COLOR_TYPE_RGB:
1125 result = "RGB";
1126 break;
1127 case PNG_COLOR_TYPE_RGB_ALPHA:
1128 result = "RGB+Alpha";
1129 break;
1130 }
1131
1132 return result;
1133}
1134
glennrpe610a072010-08-05 17:08:46 +00001135static int
glennrpcf002022011-01-30 02:38:15 +00001136Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001137{
glennrpe610a072010-08-05 17:08:46 +00001138 switch (intent)
1139 {
1140 case PerceptualIntent:
1141 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001142
glennrpe610a072010-08-05 17:08:46 +00001143 case RelativeIntent:
1144 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001145
glennrpe610a072010-08-05 17:08:46 +00001146 case SaturationIntent:
1147 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001148
glennrpe610a072010-08-05 17:08:46 +00001149 case AbsoluteIntent:
1150 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001151
glennrpe610a072010-08-05 17:08:46 +00001152 default:
1153 return -1;
1154 }
1155}
1156
1157static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001158Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001159{
glennrpcf002022011-01-30 02:38:15 +00001160 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001161 {
1162 case 0:
1163 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001164
glennrpe610a072010-08-05 17:08:46 +00001165 case 1:
1166 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001167
glennrpe610a072010-08-05 17:08:46 +00001168 case 2:
1169 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001170
glennrpe610a072010-08-05 17:08:46 +00001171 case 3:
1172 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001173
glennrpe610a072010-08-05 17:08:46 +00001174 default:
1175 return UndefinedIntent;
1176 }
1177}
1178
cristy9d8c1222012-08-10 12:34:19 +00001179static const char *
glennrp98b83d42012-07-23 02:50:31 +00001180Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1181{
1182 switch (ping_intent)
1183 {
1184 case 0:
1185 return "Perceptual Intent";
1186
1187 case 1:
1188 return "Relative Intent";
1189
1190 case 2:
1191 return "Saturation Intent";
1192
1193 case 3:
1194 return "Absolute Intent";
1195
1196 default:
1197 return "Undefined Intent";
1198 }
1199}
1200
cristybb503372010-05-27 20:51:26 +00001201static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001202{
1203 if (x > y)
1204 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001205
cristy3ed852e2009-09-05 21:47:34 +00001206 return(y);
1207}
glennrp0c3e06b2010-11-19 13:45:02 +00001208
cristyd9ecd042012-06-17 18:26:12 +00001209static const char *
glennrp5dff4352012-06-06 22:12:04 +00001210Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1211{
1212 switch (ping_colortype)
1213 {
1214 case 0:
1215 return "Grayscale";
1216
1217 case 2:
1218 return "Truecolor";
1219
1220 case 3:
1221 return "Indexed";
1222
1223 case 4:
1224 return "GrayAlpha";
1225
1226 case 6:
1227 return "RGBA";
1228
1229 default:
1230 return "UndefinedColorType";
1231 }
1232}
1233
1234
cristybb503372010-05-27 20:51:26 +00001235static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001236{
1237 if (x < y)
1238 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001239
cristy3ed852e2009-09-05 21:47:34 +00001240 return(y);
1241}
glennrpd5045b42010-03-24 12:40:35 +00001242#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001243#endif /* MAGICKCORE_PNG_DELEGATE */
1244
1245/*
1246%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1247% %
1248% %
1249% %
1250% I s M N G %
1251% %
1252% %
1253% %
1254%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1255%
1256% IsMNG() returns MagickTrue if the image format type, identified by the
1257% magick string, is MNG.
1258%
1259% The format of the IsMNG method is:
1260%
1261% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1262%
1263% A description of each parameter follows:
1264%
1265% o magick: compare image format pattern against these bytes.
1266%
1267% o length: Specifies the length of the magick string.
1268%
1269%
1270*/
1271static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1272{
1273 if (length < 8)
1274 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001275
cristy3ed852e2009-09-05 21:47:34 +00001276 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1277 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001278
cristy3ed852e2009-09-05 21:47:34 +00001279 return(MagickFalse);
1280}
1281
1282/*
1283%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1284% %
1285% %
1286% %
1287% I s J N G %
1288% %
1289% %
1290% %
1291%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1292%
1293% IsJNG() returns MagickTrue if the image format type, identified by the
1294% magick string, is JNG.
1295%
1296% The format of the IsJNG method is:
1297%
1298% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1299%
1300% A description of each parameter follows:
1301%
1302% o magick: compare image format pattern against these bytes.
1303%
1304% o length: Specifies the length of the magick string.
1305%
1306%
1307*/
1308static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1309{
1310 if (length < 8)
1311 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001312
cristy3ed852e2009-09-05 21:47:34 +00001313 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1314 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001315
cristy3ed852e2009-09-05 21:47:34 +00001316 return(MagickFalse);
1317}
1318
1319/*
1320%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1321% %
1322% %
1323% %
1324% I s P N G %
1325% %
1326% %
1327% %
1328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1329%
1330% IsPNG() returns MagickTrue if the image format type, identified by the
1331% magick string, is PNG.
1332%
1333% The format of the IsPNG method is:
1334%
1335% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1336%
1337% A description of each parameter follows:
1338%
1339% o magick: compare image format pattern against these bytes.
1340%
1341% o length: Specifies the length of the magick string.
1342%
1343*/
1344static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1345{
1346 if (length < 8)
1347 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001348
cristy3ed852e2009-09-05 21:47:34 +00001349 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1350 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001351
cristy3ed852e2009-09-05 21:47:34 +00001352 return(MagickFalse);
1353}
1354
1355#if defined(MAGICKCORE_PNG_DELEGATE)
1356#if defined(__cplusplus) || defined(c_plusplus)
1357extern "C" {
1358#endif
1359
glennrpd5045b42010-03-24 12:40:35 +00001360#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001361static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001362{
1363 unsigned char
1364 buffer[4];
1365
1366 assert(image != (Image *) NULL);
1367 assert(image->signature == MagickSignature);
1368 buffer[0]=(unsigned char) (value >> 24);
1369 buffer[1]=(unsigned char) (value >> 16);
1370 buffer[2]=(unsigned char) (value >> 8);
1371 buffer[3]=(unsigned char) value;
1372 return((size_t) WriteBlob(image,4,buffer));
1373}
1374
1375static void PNGLong(png_bytep p,png_uint_32 value)
1376{
1377 *p++=(png_byte) ((value >> 24) & 0xff);
1378 *p++=(png_byte) ((value >> 16) & 0xff);
1379 *p++=(png_byte) ((value >> 8) & 0xff);
1380 *p++=(png_byte) (value & 0xff);
1381}
1382
glennrpa521b2f2010-10-29 04:11:03 +00001383#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001384static void PNGsLong(png_bytep p,png_int_32 value)
1385{
1386 *p++=(png_byte) ((value >> 24) & 0xff);
1387 *p++=(png_byte) ((value >> 16) & 0xff);
1388 *p++=(png_byte) ((value >> 8) & 0xff);
1389 *p++=(png_byte) (value & 0xff);
1390}
glennrpa521b2f2010-10-29 04:11:03 +00001391#endif
cristy3ed852e2009-09-05 21:47:34 +00001392
1393static void PNGShort(png_bytep p,png_uint_16 value)
1394{
1395 *p++=(png_byte) ((value >> 8) & 0xff);
1396 *p++=(png_byte) (value & 0xff);
1397}
1398
1399static void PNGType(png_bytep p,png_bytep type)
1400{
1401 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1402}
1403
glennrp03812ae2010-12-24 01:31:34 +00001404static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1405 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001406{
1407 if (logging != MagickFalse)
1408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001409 " Writing %c%c%c%c chunk, length: %.20g",
1410 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001411}
glennrpd5045b42010-03-24 12:40:35 +00001412#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001413
1414#if defined(__cplusplus) || defined(c_plusplus)
1415}
1416#endif
1417
glennrpd5045b42010-03-24 12:40:35 +00001418#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001419/*
1420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1421% %
1422% %
1423% %
1424% R e a d P N G I m a g e %
1425% %
1426% %
1427% %
1428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1429%
1430% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1431% Multiple-image Network Graphics (MNG) image file and returns it. It
1432% allocates the memory necessary for the new Image structure and returns a
1433% pointer to the new image or set of images.
1434%
1435% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1436%
1437% The format of the ReadPNGImage method is:
1438%
1439% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1440%
1441% A description of each parameter follows:
1442%
1443% o image_info: the image info.
1444%
1445% o exception: return any errors or warnings in this structure.
1446%
1447% To do, more or less in chronological order (as of version 5.5.2,
1448% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1449%
1450% Get 16-bit cheap transparency working.
1451%
1452% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1453%
1454% Preserve all unknown and not-yet-handled known chunks found in input
1455% PNG file and copy them into output PNG files according to the PNG
1456% copying rules.
1457%
1458% (At this point, PNG encoding should be in full MNG compliance)
1459%
1460% Provide options for choice of background to use when the MNG BACK
1461% chunk is not present or is not mandatory (i.e., leave transparent,
1462% user specified, MNG BACK, PNG bKGD)
1463%
1464% Implement LOOP/ENDL [done, but could do discretionary loops more
1465% efficiently by linking in the duplicate frames.].
1466%
1467% Decode and act on the MHDR simplicity profile (offer option to reject
1468% files or attempt to process them anyway when the profile isn't LC or VLC).
1469%
1470% Upgrade to full MNG without Delta-PNG.
1471%
1472% o BACK [done a while ago except for background image ID]
1473% o MOVE [done 15 May 1999]
1474% o CLIP [done 15 May 1999]
1475% o DISC [done 19 May 1999]
1476% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1477% o SEEK [partially done 19 May 1999 (discard function only)]
1478% o SHOW
1479% o PAST
1480% o BASI
1481% o MNG-level tEXt/iTXt/zTXt
1482% o pHYg
1483% o pHYs
1484% o sBIT
1485% o bKGD
1486% o iTXt (wait for libpng implementation).
1487%
1488% Use the scene signature to discover when an identical scene is
1489% being reused, and just point to the original image->exception instead
1490% of storing another set of pixels. This not specific to MNG
1491% but could be applied generally.
1492%
1493% Upgrade to full MNG with Delta-PNG.
1494%
1495% JNG tEXt/iTXt/zTXt
1496%
1497% We will not attempt to read files containing the CgBI chunk.
1498% They are really Xcode files meant for display on the iPhone.
1499% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001500% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001501% since irretrievable loss of color data has occurred due to the
1502% use of premultiplied alpha.
1503*/
1504
1505#if defined(__cplusplus) || defined(c_plusplus)
1506extern "C" {
1507#endif
1508
1509/*
1510 This the function that does the actual reading of data. It is
1511 the same as the one supplied in libpng, except that it receives the
1512 datastream from the ReadBlob() function instead of standard input.
1513*/
1514static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1515{
1516 Image
1517 *image;
1518
1519 image=(Image *) png_get_io_ptr(png_ptr);
1520 if (length)
1521 {
1522 png_size_t
1523 check;
1524
1525 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1526 if (check != length)
1527 {
1528 char
1529 msg[MaxTextExtent];
1530
cristy3b6fd2e2011-05-20 12:53:50 +00001531 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001532 "Expected %.20g bytes; found %.20g bytes",(double) length,
1533 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001534 png_warning(png_ptr,msg);
1535 png_error(png_ptr,"Read Exception");
1536 }
1537 }
1538}
1539
1540#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1541 !defined(PNG_MNG_FEATURES_SUPPORTED)
1542/* We use mng_get_data() instead of png_get_data() if we have a libpng
1543 * older than libpng-1.0.3a, which was the first to allow the empty
1544 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1545 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1546 * encountered after an empty PLTE, so we have to look ahead for bKGD
1547 * chunks and remove them from the datastream that is passed to libpng,
1548 * and store their contents for later use.
1549 */
1550static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1551{
1552 MngInfo
1553 *mng_info;
1554
1555 Image
1556 *image;
1557
1558 png_size_t
1559 check;
1560
cristybb503372010-05-27 20:51:26 +00001561 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001562 i;
1563
1564 i=0;
1565 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1566 image=(Image *) mng_info->image;
1567 while (mng_info->bytes_in_read_buffer && length)
1568 {
1569 data[i]=mng_info->read_buffer[i];
1570 mng_info->bytes_in_read_buffer--;
1571 length--;
1572 i++;
1573 }
1574 if (length)
1575 {
1576 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001577
cristy3ed852e2009-09-05 21:47:34 +00001578 if (check != length)
1579 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001580
cristy3ed852e2009-09-05 21:47:34 +00001581 if (length == 4)
1582 {
1583 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1584 (data[3] == 0))
1585 {
1586 check=(png_size_t) ReadBlob(image,(size_t) length,
1587 (char *) mng_info->read_buffer);
1588 mng_info->read_buffer[4]=0;
1589 mng_info->bytes_in_read_buffer=4;
1590 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1591 mng_info->found_empty_plte=MagickTrue;
1592 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1593 {
1594 mng_info->found_empty_plte=MagickFalse;
1595 mng_info->have_saved_bkgd_index=MagickFalse;
1596 }
1597 }
glennrp0fe50b42010-11-16 03:52:51 +00001598
cristy3ed852e2009-09-05 21:47:34 +00001599 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1600 (data[3] == 1))
1601 {
1602 check=(png_size_t) ReadBlob(image,(size_t) length,
1603 (char *) mng_info->read_buffer);
1604 mng_info->read_buffer[4]=0;
1605 mng_info->bytes_in_read_buffer=4;
1606 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1607 if (mng_info->found_empty_plte)
1608 {
1609 /*
1610 Skip the bKGD data byte and CRC.
1611 */
1612 check=(png_size_t)
1613 ReadBlob(image,5,(char *) mng_info->read_buffer);
1614 check=(png_size_t) ReadBlob(image,(size_t) length,
1615 (char *) mng_info->read_buffer);
1616 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1617 mng_info->have_saved_bkgd_index=MagickTrue;
1618 mng_info->bytes_in_read_buffer=0;
1619 }
1620 }
1621 }
1622 }
1623}
1624#endif
1625
1626static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1627{
1628 Image
1629 *image;
1630
1631 image=(Image *) png_get_io_ptr(png_ptr);
1632 if (length)
1633 {
1634 png_size_t
1635 check;
1636
cristybb503372010-05-27 20:51:26 +00001637 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001638
cristy3ed852e2009-09-05 21:47:34 +00001639 if (check != length)
1640 png_error(png_ptr,"WriteBlob Failed");
1641 }
1642}
1643
1644static void png_flush_data(png_structp png_ptr)
1645{
1646 (void) png_ptr;
1647}
1648
1649#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1650static int PalettesAreEqual(Image *a,Image *b)
1651{
cristybb503372010-05-27 20:51:26 +00001652 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001653 i;
1654
1655 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1656 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001657
cristy3ed852e2009-09-05 21:47:34 +00001658 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1659 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001660
cristy3ed852e2009-09-05 21:47:34 +00001661 if (a->colors != b->colors)
1662 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001663
cristybb503372010-05-27 20:51:26 +00001664 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001665 {
1666 if ((a->colormap[i].red != b->colormap[i].red) ||
1667 (a->colormap[i].green != b->colormap[i].green) ||
1668 (a->colormap[i].blue != b->colormap[i].blue))
1669 return((int) MagickFalse);
1670 }
glennrp0fe50b42010-11-16 03:52:51 +00001671
cristy3ed852e2009-09-05 21:47:34 +00001672 return((int) MagickTrue);
1673}
1674#endif
1675
1676static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1677{
1678 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1679 mng_info->exists[i] && !mng_info->frozen[i])
1680 {
1681#ifdef MNG_OBJECT_BUFFERS
1682 if (mng_info->ob[i] != (MngBuffer *) NULL)
1683 {
1684 if (mng_info->ob[i]->reference_count > 0)
1685 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001686
cristy3ed852e2009-09-05 21:47:34 +00001687 if (mng_info->ob[i]->reference_count == 0)
1688 {
1689 if (mng_info->ob[i]->image != (Image *) NULL)
1690 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001691
cristy3ed852e2009-09-05 21:47:34 +00001692 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1693 }
1694 }
1695 mng_info->ob[i]=(MngBuffer *) NULL;
1696#endif
1697 mng_info->exists[i]=MagickFalse;
1698 mng_info->invisible[i]=MagickFalse;
1699 mng_info->viewable[i]=MagickFalse;
1700 mng_info->frozen[i]=MagickFalse;
1701 mng_info->x_off[i]=0;
1702 mng_info->y_off[i]=0;
1703 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001704 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001705 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001706 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001707 }
1708}
1709
glennrp21f0e622011-01-07 16:20:57 +00001710static void MngInfoFreeStruct(MngInfo *mng_info,
1711 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001712{
glennrp21f0e622011-01-07 16:20:57 +00001713 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001714 {
cristybb503372010-05-27 20:51:26 +00001715 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001716 i;
1717
1718 for (i=1; i < MNG_MAX_OBJECTS; i++)
1719 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001720
cristy3ed852e2009-09-05 21:47:34 +00001721 if (mng_info->global_plte != (png_colorp) NULL)
1722 mng_info->global_plte=(png_colorp)
1723 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001724
cristy3ed852e2009-09-05 21:47:34 +00001725 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1726 *have_mng_structure=MagickFalse;
1727 }
1728}
1729
1730static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1731{
1732 MngBox
1733 box;
1734
1735 box=box1;
1736 if (box.left < box2.left)
1737 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001738
cristy3ed852e2009-09-05 21:47:34 +00001739 if (box.top < box2.top)
1740 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001741
cristy3ed852e2009-09-05 21:47:34 +00001742 if (box.right > box2.right)
1743 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001744
cristy3ed852e2009-09-05 21:47:34 +00001745 if (box.bottom > box2.bottom)
1746 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001747
cristy3ed852e2009-09-05 21:47:34 +00001748 return box;
1749}
1750
1751static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1752{
1753 MngBox
1754 box;
1755
1756 /*
1757 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1758 */
cristybb503372010-05-27 20:51:26 +00001759 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1760 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1761 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1762 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001763 if (delta_type != 0)
1764 {
1765 box.left+=previous_box.left;
1766 box.right+=previous_box.right;
1767 box.top+=previous_box.top;
1768 box.bottom+=previous_box.bottom;
1769 }
glennrp0fe50b42010-11-16 03:52:51 +00001770
cristy3ed852e2009-09-05 21:47:34 +00001771 return(box);
1772}
1773
1774static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1775 unsigned char *p)
1776{
1777 MngPair
1778 pair;
1779 /*
cristybb503372010-05-27 20:51:26 +00001780 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001781 */
cristy8182b072010-05-30 20:10:53 +00001782 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1783 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001784
cristy3ed852e2009-09-05 21:47:34 +00001785 if (delta_type != 0)
1786 {
1787 pair.a+=previous_pair.a;
1788 pair.b+=previous_pair.b;
1789 }
glennrp0fe50b42010-11-16 03:52:51 +00001790
cristy3ed852e2009-09-05 21:47:34 +00001791 return(pair);
1792}
1793
cristy8182b072010-05-30 20:10:53 +00001794static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001795{
cristy8182b072010-05-30 20:10:53 +00001796 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001797}
1798
cristy16ea1392012-03-21 20:38:41 +00001799typedef struct _PNGErrorInfo
cristyc82a27b2011-10-21 01:07:16 +00001800{
cristyc82a27b2011-10-21 01:07:16 +00001801 Image
1802 *image;
1803
cristy16ea1392012-03-21 20:38:41 +00001804 ExceptionInfo
1805 *exception;
1806} PNGErrorInfo;
cristyc82a27b2011-10-21 01:07:16 +00001807
cristy16ea1392012-03-21 20:38:41 +00001808static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1809{
1810 ExceptionInfo
1811 *exception;
cristyc82a27b2011-10-21 01:07:16 +00001812
cristy16ea1392012-03-21 20:38:41 +00001813 Image
1814 *image;
1815
1816 PNGErrorInfo
1817 *error_info;
1818
1819 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1820 image=error_info->image;
1821 exception=error_info->exception;
1822
1823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1824 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1825
1826 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1827 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001828
glennrpe4017e32011-01-08 17:16:09 +00001829#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001830 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1831 * are building with libpng-1.4.x and can be ignored.
1832 */
cristy3ed852e2009-09-05 21:47:34 +00001833 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001834#else
1835 png_longjmp(ping,1);
1836#endif
cristy3ed852e2009-09-05 21:47:34 +00001837}
1838
glennrpcf002022011-01-30 02:38:15 +00001839static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001840{
cristy16ea1392012-03-21 20:38:41 +00001841 ExceptionInfo
1842 *exception;
1843
cristy3ed852e2009-09-05 21:47:34 +00001844 Image
1845 *image;
1846
cristy16ea1392012-03-21 20:38:41 +00001847 PNGErrorInfo
1848 *error_info;
1849
cristy3ed852e2009-09-05 21:47:34 +00001850 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1851 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001852
cristy16ea1392012-03-21 20:38:41 +00001853 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1854 image=error_info->image;
1855 exception=error_info->exception;
1856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1857 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001858
cristy16ea1392012-03-21 20:38:41 +00001859 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001860 message,"`%s'",image->filename);
1861}
1862
1863#ifdef PNG_USER_MEM_SUPPORTED
glennrp943b7d32013-04-21 00:40:38 +00001864#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +00001865static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1866#else
1867static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1868#endif
cristy3ed852e2009-09-05 21:47:34 +00001869{
cristydf0d90e2011-12-12 01:03:55 +00001870 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001871 return((png_voidp) AcquireMagickMemory((size_t) size));
cristy3ed852e2009-09-05 21:47:34 +00001872}
1873
1874/*
1875 Free a pointer. It is removed from the list at the same time.
1876*/
glennrpcf002022011-01-30 02:38:15 +00001877static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001878{
glennrp3bd393f2011-12-21 18:54:53 +00001879 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001880 ptr=RelinquishMagickMemory(ptr);
1881 return((png_free_ptr) NULL);
1882}
1883#endif
1884
1885#if defined(__cplusplus) || defined(c_plusplus)
1886}
1887#endif
1888
1889static int
glennrpedaa0382012-04-12 14:16:21 +00001890Magick_png_read_raw_profile(png_struct *ping,Image *image,
1891 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001892{
cristybb503372010-05-27 20:51:26 +00001893 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001894 i;
1895
1896 register unsigned char
1897 *dp;
1898
1899 register png_charp
1900 sp;
1901
1902 png_uint_32
1903 length,
1904 nibbles;
1905
1906 StringInfo
1907 *profile;
1908
glennrp0c3e06b2010-11-19 13:45:02 +00001909 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001910 unhex[103]={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,0, 0,0,0,0,0,0,0,0,0,0,
1912 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1913 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1914 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1915 13,14,15};
1916
1917 sp=text[ii].text+1;
1918 /* look for newline */
1919 while (*sp != '\n')
1920 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001921
cristy3ed852e2009-09-05 21:47:34 +00001922 /* look for length */
1923 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1924 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001925
cristyf2f27272009-12-17 14:48:46 +00001926 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001927
glennrp97f90e22011-02-22 05:47:58 +00001928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1929 " length: %lu",(unsigned long) length);
1930
cristy3ed852e2009-09-05 21:47:34 +00001931 while (*sp != ' ' && *sp != '\n')
1932 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001933
cristy3ed852e2009-09-05 21:47:34 +00001934 /* allocate space */
1935 if (length == 0)
1936 {
glennrpedaa0382012-04-12 14:16:21 +00001937 png_warning(ping,"invalid profile length");
cristy3ed852e2009-09-05 21:47:34 +00001938 return(MagickFalse);
1939 }
glennrp0fe50b42010-11-16 03:52:51 +00001940
cristy8723e4b2011-09-01 13:11:19 +00001941 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001942
cristy3ed852e2009-09-05 21:47:34 +00001943 if (profile == (StringInfo *) NULL)
1944 {
glennrpedaa0382012-04-12 14:16:21 +00001945 png_warning(ping, "unable to copy profile");
cristy3ed852e2009-09-05 21:47:34 +00001946 return(MagickFalse);
1947 }
glennrp0fe50b42010-11-16 03:52:51 +00001948
cristy3ed852e2009-09-05 21:47:34 +00001949 /* copy profile, skipping white space and column 1 "=" signs */
1950 dp=GetStringInfoDatum(profile);
1951 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001952
cristybb503372010-05-27 20:51:26 +00001953 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001954 {
1955 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1956 {
1957 if (*sp == '\0')
1958 {
glennrpedaa0382012-04-12 14:16:21 +00001959 png_warning(ping, "ran out of profile data");
cristy3ed852e2009-09-05 21:47:34 +00001960 profile=DestroyStringInfo(profile);
1961 return(MagickFalse);
1962 }
1963 sp++;
1964 }
glennrp0fe50b42010-11-16 03:52:51 +00001965
cristy3ed852e2009-09-05 21:47:34 +00001966 if (i%2 == 0)
1967 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001968
cristy3ed852e2009-09-05 21:47:34 +00001969 else
1970 (*dp++)+=unhex[(int) *sp++];
1971 }
1972 /*
1973 We have already read "Raw profile type.
1974 */
cristy16ea1392012-03-21 20:38:41 +00001975 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001976 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001977
cristy3ed852e2009-09-05 21:47:34 +00001978 if (image_info->verbose)
1979 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001980
cristy3ed852e2009-09-05 21:47:34 +00001981 return MagickTrue;
1982}
1983
1984#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1985static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1986{
1987 Image
1988 *image;
1989
1990
1991 /* The unknown chunk structure contains the chunk data:
1992 png_byte name[5];
1993 png_byte *data;
1994 png_size_t size;
1995
1996 Note that libpng has already taken care of the CRC handling.
1997 */
1998
glennrp2ad70152013-03-03 00:44:57 +00001999 LogMagickEvent(CoderEvent,GetMagickModule(),
2000 " read_vpag_chunk: found %c%c%c%c chunk",
2001 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
cristy3ed852e2009-09-05 21:47:34 +00002002
2003 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
2004 chunk->name[2] != 65 ||chunk-> name[3] != 103)
2005 return(0); /* Did not recognize */
2006
2007 /* recognized vpAg */
2008
2009 if (chunk->size != 9)
2010 return(-1); /* Error return */
2011
2012 if (chunk->data[8] != 0)
2013 return(0); /* ImageMagick requires pixel units */
2014
2015 image=(Image *) png_get_user_chunk_ptr(ping);
2016
cristybb503372010-05-27 20:51:26 +00002017 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00002018 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00002019
cristybb503372010-05-27 20:51:26 +00002020 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00002021 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2022
2023 /* Return one of the following: */
2024 /* return(-n); chunk had an error */
2025 /* return(0); did not recognize */
2026 /* return(n); success */
2027
2028 return(1);
2029
2030}
2031#endif
2032
2033/*
2034%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2035% %
2036% %
2037% %
2038% R e a d O n e P N G I m a g e %
2039% %
2040% %
2041% %
2042%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2043%
2044% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2045% (minus the 8-byte signature) and returns it. It allocates the memory
2046% necessary for the new Image structure and returns a pointer to the new
2047% image.
2048%
2049% The format of the ReadOnePNGImage method is:
2050%
2051% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2052% ExceptionInfo *exception)
2053%
2054% A description of each parameter follows:
2055%
2056% o mng_info: Specifies a pointer to a MngInfo structure.
2057%
2058% o image_info: the image info.
2059%
2060% o exception: return any errors or warnings in this structure.
2061%
2062*/
2063static Image *ReadOnePNGImage(MngInfo *mng_info,
2064 const ImageInfo *image_info, ExceptionInfo *exception)
2065{
2066 /* Read one PNG image */
2067
glennrpcc95c3f2011-04-18 16:46:48 +00002068 /* To do: Read the tIME chunk into the date:modify property */
2069 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2070
cristy3ed852e2009-09-05 21:47:34 +00002071 Image
2072 *image;
2073
glennrpd0cae252013-03-15 22:30:41 +00002074 char
glennrpec0ddbc2013-03-16 12:40:12 +00002075 im_vers[32],
2076 libpng_runv[32],
2077 libpng_vers[32],
2078 zlib_runv[32],
2079 zlib_vers[32];
glennrpd0cae252013-03-15 22:30:41 +00002080
cristy3ed852e2009-09-05 21:47:34 +00002081 int
glennrp98b83d42012-07-23 02:50:31 +00002082 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
glennrpcb395ac2011-03-30 19:50:23 +00002083 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002084 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002085 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002086 num_passes,
glennrp913f9612012-06-27 17:50:00 +00002087 number_colors,
glennrpfaa852b2010-03-30 12:17:00 +00002088 pass,
2089 ping_bit_depth,
2090 ping_color_type,
glennrpfcf06162012-11-05 14:57:08 +00002091 ping_file_depth,
glennrpfaa852b2010-03-30 12:17:00 +00002092 ping_interlace_method,
2093 ping_compression_method,
2094 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002095 ping_num_trans,
2096 unit_type;
2097
2098 double
2099 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002100
2101 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002102 logging,
glennrp98b83d42012-07-23 02:50:31 +00002103 ping_found_cHRM,
2104 ping_found_gAMA,
2105 ping_found_iCCP,
2106 ping_found_sRGB,
glennrp3d627862013-02-26 00:19:34 +00002107 ping_found_sRGB_cHRM,
glennrpecab7d72013-05-14 22:50:32 +00002108 ping_preserve_iCCP,
cristy3ed852e2009-09-05 21:47:34 +00002109 status;
2110
cristy09973322013-06-30 21:06:30 +00002111 MemoryInfo
2112 *volatile pixel_info;
2113
cristy16ea1392012-03-21 20:38:41 +00002114 PixelInfo
2115 transparent_color;
2116
2117 PNGErrorInfo
2118 error_info;
2119
glennrpfaa852b2010-03-30 12:17:00 +00002120 png_bytep
2121 ping_trans_alpha;
2122
2123 png_color_16p
2124 ping_background,
2125 ping_trans_color;
2126
cristy3ed852e2009-09-05 21:47:34 +00002127 png_info
2128 *end_info,
2129 *ping_info;
2130
2131 png_struct
2132 *ping;
2133
2134 png_textp
2135 text;
2136
glennrpfaa852b2010-03-30 12:17:00 +00002137 png_uint_32
2138 ping_height,
2139 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002140 x_resolution,
2141 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002142
cristy16ea1392012-03-21 20:38:41 +00002143 QuantumInfo
2144 *quantum_info;
2145
cristybb503372010-05-27 20:51:26 +00002146 ssize_t
cristy756ae432011-11-19 02:18:25 +00002147 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002148 y;
2149
2150 register unsigned char
2151 *p;
2152
cristybb503372010-05-27 20:51:26 +00002153 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002154 i,
2155 x;
2156
cristy16ea1392012-03-21 20:38:41 +00002157 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002158 *q;
2159
2160 size_t
glennrp39992b42010-11-14 00:03:43 +00002161 length,
cristy3ed852e2009-09-05 21:47:34 +00002162 row_offset;
2163
cristyeb3b22a2011-03-31 20:16:11 +00002164 ssize_t
2165 j;
2166
cristy75fc68f2012-10-08 16:26:00 +00002167 unsigned char
cristy09973322013-06-30 21:06:30 +00002168 *ping_pixels;
cristy55b78b52012-10-08 14:01:27 +00002169
glennrp629960f2012-05-29 19:13:52 +00002170#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002171 png_byte unused_chunks[]=
2172 {
2173 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2174 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2175 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2176 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2177 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2178 116, 73, 77, 69, (png_byte) '\0', /* tIME */
glennrp629960f2012-05-29 19:13:52 +00002179#ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2180 /* ignore the APNG chunks */
2181 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2182 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2183 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2184#endif
cristy3ed852e2009-09-05 21:47:34 +00002185 };
2186#endif
2187
glennrpd0cae252013-03-15 22:30:41 +00002188 /* Define these outside of the following "if logging()" block so they will
2189 * show in debuggers.
2190 */
2191 *im_vers='\0';
2192 (void) ConcatenateMagickString(im_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002193 MagickLibVersionText,32);
glennrpd0cae252013-03-15 22:30:41 +00002194 (void) ConcatenateMagickString(im_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002195 MagickLibAddendum,32);
2196
glennrpd0cae252013-03-15 22:30:41 +00002197 *libpng_vers='\0';
2198 (void) ConcatenateMagickString(libpng_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002199 PNG_LIBPNG_VER_STRING,32);
2200 *libpng_runv='\0';
2201 (void) ConcatenateMagickString(libpng_runv,
2202 png_get_libpng_ver(NULL),32);
2203
glennrpd0cae252013-03-15 22:30:41 +00002204 *zlib_vers='\0';
2205 (void) ConcatenateMagickString(zlib_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002206 ZLIB_VERSION,32);
2207 *zlib_runv='\0';
2208 (void) ConcatenateMagickString(zlib_runv,
2209 zlib_version,32);
2210
glennrp2dd19062014-07-28 00:22:24 +00002211 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2212 " Enter ReadOnePNGImage()\n"
2213 " IM version = %s\n"
2214 " Libpng version = %s",
2215 im_vers, libpng_vers);
2216
2217 if (logging != MagickFalse)
2218 {
2219 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
glennrpd0cae252013-03-15 22:30:41 +00002220 {
glennrp2dd19062014-07-28 00:22:24 +00002221 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2222 libpng_runv);
glennrpd0cae252013-03-15 22:30:41 +00002223 }
glennrp2dd19062014-07-28 00:22:24 +00002224 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
2225 zlib_vers);
2226 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2227 {
2228 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2229 zlib_runv);
2230 }
2231 }
glennrpd0cae252013-03-15 22:30:41 +00002232
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(),
glennrp2dd19062014-07-28 00:22:24 +00002257 " Before reading:\n"
2258 " image->alpha_trait=%d"
2259 " image->rendering_intent=%d\n"
2260 " image->colorspace=%d\n"
2261 " image->gamma=%f",
2262 (int) image->alpha_trait, (int) image->rendering_intent,
2263 (int) image->colorspace, image->gamma);
glennrp98b83d42012-07-23 02:50:31 +00002264 }
2265 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2266
glennrp0e319732011-01-25 21:53:13 +00002267 /* Set to an out-of-range color unless tRNS chunk is present */
2268 transparent_color.red=65537;
2269 transparent_color.green=65537;
2270 transparent_color.blue=65537;
cristy16ea1392012-03-21 20:38:41 +00002271 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002272
glennrp913f9612012-06-27 17:50:00 +00002273 number_colors=0;
glennrpcb395ac2011-03-30 19:50:23 +00002274 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002275 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002276 num_raw_profiles = 0;
2277
glennrp98b83d42012-07-23 02:50:31 +00002278 ping_found_cHRM = MagickFalse;
2279 ping_found_gAMA = MagickFalse;
2280 ping_found_iCCP = MagickFalse;
2281 ping_found_sRGB = MagickFalse;
glennrp4b917592013-06-20 19:53:59 +00002282 ping_found_sRGB_cHRM = MagickFalse;
glennrpecab7d72013-05-14 22:50:32 +00002283 ping_preserve_iCCP = MagickFalse;
2284
glennrp98b83d42012-07-23 02:50:31 +00002285
cristy3ed852e2009-09-05 21:47:34 +00002286 /*
2287 Allocate the PNG structures
2288 */
2289#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00002290 error_info.image=image;
2291 error_info.exception=exception;
2292 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002293 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2294 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002295#else
cristy16ea1392012-03-21 20:38:41 +00002296 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002297 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002298#endif
2299 if (ping == (png_struct *) NULL)
2300 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002301
cristy3ed852e2009-09-05 21:47:34 +00002302 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002303
cristy3ed852e2009-09-05 21:47:34 +00002304 if (ping_info == (png_info *) NULL)
2305 {
2306 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2307 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2308 }
glennrp0fe50b42010-11-16 03:52:51 +00002309
cristy3ed852e2009-09-05 21:47:34 +00002310 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002311
cristy3ed852e2009-09-05 21:47:34 +00002312 if (end_info == (png_info *) NULL)
2313 {
2314 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2315 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2316 }
glennrp0fe50b42010-11-16 03:52:51 +00002317
cristy09973322013-06-30 21:06:30 +00002318 pixel_info=(MemoryInfo *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002319
glennrpfaa852b2010-03-30 12:17:00 +00002320 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002321 {
2322 /*
2323 PNG image is corrupt.
2324 */
2325 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002326
glennrp868fff32014-03-16 22:09:06 +00002327#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002328 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002329#endif
glennrpedaa0382012-04-12 14:16:21 +00002330
cristy09973322013-06-30 21:06:30 +00002331 if (pixel_info != (MemoryInfo *) NULL)
2332 pixel_info=RelinquishVirtualMemory(pixel_info);
glennrpedaa0382012-04-12 14:16:21 +00002333
cristy3ed852e2009-09-05 21:47:34 +00002334 if (logging != MagickFalse)
2335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2336 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002337
cristy3ed852e2009-09-05 21:47:34 +00002338 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002339 {
cristy16ea1392012-03-21 20:38:41 +00002340 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002341 image->columns=0;
2342 }
glennrp0fe50b42010-11-16 03:52:51 +00002343
cristy3ed852e2009-09-05 21:47:34 +00002344 return(GetFirstImageInList(image));
2345 }
glennrpedaa0382012-04-12 14:16:21 +00002346
2347 /* { For navigation to end of SETJMP-protected block. Within this
2348 * block, use png_error() instead of Throwing an Exception, to ensure
2349 * that libpng is able to clean up, and that the semaphore is unlocked.
2350 */
2351
glennrp868fff32014-03-16 22:09:06 +00002352#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpedaa0382012-04-12 14:16:21 +00002353 LockSemaphoreInfo(ping_semaphore);
2354#endif
2355
glennrp943b7d32013-04-21 00:40:38 +00002356#ifdef PNG_BENIGN_ERRORS_SUPPORTED
glennrpa3a5f952013-04-17 12:58:15 +00002357 /* Allow benign errors */
2358 png_set_benign_errors(ping, 1);
2359#endif
2360
cristy3ed852e2009-09-05 21:47:34 +00002361 /*
2362 Prepare PNG for reading.
2363 */
glennrpfaa852b2010-03-30 12:17:00 +00002364
cristy3ed852e2009-09-05 21:47:34 +00002365 mng_info->image_found++;
2366 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002367
cristy3ed852e2009-09-05 21:47:34 +00002368 if (LocaleCompare(image_info->magick,"MNG") == 0)
2369 {
2370#if defined(PNG_MNG_FEATURES_SUPPORTED)
2371 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2372 png_set_read_fn(ping,image,png_get_data);
2373#else
2374#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2375 png_permit_empty_plte(ping,MagickTrue);
2376 png_set_read_fn(ping,image,png_get_data);
2377#else
2378 mng_info->image=image;
2379 mng_info->bytes_in_read_buffer=0;
2380 mng_info->found_empty_plte=MagickFalse;
2381 mng_info->have_saved_bkgd_index=MagickFalse;
2382 png_set_read_fn(ping,mng_info,mng_get_data);
2383#endif
2384#endif
2385 }
glennrp0fe50b42010-11-16 03:52:51 +00002386
cristy3ed852e2009-09-05 21:47:34 +00002387 else
2388 png_set_read_fn(ping,image,png_get_data);
2389
glennrpe604a752014-06-28 02:24:30 +00002390 {
2391 const char
2392 *value;
2393
2394 value=GetImageOption(image_info,"profile:skip");
2395
2396 if (IsOptionMember("ICC",value) == MagickFalse)
2397 {
2398
2399 value=GetImageOption(image_info,"png:preserve-iCCP");
2400
2401 if (value == NULL)
2402 value=GetImageArtifact(image,"png:preserve-iCCP");
2403
2404 if (value != NULL)
2405 ping_preserve_iCCP=MagickTrue;
glennrp201f0c92014-06-28 16:13:49 +00002406
2407#if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2408 /* Don't let libpng check for ICC/sRGB profile because we're going
2409 * to do that anyway. This feature was added at libpng-1.6.12.
glennrpcf45b202014-06-29 16:46:17 +00002410 * If logging, go ahead and check and issue a warning as appropriate.
glennrp201f0c92014-06-28 16:13:49 +00002411 */
glennrpcf45b202014-06-29 16:46:17 +00002412 if (logging == MagickFalse)
2413 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
glennrp201f0c92014-06-28 16:13:49 +00002414#endif
glennrpe604a752014-06-28 02:24:30 +00002415 }
2416#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2417 else
2418 {
2419 png_set_keep_unknown_chunks(ping, 1, mng_iCCP, 1);
2420 }
2421#endif
2422 }
cristy3ed852e2009-09-05 21:47:34 +00002423#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2424 /* Ignore unused chunks and all unknown chunks except for vpAg */
glennrp2ad70152013-03-03 00:44:57 +00002425#if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2426 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2427#else
cristy3ed852e2009-09-05 21:47:34 +00002428 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
glennrp2ad70152013-03-03 00:44:57 +00002429#endif
cristy3ed852e2009-09-05 21:47:34 +00002430 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2431 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2432 (int)sizeof(unused_chunks)/5);
2433 /* Callback for other unknown chunks */
2434 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2435#endif
2436
glennrp2feb1412013-01-22 15:02:16 +00002437#ifdef PNG_SET_USER_LIMITS_SUPPORTED
glennrp09cd9622013-01-22 15:17:38 +00002438# if (PNG_LIBPNG_VER >= 10400)
glennrp2feb1412013-01-22 15:02:16 +00002439 /* Limit the size of the chunk storage cache used for sPLT, text,
glennrp68736192013-01-24 06:32:08 +00002440 * and unknown chunks.
glennrp2feb1412013-01-22 15:02:16 +00002441 */
glennrp68736192013-01-24 06:32:08 +00002442 png_set_chunk_cache_max(ping, 32767);
glennrp09cd9622013-01-22 15:17:38 +00002443# endif
glennrp2feb1412013-01-22 15:02:16 +00002444#endif
2445
glennrp9bf97b62012-06-06 21:03:14 +00002446#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2447 /* Disable new libpng-1.5.10 feature */
2448 png_set_check_for_invalid_index (ping, 0);
2449#endif
2450
glennrp991e92a2010-01-28 03:09:00 +00002451#if (PNG_LIBPNG_VER < 10400)
2452# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2453 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002454 /* Disable thread-unsafe features of pnggccrd */
2455 if (png_access_version_number() >= 10200)
2456 {
2457 png_uint_32 mmx_disable_mask=0;
2458 png_uint_32 asm_flags;
2459
2460 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2461 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2462 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2463 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2464 asm_flags=png_get_asm_flags(ping);
2465 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2466 }
glennrp991e92a2010-01-28 03:09:00 +00002467# endif
cristy3ed852e2009-09-05 21:47:34 +00002468#endif
2469
2470 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002471
2472 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2473 &ping_bit_depth,&ping_color_type,
2474 &ping_interlace_method,&ping_compression_method,
2475 &ping_filter_method);
2476
glennrpfcf06162012-11-05 14:57:08 +00002477 ping_file_depth = ping_bit_depth;
2478
glennrp0d767722014-01-17 16:08:31 +00002479 /* Swap bytes if requested */
2480 if (ping_file_depth == 16)
2481 {
2482 const char
glennrpa3d5f0e2014-01-30 18:58:51 +00002483 *value;
2484
2485 value=GetImageOption(image_info,"png:swap-bytes");
2486
2487 if (value == NULL)
2488 value=GetImageArtifact(image,"png:swap-bytes");
2489
2490 if (value != NULL)
2491 png_set_swap(ping);
glennrp0d767722014-01-17 16:08:31 +00002492 }
2493
glennrp5830fbc2013-01-27 06:11:45 +00002494 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2495 {
2496 char
2497 msg[MaxTextExtent];
2498
2499 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
glennrp3398b5b2013-05-03 23:10:31 +00002500 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
glennrp5830fbc2013-01-27 06:11:45 +00002501
2502 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
glennrp3398b5b2013-05-03 23:10:31 +00002503 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
glennrp5830fbc2013-01-27 06:11:45 +00002504 }
2505
glennrpfaa852b2010-03-30 12:17:00 +00002506 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2507 &ping_trans_color);
2508
2509 (void) png_get_bKGD(ping, ping_info, &ping_background);
2510
2511 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002512 {
glennrpfcf06162012-11-05 14:57:08 +00002513 png_set_packing(ping);
2514 ping_bit_depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00002515 }
glennrpfaa852b2010-03-30 12:17:00 +00002516
2517 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002518 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002519 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
glennrp98b83d42012-07-23 02:50:31 +00002520
cristy176b29a2012-06-21 13:35:15 +00002521 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2522 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2523 {
2524 image->rendering_intent=UndefinedIntent;
glennrp98b83d42012-07-23 02:50:31 +00002525 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
cristy176b29a2012-06-21 13:35:15 +00002526 image->gamma=1.000;
2527 (void) ResetMagickMemory(&image->chromaticity,0,
2528 sizeof(image->chromaticity));
2529 }
glennrp98b83d42012-07-23 02:50:31 +00002530
cristy3ed852e2009-09-05 21:47:34 +00002531 if (logging != MagickFalse)
2532 {
2533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00002534 " PNG width: %.20g, height: %.20g\n"
2535 " PNG color_type: %d, bit_depth: %d\n"
2536 " PNG compression_method: %d\n"
cristy3ed852e2009-09-05 21:47:34 +00002537 " PNG interlace_method: %d, filter_method: %d",
glennrp2dd19062014-07-28 00:22:24 +00002538 (double) ping_width, (double) ping_height,
2539 ping_color_type, ping_bit_depth,
2540 ping_compression_method,
glennrpfaa852b2010-03-30 12:17:00 +00002541 ping_interlace_method,ping_filter_method);
glennrp2dd19062014-07-28 00:22:24 +00002542
cristy3ed852e2009-09-05 21:47:34 +00002543 }
2544
glennrpecab7d72013-05-14 22:50:32 +00002545 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2546 {
2547 ping_found_iCCP=MagickTrue;
2548 if (logging != MagickFalse)
2549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2550 " Found PNG iCCP chunk.");
2551 }
2552
glennrp98b83d42012-07-23 02:50:31 +00002553 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2554 {
2555 ping_found_gAMA=MagickTrue;
2556 if (logging != MagickFalse)
2557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2558 " Found PNG gAMA chunk.");
2559 }
2560
2561 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2562 {
2563 ping_found_cHRM=MagickTrue;
2564 if (logging != MagickFalse)
2565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2566 " Found PNG cHRM chunk.");
2567 }
2568
glennrpecab7d72013-05-14 22:50:32 +00002569 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2570 PNG_INFO_sRGB))
glennrp98b83d42012-07-23 02:50:31 +00002571 {
2572 ping_found_sRGB=MagickTrue;
2573 if (logging != MagickFalse)
2574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2575 " Found PNG sRGB chunk.");
2576 }
2577
glennrpfaa852b2010-03-30 12:17:00 +00002578#ifdef PNG_READ_iCCP_SUPPORTED
glennrpe604a752014-06-28 02:24:30 +00002579 if (ping_found_iCCP !=MagickTrue &&
glennrpecab7d72013-05-14 22:50:32 +00002580 ping_found_sRGB != MagickTrue &&
2581 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2582 {
2583 ping_found_iCCP=MagickTrue;
2584 if (logging != MagickFalse)
2585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2586 " Found PNG iCCP chunk.");
2587 }
2588
glennrpfaa852b2010-03-30 12:17:00 +00002589 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002590 {
2591 int
2592 compression;
2593
glennrpe4017e32011-01-08 17:16:09 +00002594#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002595 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002596 info;
2597#else
2598 png_bytep
2599 info;
2600#endif
2601
2602 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002603 name;
2604
2605 png_uint_32
2606 profile_length;
2607
2608 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2609 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002610
cristy3ed852e2009-09-05 21:47:34 +00002611 if (profile_length != 0)
2612 {
2613 StringInfo
2614 *profile;
2615
2616 if (logging != MagickFalse)
2617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2618 " Reading PNG iCCP chunk.");
glennrpecab7d72013-05-14 22:50:32 +00002619
cristye8f8f382011-09-01 13:32:37 +00002620 profile=BlobToStringInfo(info,profile_length);
glennrpecab7d72013-05-14 22:50:32 +00002621
cristye8f8f382011-09-01 13:32:37 +00002622 if (profile == (StringInfo *) NULL)
glennrpecab7d72013-05-14 22:50:32 +00002623 {
2624 png_warning(ping, "ICC profile is NULL");
2625 profile=DestroyStringInfo(profile);
2626 }
glennrpedaa0382012-04-12 14:16:21 +00002627 else
glennrpecab7d72013-05-14 22:50:32 +00002628 {
2629 if (ping_preserve_iCCP == MagickFalse)
glennrpedaa0382012-04-12 14:16:21 +00002630 {
glennrpecab7d72013-05-14 22:50:32 +00002631 int
2632 icheck,
2633 got_crc=0;
2634
2635
2636 png_uint_32
2637 length,
2638 profile_crc=0;
2639
2640 unsigned char
2641 *data;
2642
2643 length=(png_uint_32) GetStringInfoLength(profile);
2644
2645 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2646 {
2647 if (length == sRGB_info[icheck].len)
2648 {
2649 if (got_crc == 0)
2650 {
2651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2652 " Got a %lu-byte ICC profile (potentially sRGB)",
2653 (unsigned long) length);
2654
2655 data=GetStringInfoDatum(profile);
2656 profile_crc=crc32(0,data,length);
2657
2658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2659 " with crc=%8x",(unsigned int) profile_crc);
2660 got_crc++;
2661 }
2662
2663 if (profile_crc == sRGB_info[icheck].crc)
2664 {
2665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2666 " It is sRGB with rendering intent = %s",
2667 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2668 sRGB_info[icheck].intent));
2669 if (image->rendering_intent==UndefinedIntent)
2670 {
2671 image->rendering_intent=
2672 Magick_RenderingIntent_from_PNG_RenderingIntent(
2673 sRGB_info[icheck].intent);
2674 }
2675 break;
2676 }
2677 }
2678 }
2679 if (sRGB_info[icheck].len == 0)
2680 {
2681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2682 " Got a %lu-byte ICC profile not recognized as sRGB",
2683 (unsigned long) length);
2684 (void) SetImageProfile(image,"icc",profile,exception);
2685 }
glennrpedaa0382012-04-12 14:16:21 +00002686 }
glennrpecab7d72013-05-14 22:50:32 +00002687 else /* Preserve-iCCP */
2688 {
2689 (void) SetImageProfile(image,"icc",profile,exception);
2690 }
2691
2692 profile=DestroyStringInfo(profile);
2693 }
cristy3ed852e2009-09-05 21:47:34 +00002694 }
2695 }
2696#endif
glennrpecab7d72013-05-14 22:50:32 +00002697
cristy3ed852e2009-09-05 21:47:34 +00002698#if defined(PNG_READ_sRGB_SUPPORTED)
2699 {
glennrpecab7d72013-05-14 22:50:32 +00002700 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2701 PNG_INFO_sRGB))
2702 {
2703 if (png_get_sRGB(ping,ping_info,&intent))
cristy2ea7a8e2012-02-08 01:04:50 +00002704 {
glennrpecab7d72013-05-14 22:50:32 +00002705 if (image->rendering_intent == UndefinedIntent)
2706 image->rendering_intent=
2707 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002708
cristy3ed852e2009-09-05 21:47:34 +00002709 if (logging != MagickFalse)
2710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002711 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002712 }
glennrpecab7d72013-05-14 22:50:32 +00002713 }
2714
2715 else if (mng_info->have_global_srgb)
2716 {
2717 if (image->rendering_intent == UndefinedIntent)
2718 image->rendering_intent=
2719 Magick_RenderingIntent_from_PNG_RenderingIntent
2720 (mng_info->global_srgb_intent);
2721 }
cristy3ed852e2009-09-05 21:47:34 +00002722 }
2723#endif
glennrpecab7d72013-05-14 22:50:32 +00002724
2725
cristy3ed852e2009-09-05 21:47:34 +00002726 {
glennrpfaa852b2010-03-30 12:17:00 +00002727 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2728 if (mng_info->have_global_gama)
2729 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002730
cristy3ed852e2009-09-05 21:47:34 +00002731 if (png_get_gAMA(ping,ping_info,&file_gamma))
2732 {
2733 image->gamma=(float) file_gamma;
2734 if (logging != MagickFalse)
2735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2736 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2737 }
2738 }
glennrp98b83d42012-07-23 02:50:31 +00002739
glennrpfaa852b2010-03-30 12:17:00 +00002740 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2741 {
2742 if (mng_info->have_global_chrm != MagickFalse)
2743 {
2744 (void) png_set_cHRM(ping,ping_info,
2745 mng_info->global_chrm.white_point.x,
2746 mng_info->global_chrm.white_point.y,
2747 mng_info->global_chrm.red_primary.x,
2748 mng_info->global_chrm.red_primary.y,
2749 mng_info->global_chrm.green_primary.x,
2750 mng_info->global_chrm.green_primary.y,
2751 mng_info->global_chrm.blue_primary.x,
2752 mng_info->global_chrm.blue_primary.y);
2753 }
2754 }
glennrp0fe50b42010-11-16 03:52:51 +00002755
glennrpfaa852b2010-03-30 12:17:00 +00002756 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002757 {
2758 (void) png_get_cHRM(ping,ping_info,
2759 &image->chromaticity.white_point.x,
2760 &image->chromaticity.white_point.y,
2761 &image->chromaticity.red_primary.x,
2762 &image->chromaticity.red_primary.y,
2763 &image->chromaticity.green_primary.x,
2764 &image->chromaticity.green_primary.y,
2765 &image->chromaticity.blue_primary.x,
2766 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002767
glennrpecab7d72013-05-14 22:50:32 +00002768 ping_found_cHRM=MagickTrue;
2769
glennrp3d627862013-02-26 00:19:34 +00002770 if (image->chromaticity.red_primary.x>0.6399f &&
2771 image->chromaticity.red_primary.x<0.6401f &&
2772 image->chromaticity.red_primary.y>0.3299f &&
2773 image->chromaticity.red_primary.y<0.3301f &&
2774 image->chromaticity.green_primary.x>0.2999f &&
2775 image->chromaticity.green_primary.x<0.3001f &&
2776 image->chromaticity.green_primary.y>0.5999f &&
2777 image->chromaticity.green_primary.y<0.6001f &&
2778 image->chromaticity.blue_primary.x>0.1499f &&
2779 image->chromaticity.blue_primary.x<0.1501f &&
2780 image->chromaticity.blue_primary.y>0.0599f &&
2781 image->chromaticity.blue_primary.y<0.0601f &&
2782 image->chromaticity.white_point.x>0.3126f &&
2783 image->chromaticity.white_point.x<0.3128f &&
2784 image->chromaticity.white_point.y>0.3289f &&
2785 image->chromaticity.white_point.y<0.3291f)
2786 ping_found_sRGB_cHRM=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002787 }
glennrp0fe50b42010-11-16 03:52:51 +00002788
glennrpe610a072010-08-05 17:08:46 +00002789 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002790 {
glennrp3d627862013-02-26 00:19:34 +00002791 if (ping_found_sRGB != MagickTrue &&
2792 (ping_found_gAMA != MagickTrue ||
glennrp84288232013-03-02 15:52:36 +00002793 (image->gamma > .45 && image->gamma < .46)) &&
glennrp3d627862013-02-26 00:19:34 +00002794 (ping_found_cHRM != MagickTrue ||
cristycd8b3312013-12-22 01:51:11 +00002795 ping_found_sRGB_cHRM != MagickFalse) &&
glennrp3d627862013-02-26 00:19:34 +00002796 ping_found_iCCP != MagickTrue)
glennrp5cf1bff2013-01-16 02:44:03 +00002797 {
2798 png_set_sRGB(ping,ping_info,
2799 Magick_RenderingIntent_to_PNG_RenderingIntent
2800 (image->rendering_intent));
glennrp5cf1bff2013-01-16 02:44:03 +00002801 file_gamma=1.000f/2.200f;
2802 ping_found_sRGB=MagickTrue;
glennrp84288232013-03-02 15:52:36 +00002803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp918b9dc2013-04-03 13:41:41 +00002804 " Setting sRGB as if in input");
glennrp5cf1bff2013-01-16 02:44:03 +00002805 }
cristy3ed852e2009-09-05 21:47:34 +00002806 }
glennrp5cf1bff2013-01-16 02:44:03 +00002807
cristy3ed852e2009-09-05 21:47:34 +00002808#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002809 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002810 {
cristy905ef802011-02-23 00:29:18 +00002811 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2812 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002813
cristy3ed852e2009-09-05 21:47:34 +00002814 if (logging != MagickFalse)
2815 if (image->page.x || image->page.y)
2816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002817 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2818 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002819 }
2820#endif
2821#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002822 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2823 {
2824 if (mng_info->have_global_phys)
2825 {
2826 png_set_pHYs(ping,ping_info,
2827 mng_info->global_x_pixels_per_unit,
2828 mng_info->global_y_pixels_per_unit,
2829 mng_info->global_phys_unit_type);
2830 }
2831 }
2832
cristy5c97f622014-01-15 12:36:09 +00002833 x_resolution=0;
2834 y_resolution=0;
2835 unit_type=0;
glennrpfaa852b2010-03-30 12:17:00 +00002836 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002837 {
cristy3ed852e2009-09-05 21:47:34 +00002838 /*
2839 Set image resolution.
2840 */
2841 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002842 &unit_type);
cristy16ea1392012-03-21 20:38:41 +00002843 image->resolution.x=(double) x_resolution;
2844 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002845
cristy3ed852e2009-09-05 21:47:34 +00002846 if (unit_type == PNG_RESOLUTION_METER)
2847 {
2848 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00002849 image->resolution.x=(double) x_resolution/100.0;
2850 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002851 }
glennrp0fe50b42010-11-16 03:52:51 +00002852
cristy3ed852e2009-09-05 21:47:34 +00002853 if (logging != MagickFalse)
2854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002855 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2856 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002857 }
cristy3ed852e2009-09-05 21:47:34 +00002858#endif
glennrp823b55c2011-03-14 18:46:46 +00002859
glennrpfaa852b2010-03-30 12:17:00 +00002860 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002861 {
cristy3ed852e2009-09-05 21:47:34 +00002862 png_colorp
2863 palette;
2864
2865 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002866
cristy3ed852e2009-09-05 21:47:34 +00002867 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002868 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002869 {
2870 if (mng_info->global_plte_length)
2871 {
2872 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2873 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002874
glennrpfaa852b2010-03-30 12:17:00 +00002875 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrpedaa0382012-04-12 14:16:21 +00002876 {
cristy3ed852e2009-09-05 21:47:34 +00002877 if (mng_info->global_trns_length)
2878 {
glennrpedaa0382012-04-12 14:16:21 +00002879 png_warning(ping,
2880 "global tRNS has more entries than global PLTE");
cristy3ed852e2009-09-05 21:47:34 +00002881 }
glennrpedaa0382012-04-12 14:16:21 +00002882 else
2883 {
2884 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2885 (int) mng_info->global_trns_length,NULL);
2886 }
2887 }
glennrpbfd9e612011-04-22 14:02:20 +00002888#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002889 if (
2890#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2891 mng_info->have_saved_bkgd_index ||
2892#endif
glennrpfaa852b2010-03-30 12:17:00 +00002893 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002894 {
2895 png_color_16
2896 background;
2897
2898#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2899 if (mng_info->have_saved_bkgd_index)
2900 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002901#endif
glennrpfaa852b2010-03-30 12:17:00 +00002902 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2903 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002904
cristy3ed852e2009-09-05 21:47:34 +00002905 background.red=(png_uint_16)
2906 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002907
cristy3ed852e2009-09-05 21:47:34 +00002908 background.green=(png_uint_16)
2909 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002910
cristy3ed852e2009-09-05 21:47:34 +00002911 background.blue=(png_uint_16)
2912 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002913
glennrpc6c391a2011-04-27 02:23:56 +00002914 background.gray=(png_uint_16)
2915 mng_info->global_plte[background.index].green;
2916
cristy3ed852e2009-09-05 21:47:34 +00002917 png_set_bKGD(ping,ping_info,&background);
2918 }
2919#endif
2920 }
2921 else
glennrpedaa0382012-04-12 14:16:21 +00002922 png_error(ping,"No global PLTE in file");
cristy3ed852e2009-09-05 21:47:34 +00002923 }
2924 }
2925
glennrpbfd9e612011-04-22 14:02:20 +00002926#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002927 if (mng_info->have_global_bkgd &&
2928 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002929 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002930
glennrpfaa852b2010-03-30 12:17:00 +00002931 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002932 {
glennrpbfd9e612011-04-22 14:02:20 +00002933 unsigned int
2934 bkgd_scale;
2935
glennrp2dd19062014-07-28 00:22:24 +00002936 /* Set image background color.
2937 * Scale background components to 16-bit, then scale
glennrpbfd9e612011-04-22 14:02:20 +00002938 * to quantum depth
2939 */
glennrp0fe50b42010-11-16 03:52:51 +00002940
glennrpbfd9e612011-04-22 14:02:20 +00002941 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002942
glennrpfcf06162012-11-05 14:57:08 +00002943 if (ping_file_depth == 1)
glennrpbfd9e612011-04-22 14:02:20 +00002944 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002945
glennrpfcf06162012-11-05 14:57:08 +00002946 else if (ping_file_depth == 2)
glennrpbfd9e612011-04-22 14:02:20 +00002947 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002948
glennrpfcf06162012-11-05 14:57:08 +00002949 else if (ping_file_depth == 4)
glennrpbfd9e612011-04-22 14:02:20 +00002950 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002951
glennrpfcf06162012-11-05 14:57:08 +00002952 if (ping_file_depth <= 8)
glennrpbfd9e612011-04-22 14:02:20 +00002953 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002954
glennrpbfd9e612011-04-22 14:02:20 +00002955 ping_background->red *= bkgd_scale;
2956 ping_background->green *= bkgd_scale;
2957 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002958
glennrpbfd9e612011-04-22 14:02:20 +00002959 if (logging != MagickFalse)
2960 {
glennrp2dd19062014-07-28 00:22:24 +00002961 if (logging != MagickFalse)
2962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2963 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d).\n"
2964 " bkgd_scale=%d. ping_background=(%d,%d,%d).",
2965 ping_background->red,ping_background->green,
2966 ping_background->blue,
2967 bkgd_scale,ping_background->red,
2968 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002969 }
glennrp2cbb4482010-06-02 04:37:24 +00002970
glennrpbfd9e612011-04-22 14:02:20 +00002971 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002972 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002973
glennrpbfd9e612011-04-22 14:02:20 +00002974 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002975 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002976
glennrpbfd9e612011-04-22 14:02:20 +00002977 image->background_color.blue=
2978 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002979
cristy16ea1392012-03-21 20:38:41 +00002980 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002981
glennrpbfd9e612011-04-22 14:02:20 +00002982 if (logging != MagickFalse)
2983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2984 " image->background_color=(%.20g,%.20g,%.20g).",
2985 (double) image->background_color.red,
2986 (double) image->background_color.green,
2987 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002988 }
glennrpbfd9e612011-04-22 14:02:20 +00002989#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002990
glennrpfaa852b2010-03-30 12:17:00 +00002991 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002992 {
2993 /*
glennrpa6a06632011-01-19 15:15:34 +00002994 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002995 */
2996 int
2997 max_sample;
2998
cristy35ef8242010-06-03 16:24:13 +00002999 size_t
3000 one=1;
3001
cristy3ed852e2009-09-05 21:47:34 +00003002 if (logging != MagickFalse)
3003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3004 " Reading PNG tRNS chunk.");
3005
glennrpfcf06162012-11-05 14:57:08 +00003006 max_sample = (int) ((one << ping_file_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00003007
glennrpfaa852b2010-03-30 12:17:00 +00003008 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3009 (int)ping_trans_color->gray > max_sample) ||
3010 (ping_color_type == PNG_COLOR_TYPE_RGB &&
3011 ((int)ping_trans_color->red > max_sample ||
3012 (int)ping_trans_color->green > max_sample ||
3013 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00003014 {
3015 if (logging != MagickFalse)
3016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3017 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00003018 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00003019 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy8a46d822012-08-28 23:32:39 +00003020 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00003021 }
3022 else
3023 {
glennrpa6a06632011-01-19 15:15:34 +00003024 int
3025 scale_to_short;
3026
glennrpfcf06162012-11-05 14:57:08 +00003027 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
glennrpa6a06632011-01-19 15:15:34 +00003028
3029 /* Scale transparent_color to short */
3030 transparent_color.red= scale_to_short*ping_trans_color->red;
3031 transparent_color.green= scale_to_short*ping_trans_color->green;
3032 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy16ea1392012-03-21 20:38:41 +00003033 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00003034
glennrpfaa852b2010-03-30 12:17:00 +00003035 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00003036 {
glennrp0f111982010-07-07 20:18:33 +00003037 if (logging != MagickFalse)
3038 {
3039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00003040 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
3041 (int) ping_trans_color->gray,(int) transparent_color.alpha);
glennrp0fe50b42010-11-16 03:52:51 +00003042
glennrp0f111982010-07-07 20:18:33 +00003043 }
cristy16ea1392012-03-21 20:38:41 +00003044 transparent_color.red=transparent_color.alpha;
3045 transparent_color.green=transparent_color.alpha;
3046 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00003047 }
3048 }
3049 }
3050#if defined(PNG_READ_sBIT_SUPPORTED)
3051 if (mng_info->have_global_sbit)
3052 {
glennrpfaa852b2010-03-30 12:17:00 +00003053 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00003054 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3055 }
3056#endif
3057 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00003058
cristy3ed852e2009-09-05 21:47:34 +00003059 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00003060
3061 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3062
cristy3ed852e2009-09-05 21:47:34 +00003063 /*
3064 Initialize image structure.
3065 */
3066 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00003067 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00003068 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00003069 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00003070 if (mng_info->mng_type == 0)
3071 {
glennrpfaa852b2010-03-30 12:17:00 +00003072 mng_info->mng_width=ping_width;
3073 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00003074 mng_info->frame=mng_info->image_box;
3075 mng_info->clip=mng_info->image_box;
3076 }
glennrp0fe50b42010-11-16 03:52:51 +00003077
cristy3ed852e2009-09-05 21:47:34 +00003078 else
3079 {
3080 image->page.y=mng_info->y_off[mng_info->object_id];
3081 }
glennrp0fe50b42010-11-16 03:52:51 +00003082
cristy3ed852e2009-09-05 21:47:34 +00003083 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00003084 image->columns=ping_width;
3085 image->rows=ping_height;
glennrpa6c5d342012-05-14 13:21:17 +00003086
cristy16ea1392012-03-21 20:38:41 +00003087 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3088 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
glennrp8d0bca52012-05-17 02:33:23 +00003089 {
glennrp0a5b92c2014-03-16 22:08:03 +00003090 double
3091 image_gamma = image->gamma;
3092
3093 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3094 " image->gamma=%f",(float) image_gamma);
3095
3096 if (image_gamma > 0.75)
glennrp8d0bca52012-05-17 02:33:23 +00003097 {
glennrp0a5b92c2014-03-16 22:08:03 +00003098 /* Set image->rendering_intent to Undefined,
glennrpe88af772012-08-22 13:59:50 +00003099 * image->colorspace to GRAY, and reset image->chromaticity.
glennrp8d0bca52012-05-17 02:33:23 +00003100 */
glennrp67429932013-10-05 14:59:56 +00003101 image->intensity = Rec709LuminancePixelIntensityMethod;
glennrp8d0bca52012-05-17 02:33:23 +00003102 SetImageColorspace(image,GRAYColorspace,exception);
glennrp0a5b92c2014-03-16 22:08:03 +00003103 image->gamma = image_gamma;
glennrp8d0bca52012-05-17 02:33:23 +00003104 }
glennrp8d0bca52012-05-17 02:33:23 +00003105 }
glennrpe88af772012-08-22 13:59:50 +00003106
3107 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0a5b92c2014-03-16 22:08:03 +00003108 " image->colorspace=%d",(int) image->colorspace);
glennrpa6c5d342012-05-14 13:21:17 +00003109
glennrpfaa852b2010-03-30 12:17:00 +00003110 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrp32340ff2012-08-25 17:36:19 +00003111 ((int) ping_bit_depth < 16 &&
3112 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00003113 {
cristybefe4d22010-06-07 01:18:58 +00003114 size_t
3115 one;
3116
cristy3ed852e2009-09-05 21:47:34 +00003117 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00003118 one=1;
glennrpfcf06162012-11-05 14:57:08 +00003119 image->colors=one << ping_file_depth;
cristy3ed852e2009-09-05 21:47:34 +00003120#if (MAGICKCORE_QUANTUM_DEPTH == 8)
3121 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00003122 image->colors=256;
3123#else
3124 if (image->colors > 65536L)
3125 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00003126#endif
glennrpfaa852b2010-03-30 12:17:00 +00003127 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00003128 {
cristy3ed852e2009-09-05 21:47:34 +00003129 png_colorp
3130 palette;
3131
3132 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00003133 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00003134
cristy3ed852e2009-09-05 21:47:34 +00003135 if (logging != MagickFalse)
3136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3137 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3138 }
3139 }
3140
3141 if (image->storage_class == PseudoClass)
3142 {
3143 /*
3144 Initialize image colormap.
3145 */
cristy16ea1392012-03-21 20:38:41 +00003146 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
glennrpedaa0382012-04-12 14:16:21 +00003147 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003148
glennrpfaa852b2010-03-30 12:17:00 +00003149 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00003150 {
cristy3ed852e2009-09-05 21:47:34 +00003151 png_colorp
3152 palette;
3153
3154 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00003155
glennrp6af6cf12011-04-22 13:05:16 +00003156 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00003157 {
3158 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3159 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3160 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3161 }
glennrp6af6cf12011-04-22 13:05:16 +00003162
glennrp67b9c1a2011-04-22 18:47:36 +00003163 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00003164 {
3165 image->colormap[i].red=0;
3166 image->colormap[i].green=0;
3167 image->colormap[i].blue=0;
3168 }
cristy3ed852e2009-09-05 21:47:34 +00003169 }
glennrp0fe50b42010-11-16 03:52:51 +00003170
cristy3ed852e2009-09-05 21:47:34 +00003171 else
3172 {
cristybb503372010-05-27 20:51:26 +00003173 size_t
cristy3ed852e2009-09-05 21:47:34 +00003174 scale;
3175
glennrpfcf06162012-11-05 14:57:08 +00003176 scale=(QuantumRange/((1UL << ping_file_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00003177
cristy3ed852e2009-09-05 21:47:34 +00003178 if (scale < 1)
3179 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00003180
cristybb503372010-05-27 20:51:26 +00003181 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00003182 {
3183 image->colormap[i].red=(Quantum) (i*scale);
3184 image->colormap[i].green=(Quantum) (i*scale);
3185 image->colormap[i].blue=(Quantum) (i*scale);
3186 }
3187 }
3188 }
glennrp147bc912011-03-30 18:47:21 +00003189
glennrpcb395ac2011-03-30 19:50:23 +00003190 /* Set some properties for reporting by "identify" */
3191 {
glennrp147bc912011-03-30 18:47:21 +00003192 char
3193 msg[MaxTextExtent];
3194
glennrpfcf06162012-11-05 14:57:08 +00003195 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
glennrp147bc912011-03-30 18:47:21 +00003196 ping_interlace_method in value */
3197
cristy3b6fd2e2011-05-20 12:53:50 +00003198 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00003199 "%d, %d",(int) ping_width, (int) ping_height);
glennrp3398b5b2013-05-03 23:10:31 +00003200 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00003201
glennrpfcf06162012-11-05 14:57:08 +00003202 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
glennrp3398b5b2013-05-03 23:10:31 +00003203 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00003204
glennrp5dff4352012-06-06 22:12:04 +00003205 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3206 (int) ping_color_type,
3207 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
glennrp3398b5b2013-05-03 23:10:31 +00003208 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00003209
glennrp913f9612012-06-27 17:50:00 +00003210 if (ping_interlace_method == 0)
3211 {
3212 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3213 (int) ping_interlace_method);
3214 }
3215 else if (ping_interlace_method == 1)
3216 {
3217 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3218 (int) ping_interlace_method);
3219 }
3220 else
3221 {
3222 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3223 (int) ping_interlace_method);
3224 }
3225 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3226
3227 if (number_colors != 0)
3228 {
3229 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3230 (int) number_colors);
glennrp3398b5b2013-05-03 23:10:31 +00003231 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
glennrp913f9612012-06-27 17:50:00 +00003232 exception);
3233 }
glennrpcb395ac2011-03-30 19:50:23 +00003234 }
glennrp147bc912011-03-30 18:47:21 +00003235
cristy3ed852e2009-09-05 21:47:34 +00003236 /*
3237 Read image scanlines.
3238 */
3239 if (image->delay != 0)
3240 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00003241
glennrp0ca69b12010-07-26 01:57:52 +00003242 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00003243 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3244 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00003245 {
glennrp1b888c42012-03-02 15:18:14 +00003246 /* This happens later in non-ping decodes */
3247 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3248 image->storage_class=DirectClass;
3249
cristy3ed852e2009-09-05 21:47:34 +00003250 if (logging != MagickFalse)
3251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003252 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003253 mng_info->scenes_found-1);
3254 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00003255
glennrp868fff32014-03-16 22:09:06 +00003256#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00003257 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003258#endif
glennrpedaa0382012-04-12 14:16:21 +00003259
cristy3ed852e2009-09-05 21:47:34 +00003260 if (logging != MagickFalse)
3261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3262 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00003263
cristy3ed852e2009-09-05 21:47:34 +00003264 return(image);
3265 }
glennrp0fe50b42010-11-16 03:52:51 +00003266
cristy3ed852e2009-09-05 21:47:34 +00003267 if (logging != MagickFalse)
3268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3269 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00003270
cristy3ed852e2009-09-05 21:47:34 +00003271 if (num_passes > 1)
cristy09973322013-06-30 21:06:30 +00003272 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
glennrpcf002022011-01-30 02:38:15 +00003273 sizeof(*ping_pixels));
cristy09973322013-06-30 21:06:30 +00003274 else
3275 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00003276
cristy09973322013-06-30 21:06:30 +00003277 if (pixel_info == (MemoryInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003278 png_error(ping,"Memory allocation failed");
cristy09973322013-06-30 21:06:30 +00003279 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
glennrp0fe50b42010-11-16 03:52:51 +00003280
cristy3ed852e2009-09-05 21:47:34 +00003281 if (logging != MagickFalse)
3282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3283 " Converting PNG pixels to pixel packets");
3284 /*
3285 Convert PNG pixels to pixel packets.
3286 */
cristy16ea1392012-03-21 20:38:41 +00003287 quantum_info=AcquireQuantumInfo(image_info,image);
3288
3289 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003290 png_error(ping,"Failed to allocate quantum_info");
glennrp0fe50b42010-11-16 03:52:51 +00003291
glennrp4b840d72012-11-22 16:01:16 +00003292 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3293
glennrpc8cbc5d2011-01-01 00:12:34 +00003294 {
3295
3296 MagickBooleanType
3297 found_transparent_pixel;
3298
3299 found_transparent_pixel=MagickFalse;
3300
cristy3ed852e2009-09-05 21:47:34 +00003301 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00003302 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003303 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00003304 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003305 /*
3306 Convert image to DirectClass pixel packets.
3307 */
cristy8a46d822012-08-28 23:32:39 +00003308 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
glennrpc8cbc5d2011-01-01 00:12:34 +00003309 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3310 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
cristyb0a657e2012-08-29 00:45:37 +00003311 BlendPixelTrait : UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00003312
glennrpc8cbc5d2011-01-01 00:12:34 +00003313 for (y=0; y < (ssize_t) image->rows; y++)
3314 {
3315 if (num_passes > 1)
3316 row_offset=ping_rowbytes*y;
3317
3318 else
3319 row_offset=0;
3320
glennrpcf002022011-01-30 02:38:15 +00003321 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc5c31be2012-11-07 18:42:18 +00003322
3323 if (pass < num_passes-1)
3324 continue;
3325
cristy862a33c2012-05-17 22:49:37 +00003326 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003327
cristy16ea1392012-03-21 20:38:41 +00003328 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00003329 break;
3330
cristy16ea1392012-03-21 20:38:41 +00003331 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3332 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3333 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003334
cristy16ea1392012-03-21 20:38:41 +00003335 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3336 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3337 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003338
cristy16ea1392012-03-21 20:38:41 +00003339 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3340 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3341 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003342
cristy16ea1392012-03-21 20:38:41 +00003343 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3344 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3345 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003346
cristy16ea1392012-03-21 20:38:41 +00003347 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3348 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3349 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00003350
glennrpc8cbc5d2011-01-01 00:12:34 +00003351 if (found_transparent_pixel == MagickFalse)
3352 {
3353 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00003354 if (y== 0 && logging != MagickFalse)
3355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3356 " Looking for cheap transparent pixel");
3357
glennrpc8cbc5d2011-01-01 00:12:34 +00003358 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3359 {
glennrp5aa37f62011-01-02 03:07:57 +00003360 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3361 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy16ea1392012-03-21 20:38:41 +00003362 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00003363 {
glennrpa6a06632011-01-19 15:15:34 +00003364 if (logging != MagickFalse)
3365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3366 " ...got one.");
3367
glennrpc8cbc5d2011-01-01 00:12:34 +00003368 found_transparent_pixel = MagickTrue;
3369 break;
3370 }
glennrp4f25bd02011-01-01 18:51:28 +00003371 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3372 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
cristy16ea1392012-03-21 20:38:41 +00003373 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3374 transparent_color.red &&
3375 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3376 transparent_color.green &&
3377 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3378 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00003379 {
glennrpa6a06632011-01-19 15:15:34 +00003380 if (logging != MagickFalse)
3381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3382 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00003383 found_transparent_pixel = MagickTrue;
3384 break;
3385 }
cristy16ea1392012-03-21 20:38:41 +00003386 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00003387 }
3388 }
3389
3390 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3391 {
3392 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3393 image->rows);
3394
3395 if (status == MagickFalse)
3396 break;
3397 }
3398 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3399 break;
3400 }
3401
3402 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3403 {
3404 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003405 if (status == MagickFalse)
3406 break;
3407 }
cristy3ed852e2009-09-05 21:47:34 +00003408 }
cristy3ed852e2009-09-05 21:47:34 +00003409 }
glennrp0fe50b42010-11-16 03:52:51 +00003410
cristy3ed852e2009-09-05 21:47:34 +00003411 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003412
cristy3ed852e2009-09-05 21:47:34 +00003413 for (pass=0; pass < num_passes; pass++)
3414 {
3415 Quantum
3416 *quantum_scanline;
3417
3418 register Quantum
3419 *r;
3420
3421 /*
3422 Convert grayscale image to PseudoClass pixel packets.
3423 */
glennrpc17d96f2011-06-27 01:20:11 +00003424 if (logging != MagickFalse)
3425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3426 " Converting grayscale pixels to pixel packets");
cristy16ea1392012-03-21 20:38:41 +00003427
cristy8a46d822012-08-28 23:32:39 +00003428 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristyb0a657e2012-08-29 00:45:37 +00003429 BlendPixelTrait : UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00003430
cristy3ed852e2009-09-05 21:47:34 +00003431 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
cristyb0a657e2012-08-29 00:45:37 +00003432 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3433 sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003434
cristy3ed852e2009-09-05 21:47:34 +00003435 if (quantum_scanline == (Quantum *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003436 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003437
cristybb503372010-05-27 20:51:26 +00003438 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003439 {
glennrp4f996392013-01-26 22:56:50 +00003440 Quantum
3441 alpha;
3442
cristy3ed852e2009-09-05 21:47:34 +00003443 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003444 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003445
cristy3ed852e2009-09-05 21:47:34 +00003446 else
3447 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003448
glennrpcf002022011-01-30 02:38:15 +00003449 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc5c31be2012-11-07 18:42:18 +00003450
3451 if (pass < num_passes-1)
3452 continue;
3453
glennrp4f996392013-01-26 22:56:50 +00003454 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003455
cristy16ea1392012-03-21 20:38:41 +00003456 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003457 break;
glennrp0fe50b42010-11-16 03:52:51 +00003458
glennrpcf002022011-01-30 02:38:15 +00003459 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003460 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003461
glennrpfaa852b2010-03-30 12:17:00 +00003462 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003463 {
cristy3ed852e2009-09-05 21:47:34 +00003464 case 8:
3465 {
glennrp4f996392013-01-26 22:56:50 +00003466
glennrpfaa852b2010-03-30 12:17:00 +00003467 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003468 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003469 {
glennrpa18d5bc2011-04-23 14:51:34 +00003470 *r++=*p++;
glennrp4f996392013-01-26 22:56:50 +00003471
3472 alpha=ScaleCharToQuantum((unsigned char)*p++);
3473
3474 SetPixelAlpha(image,alpha,q);
3475
3476 if (alpha != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003477 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003478
cristy16ea1392012-03-21 20:38:41 +00003479 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003480 }
glennrp0fe50b42010-11-16 03:52:51 +00003481
cristy3ed852e2009-09-05 21:47:34 +00003482 else
cristybb503372010-05-27 20:51:26 +00003483 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003484 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003485
cristy3ed852e2009-09-05 21:47:34 +00003486 break;
3487 }
glennrp47b9dd52010-11-24 18:12:06 +00003488
cristy3ed852e2009-09-05 21:47:34 +00003489 case 16:
3490 {
cristybb503372010-05-27 20:51:26 +00003491 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003492 {
glennrpc17d96f2011-06-27 01:20:11 +00003493#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
cristy87281ec2013-05-08 11:50:14 +00003494 unsigned short
glennrp58f77c72011-04-23 14:09:09 +00003495 quantum;
3496
3497 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003498 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003499
3500 else
glennrpc17d96f2011-06-27 01:20:11 +00003501 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003502
glennrp58f77c72011-04-23 14:09:09 +00003503 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003504 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003505 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003506
3507 if (ping_color_type == 4)
3508 {
glennrpc17d96f2011-06-27 01:20:11 +00003509 if (image->colors > 256)
3510 quantum=((*p++) << 8);
3511 else
3512 quantum=0;
3513
glennrp58f77c72011-04-23 14:09:09 +00003514 quantum|=(*p++);
glennrp4f996392013-01-26 22:56:50 +00003515
3516 alpha=ScaleShortToQuantum(quantum);
3517 SetPixelAlpha(image,alpha,q);
3518
3519 if (alpha != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003520 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003521
cristy16ea1392012-03-21 20:38:41 +00003522 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003523 }
glennrp58f77c72011-04-23 14:09:09 +00003524
3525#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3526 *r++=(*p++);
3527 p++; /* strip low byte */
3528
3529 if (ping_color_type == 4)
3530 {
cristy16ea1392012-03-21 20:38:41 +00003531 SetPixelAlpha(image,*p++,q);
glennrp4f996392013-01-26 22:56:50 +00003532
cristy16ea1392012-03-21 20:38:41 +00003533 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003534 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003535
glennrp9d0ea4d2011-04-22 18:35:57 +00003536 p++;
cristy16ea1392012-03-21 20:38:41 +00003537 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003538 }
cristy3ed852e2009-09-05 21:47:34 +00003539#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003540 }
glennrp47b9dd52010-11-24 18:12:06 +00003541
cristy3ed852e2009-09-05 21:47:34 +00003542 break;
3543 }
glennrp47b9dd52010-11-24 18:12:06 +00003544
cristy3ed852e2009-09-05 21:47:34 +00003545 default:
3546 break;
3547 }
glennrp3faa9a32011-04-23 14:00:25 +00003548
cristy3ed852e2009-09-05 21:47:34 +00003549 /*
3550 Transfer image scanline.
3551 */
3552 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003553
cristy16ea1392012-03-21 20:38:41 +00003554 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3555
3556 if (q == (Quantum *) NULL)
3557 break;
cristybb503372010-05-27 20:51:26 +00003558 for (x=0; x < (ssize_t) image->columns; x++)
cristy16ea1392012-03-21 20:38:41 +00003559 {
3560 SetPixelIndex(image,*r++,q);
3561 q+=GetPixelChannels(image);
3562 }
glennrp0fe50b42010-11-16 03:52:51 +00003563
cristy3ed852e2009-09-05 21:47:34 +00003564 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3565 break;
glennrp0fe50b42010-11-16 03:52:51 +00003566
cristy7a287bf2010-02-14 02:18:09 +00003567 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3568 {
cristycee97112010-05-28 00:44:52 +00003569 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003570 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003571
cristy7a287bf2010-02-14 02:18:09 +00003572 if (status == MagickFalse)
3573 break;
3574 }
cristy3ed852e2009-09-05 21:47:34 +00003575 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003576
cristy7a287bf2010-02-14 02:18:09 +00003577 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003578 {
3579 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003580
cristy3ed852e2009-09-05 21:47:34 +00003581 if (status == MagickFalse)
3582 break;
3583 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003584
cristy3ed852e2009-09-05 21:47:34 +00003585 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3586 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003587
cristyb0a657e2012-08-29 00:45:37 +00003588 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3589 UndefinedPixelTrait;
glennrpc8cbc5d2011-01-01 00:12:34 +00003590
3591 if (logging != MagickFalse)
3592 {
3593 if (found_transparent_pixel != MagickFalse)
3594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3595 " Found transparent pixel");
3596 else
glennrp5aa37f62011-01-02 03:07:57 +00003597 {
3598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3599 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003600
glennrp5aa37f62011-01-02 03:07:57 +00003601 ping_color_type&=0x03;
3602 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003603 }
3604 }
3605
cristy16ea1392012-03-21 20:38:41 +00003606 if (quantum_info != (QuantumInfo *) NULL)
3607 quantum_info=DestroyQuantumInfo(quantum_info);
3608
cristy5c6f7892010-05-05 22:53:29 +00003609 if (image->storage_class == PseudoClass)
3610 {
cristyb0a657e2012-08-29 00:45:37 +00003611 PixelTrait
3612 alpha_trait;
cristy5c6f7892010-05-05 22:53:29 +00003613
cristyb0a657e2012-08-29 00:45:37 +00003614 alpha_trait=image->alpha_trait;
cristy8a46d822012-08-28 23:32:39 +00003615 image->alpha_trait=UndefinedPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003616 (void) SyncImage(image,exception);
cristyb0a657e2012-08-29 00:45:37 +00003617 image->alpha_trait=alpha_trait;
cristy5c6f7892010-05-05 22:53:29 +00003618 }
glennrp47b9dd52010-11-24 18:12:06 +00003619
glennrp4eb39312011-03-30 21:34:55 +00003620 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003621
3622 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003623 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003624 {
3625 png_destroy_read_struct(&ping,&ping_info,&end_info);
cristy09973322013-06-30 21:06:30 +00003626 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00003627 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00003628 (void) SetImageBackgroundColor(image,exception);
glennrp868fff32014-03-16 22:09:06 +00003629#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00003630 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003631#endif
3632 if (logging != MagickFalse)
3633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3634 " exit ReadOnePNGImage() early.");
3635 return(image);
3636 }
glennrp47b9dd52010-11-24 18:12:06 +00003637
glennrpfaa852b2010-03-30 12:17:00 +00003638 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003639 {
3640 ClassType
3641 storage_class;
3642
3643 /*
3644 Image has a transparent background.
3645 */
3646 storage_class=image->storage_class;
cristy8a46d822012-08-28 23:32:39 +00003647 image->alpha_trait=BlendPixelTrait;
glennrpc11cf6a2010-03-20 16:46:19 +00003648
glennrp3c218112010-11-27 15:31:26 +00003649/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003650
glennrp0fe50b42010-11-16 03:52:51 +00003651 if (storage_class == PseudoClass)
3652 {
3653 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003654 {
glennrp0fe50b42010-11-16 03:52:51 +00003655 for (x=0; x < ping_num_trans; x++)
3656 {
cristy8a46d822012-08-28 23:32:39 +00003657 image->colormap[x].alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003658 image->colormap[x].alpha =
3659 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003660 }
glennrpc11cf6a2010-03-20 16:46:19 +00003661 }
glennrp47b9dd52010-11-24 18:12:06 +00003662
glennrp0fe50b42010-11-16 03:52:51 +00003663 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3664 {
3665 for (x=0; x < (int) image->colors; x++)
3666 {
3667 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy16ea1392012-03-21 20:38:41 +00003668 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003669 {
cristy8a46d822012-08-28 23:32:39 +00003670 image->colormap[x].alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003671 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003672 }
3673 }
3674 }
cristy16ea1392012-03-21 20:38:41 +00003675 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003676 }
glennrp47b9dd52010-11-24 18:12:06 +00003677
glennrpa6a06632011-01-19 15:15:34 +00003678#if 1 /* Should have already been done above, but glennrp problem P10
3679 * needs this.
3680 */
glennrp0fe50b42010-11-16 03:52:51 +00003681 else
3682 {
3683 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003684 {
glennrp0fe50b42010-11-16 03:52:51 +00003685 image->storage_class=storage_class;
3686 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3687
cristy16ea1392012-03-21 20:38:41 +00003688 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003689 break;
3690
glennrp0fe50b42010-11-16 03:52:51 +00003691
glennrpa6a06632011-01-19 15:15:34 +00003692 /* Caution: on a Q8 build, this does not distinguish between
3693 * 16-bit colors that differ only in the low byte
3694 */
glennrp0fe50b42010-11-16 03:52:51 +00003695 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3696 {
cristy16ea1392012-03-21 20:38:41 +00003697 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3698 transparent_color.red &&
3699 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3700 transparent_color.green &&
3701 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3702 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003703 {
cristy16ea1392012-03-21 20:38:41 +00003704 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003705 }
glennrp0fe50b42010-11-16 03:52:51 +00003706
glennrp67b9c1a2011-04-22 18:47:36 +00003707#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003708 else
glennrp4f25bd02011-01-01 18:51:28 +00003709 {
cristy16ea1392012-03-21 20:38:41 +00003710 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003711 }
glennrpa6a06632011-01-19 15:15:34 +00003712#endif
glennrp0fe50b42010-11-16 03:52:51 +00003713
cristy16ea1392012-03-21 20:38:41 +00003714 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003715 }
3716
3717 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3718 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003719 }
glennrp0fe50b42010-11-16 03:52:51 +00003720 }
glennrpa6a06632011-01-19 15:15:34 +00003721#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003722
cristy3ed852e2009-09-05 21:47:34 +00003723 image->storage_class=DirectClass;
3724 }
glennrp3c218112010-11-27 15:31:26 +00003725
cristyeb3b22a2011-03-31 20:16:11 +00003726 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003727 {
3728 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003729 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3730 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003731 else
glennrpa0ed0092011-04-18 16:36:29 +00003732 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3733 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003734
glennrp4eb39312011-03-30 21:34:55 +00003735 if (status != MagickFalse)
3736 for (i=0; i < (ssize_t) num_text; i++)
3737 {
3738 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003739
glennrp4eb39312011-03-30 21:34:55 +00003740 if (logging != MagickFalse)
3741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3742 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003743
glennrp4eb39312011-03-30 21:34:55 +00003744 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003745 {
glennrp99f96812014-06-30 02:57:09 +00003746 const char
3747 *value;
3748
3749 value=GetImageOption(image_info,"profile:skip");
3750
3751 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3752 {
3753 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3754 (int) i,exception);
3755 num_raw_profiles++;
3756 if (logging != MagickFalse)
3757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3758 " Read raw profile %s",text[i].key+17);
3759 }
3760 else
3761 {
3762 if (logging != MagickFalse)
3763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3764 " Skipping raw profile %s",text[i].key+17);
3765 }
glennrp97f90e22011-02-22 05:47:58 +00003766 }
glennrp0fe50b42010-11-16 03:52:51 +00003767
glennrp4eb39312011-03-30 21:34:55 +00003768 else
3769 {
3770 char
3771 *value;
3772
3773 length=text[i].text_length;
3774 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3775 sizeof(*value));
3776 if (value == (char *) NULL)
3777 {
glennrpedaa0382012-04-12 14:16:21 +00003778 png_error(ping,"Memory allocation failed");
glennrp4eb39312011-03-30 21:34:55 +00003779 break;
3780 }
3781 *value='\0';
3782 (void) ConcatenateMagickString(value,text[i].text,length+2);
3783
3784 /* Don't save "density" or "units" property if we have a pHYs
3785 * chunk
3786 */
3787 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3788 (LocaleCompare(text[i].key,"density") != 0 &&
3789 LocaleCompare(text[i].key,"units") != 0))
cristy16ea1392012-03-21 20:38:41 +00003790 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003791
3792 if (logging != MagickFalse)
3793 {
3794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00003795 " length: %lu\n"
3796 " Keyword: %s",
3797 (unsigned long) length,
3798 text[i].key);
glennrp4eb39312011-03-30 21:34:55 +00003799 }
3800
3801 value=DestroyString(value);
3802 }
3803 }
3804 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003805 }
glennrp3c218112010-11-27 15:31:26 +00003806
cristy3ed852e2009-09-05 21:47:34 +00003807#ifdef MNG_OBJECT_BUFFERS
3808 /*
3809 Store the object if necessary.
3810 */
3811 if (object_id && !mng_info->frozen[object_id])
3812 {
3813 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3814 {
3815 /*
3816 create a new object buffer.
3817 */
3818 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003819 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003820
cristy3ed852e2009-09-05 21:47:34 +00003821 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3822 {
3823 mng_info->ob[object_id]->image=(Image *) NULL;
3824 mng_info->ob[object_id]->reference_count=1;
3825 }
3826 }
glennrp47b9dd52010-11-24 18:12:06 +00003827
cristy3ed852e2009-09-05 21:47:34 +00003828 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3829 mng_info->ob[object_id]->frozen)
3830 {
3831 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003832 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003833
cristy3ed852e2009-09-05 21:47:34 +00003834 if (mng_info->ob[object_id]->frozen)
glennrpedaa0382012-04-12 14:16:21 +00003835 png_error(ping,"Cannot overwrite frozen MNG object buffer");
cristy3ed852e2009-09-05 21:47:34 +00003836 }
glennrp0fe50b42010-11-16 03:52:51 +00003837
cristy3ed852e2009-09-05 21:47:34 +00003838 else
3839 {
cristy3ed852e2009-09-05 21:47:34 +00003840
3841 if (mng_info->ob[object_id]->image != (Image *) NULL)
3842 mng_info->ob[object_id]->image=DestroyImage
3843 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003844
cristy3ed852e2009-09-05 21:47:34 +00003845 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristy16ea1392012-03-21 20:38:41 +00003846 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003847
cristy3ed852e2009-09-05 21:47:34 +00003848 if (mng_info->ob[object_id]->image != (Image *) NULL)
3849 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003850
cristy3ed852e2009-09-05 21:47:34 +00003851 else
glennrpedaa0382012-04-12 14:16:21 +00003852 png_error(ping, "Cloning image for object buffer failed");
glennrp0fe50b42010-11-16 03:52:51 +00003853
glennrpfaa852b2010-03-30 12:17:00 +00003854 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003855 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003856
glennrpfaa852b2010-03-30 12:17:00 +00003857 mng_info->ob[object_id]->width=ping_width;
3858 mng_info->ob[object_id]->height=ping_height;
3859 mng_info->ob[object_id]->color_type=ping_color_type;
3860 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3861 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3862 mng_info->ob[object_id]->compression_method=
3863 ping_compression_method;
3864 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003865
glennrpfaa852b2010-03-30 12:17:00 +00003866 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003867 {
cristy3ed852e2009-09-05 21:47:34 +00003868 png_colorp
3869 plte;
3870
3871 /*
3872 Copy the PLTE to the object buffer.
3873 */
3874 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3875 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003876
cristy3ed852e2009-09-05 21:47:34 +00003877 for (i=0; i < number_colors; i++)
3878 {
3879 mng_info->ob[object_id]->plte[i]=plte[i];
3880 }
3881 }
glennrp47b9dd52010-11-24 18:12:06 +00003882
cristy3ed852e2009-09-05 21:47:34 +00003883 else
3884 mng_info->ob[object_id]->plte_length=0;
3885 }
3886 }
3887#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003888
cristy8a46d822012-08-28 23:32:39 +00003889 /* Set image->alpha_trait to MagickTrue if the input colortype supports
glennrp0a55b4c2011-03-23 12:38:38 +00003890 * alpha or if a valid tRNS chunk is present, no matter whether there
3891 * is actual transparency present.
3892 */
cristy8a46d822012-08-28 23:32:39 +00003893 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
glennrp0a55b4c2011-03-23 12:38:38 +00003894 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3895 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
cristyb0a657e2012-08-29 00:45:37 +00003896 BlendPixelTrait : UndefinedPixelTrait;
glennrp0a55b4c2011-03-23 12:38:38 +00003897
glennrp224ad8d2013-02-01 17:04:04 +00003898#if 0 /* I'm not sure what's wrong here but it does not work. */
glennrp5830fbc2013-01-27 06:11:45 +00003899 if (image->alpha_trait == BlendPixelTrait)
3900 {
3901 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3902 (void) SetImageType(image,GrayscaleMatteType,exception);
3903
3904 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3905 (void) SetImageType(image,PaletteMatteType,exception);
3906
3907 else
3908 (void) SetImageType(image,TrueColorMatteType,exception);
3909 }
3910
3911 else
3912 {
3913 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3914 (void) SetImageType(image,GrayscaleType,exception);
3915
3916 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3917 (void) SetImageType(image,PaletteType,exception);
3918
3919 else
3920 (void) SetImageType(image,TrueColorType,exception);
3921 }
glennrp224ad8d2013-02-01 17:04:04 +00003922#endif
glennrp5830fbc2013-01-27 06:11:45 +00003923
glennrpcb395ac2011-03-30 19:50:23 +00003924 /* Set more properties for identify to retrieve */
3925 {
3926 char
3927 msg[MaxTextExtent];
3928
glennrp4eb39312011-03-30 21:34:55 +00003929 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003930 {
3931 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003932 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003933 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrp3398b5b2013-05-03 23:10:31 +00003934 (void) SetImageProperty(image,"png:text",msg,
cristy16ea1392012-03-21 20:38:41 +00003935 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003936 }
3937
3938 if (num_raw_profiles != 0)
3939 {
cristy3b6fd2e2011-05-20 12:53:50 +00003940 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003941 "%d were found", num_raw_profiles);
cristy16ea1392012-03-21 20:38:41 +00003942 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3943 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003944 }
3945
glennrp98b83d42012-07-23 02:50:31 +00003946 if (ping_found_cHRM != MagickFalse)
glennrp59612252011-03-30 21:45:21 +00003947 {
cristy3b6fd2e2011-05-20 12:53:50 +00003948 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003949 "chunk was found (see Chromaticity, above)");
glennrp3398b5b2013-05-03 23:10:31 +00003950 (void) SetImageProperty(image,"png:cHRM",msg,
cristy16ea1392012-03-21 20:38:41 +00003951 exception);
glennrp59612252011-03-30 21:45:21 +00003952 }
glennrpcb395ac2011-03-30 19:50:23 +00003953
3954 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003955 {
cristy3b6fd2e2011-05-20 12:53:50 +00003956 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003957 "chunk was found (see Background color, above)");
glennrp3398b5b2013-05-03 23:10:31 +00003958 (void) SetImageProperty(image,"png:bKGD",msg,
cristy16ea1392012-03-21 20:38:41 +00003959 exception);
glennrp59612252011-03-30 21:45:21 +00003960 }
3961
cristy3b6fd2e2011-05-20 12:53:50 +00003962 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003963 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003964
glennrp98b83d42012-07-23 02:50:31 +00003965#if defined(PNG_iCCP_SUPPORTED)
3966 if (ping_found_iCCP != MagickFalse)
glennrp3398b5b2013-05-03 23:10:31 +00003967 (void) SetImageProperty(image,"png:iCCP",msg,
cristy16ea1392012-03-21 20:38:41 +00003968 exception);
glennrp98b83d42012-07-23 02:50:31 +00003969#endif
glennrpcb395ac2011-03-30 19:50:23 +00003970
glennrpcb395ac2011-03-30 19:50:23 +00003971 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrp3398b5b2013-05-03 23:10:31 +00003972 (void) SetImageProperty(image,"png:tRNS",msg,
cristy16ea1392012-03-21 20:38:41 +00003973 exception);
glennrp4eb39312011-03-30 21:34:55 +00003974
3975#if defined(PNG_sRGB_SUPPORTED)
glennrp98b83d42012-07-23 02:50:31 +00003976 if (ping_found_sRGB != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003977 {
cristy3b6fd2e2011-05-20 12:53:50 +00003978 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp98b83d42012-07-23 02:50:31 +00003979 "intent=%d (%s)",
3980 (int) intent,
3981 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
glennrp3398b5b2013-05-03 23:10:31 +00003982 (void) SetImageProperty(image,"png:sRGB",msg,
glennrp98b83d42012-07-23 02:50:31 +00003983 exception);
glennrp4eb39312011-03-30 21:34:55 +00003984 }
3985#endif
3986
glennrp98b83d42012-07-23 02:50:31 +00003987 if (ping_found_gAMA != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003988 {
cristy3b6fd2e2011-05-20 12:53:50 +00003989 (void) FormatLocaleString(msg,MaxTextExtent,
cristy16ea1392012-03-21 20:38:41 +00003990 "gamma=%.8g (See Gamma, above)",
3991 file_gamma);
glennrp3398b5b2013-05-03 23:10:31 +00003992 (void) SetImageProperty(image,"png:gAMA",msg,
cristy16ea1392012-03-21 20:38:41 +00003993 exception);
glennrp4eb39312011-03-30 21:34:55 +00003994 }
3995
3996#if defined(PNG_pHYs_SUPPORTED)
3997 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3998 {
cristy3b6fd2e2011-05-20 12:53:50 +00003999 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00004000 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00004001 (double) x_resolution,(double) y_resolution, unit_type);
glennrp3398b5b2013-05-03 23:10:31 +00004002 (void) SetImageProperty(image,"png:pHYs",msg,
cristy16ea1392012-03-21 20:38:41 +00004003 exception);
glennrp4eb39312011-03-30 21:34:55 +00004004 }
4005#endif
4006
4007#if defined(PNG_oFFs_SUPPORTED)
4008 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4009 {
cristy3b6fd2e2011-05-20 12:53:50 +00004010 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00004011 (double) image->page.x,(double) image->page.y);
glennrp3398b5b2013-05-03 23:10:31 +00004012 (void) SetImageProperty(image,"png:oFFs",msg,
cristy16ea1392012-03-21 20:38:41 +00004013 exception);
glennrp4eb39312011-03-30 21:34:55 +00004014 }
4015#endif
4016
glennrp07523c72011-03-31 18:12:10 +00004017 if ((image->page.width != 0 && image->page.width != image->columns) ||
4018 (image->page.height != 0 && image->page.height != image->rows))
4019 {
cristy3b6fd2e2011-05-20 12:53:50 +00004020 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00004021 "width=%.20g, height=%.20g",
4022 (double) image->page.width,(double) image->page.height);
glennrp3398b5b2013-05-03 23:10:31 +00004023 (void) SetImageProperty(image,"png:vpAg",msg,
cristy16ea1392012-03-21 20:38:41 +00004024 exception);
glennrp07523c72011-03-31 18:12:10 +00004025 }
glennrpcb395ac2011-03-30 19:50:23 +00004026 }
4027
cristy3ed852e2009-09-05 21:47:34 +00004028 /*
4029 Relinquish resources.
4030 */
4031 png_destroy_read_struct(&ping,&ping_info,&end_info);
4032
cristy09973322013-06-30 21:06:30 +00004033 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00004034
4035 if (logging != MagickFalse)
4036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4037 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004038
glennrp868fff32014-03-16 22:09:06 +00004039#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpedaa0382012-04-12 14:16:21 +00004040 UnlockSemaphoreInfo(ping_semaphore);
4041#endif
4042
4043 /* } for navigation to beginning of SETJMP-protected block, revert to
4044 * Throwing an Exception when an error occurs.
4045 */
4046
cristy3ed852e2009-09-05 21:47:34 +00004047 return(image);
4048
4049/* end of reading one PNG image */
4050}
4051
4052static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4053{
4054 Image
4055 *image,
4056 *previous;
4057
4058 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004059 have_mng_structure,
4060 logging,
cristy3ed852e2009-09-05 21:47:34 +00004061 status;
4062
4063 MngInfo
4064 *mng_info;
4065
4066 char
4067 magic_number[MaxTextExtent];
4068
cristy3ed852e2009-09-05 21:47:34 +00004069 ssize_t
4070 count;
4071
4072 /*
4073 Open image file.
4074 */
4075 assert(image_info != (const ImageInfo *) NULL);
4076 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00004077
cristy3ed852e2009-09-05 21:47:34 +00004078 if (image_info->debug != MagickFalse)
4079 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4080 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00004081
cristy3ed852e2009-09-05 21:47:34 +00004082 assert(exception != (ExceptionInfo *) NULL);
4083 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004084 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004085 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004086 mng_info=(MngInfo *) NULL;
4087 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004088
cristy3ed852e2009-09-05 21:47:34 +00004089 if (status == MagickFalse)
4090 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00004091
cristy3ed852e2009-09-05 21:47:34 +00004092 /*
4093 Verify PNG signature.
4094 */
4095 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004096
glennrpdde35db2011-02-21 12:06:32 +00004097 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004098 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004099
cristy3ed852e2009-09-05 21:47:34 +00004100 /*
4101 Allocate a MngInfo structure.
4102 */
4103 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004104 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004105
cristy3ed852e2009-09-05 21:47:34 +00004106 if (mng_info == (MngInfo *) NULL)
4107 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004108
cristy3ed852e2009-09-05 21:47:34 +00004109 /*
4110 Initialize members of the MngInfo structure.
4111 */
4112 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4113 mng_info->image=image;
4114 have_mng_structure=MagickTrue;
4115
4116 previous=image;
4117 image=ReadOnePNGImage(mng_info,image_info,exception);
4118 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00004119
cristy3ed852e2009-09-05 21:47:34 +00004120 if (image == (Image *) NULL)
4121 {
4122 if (previous != (Image *) NULL)
4123 {
4124 if (previous->signature != MagickSignature)
4125 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004126
cristy3ed852e2009-09-05 21:47:34 +00004127 (void) CloseBlob(previous);
4128 (void) DestroyImageList(previous);
4129 }
glennrp0fe50b42010-11-16 03:52:51 +00004130
cristy3ed852e2009-09-05 21:47:34 +00004131 if (logging != MagickFalse)
4132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4133 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004134
cristy3ed852e2009-09-05 21:47:34 +00004135 return((Image *) NULL);
4136 }
glennrp47b9dd52010-11-24 18:12:06 +00004137
cristy3ed852e2009-09-05 21:47:34 +00004138 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004139
cristy3ed852e2009-09-05 21:47:34 +00004140 if ((image->columns == 0) || (image->rows == 0))
4141 {
4142 if (logging != MagickFalse)
4143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4144 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00004145
cristy3ed852e2009-09-05 21:47:34 +00004146 ThrowReaderException(CorruptImageError,"CorruptImage");
4147 }
glennrp47b9dd52010-11-24 18:12:06 +00004148
cristy72715f52012-06-26 17:55:16 +00004149 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
glennrp3d627862013-02-26 00:19:34 +00004150 ((image->gamma < .45) || (image->gamma > .46)) &&
4151 !(image->chromaticity.red_primary.x>0.6399f &&
4152 image->chromaticity.red_primary.x<0.6401f &&
4153 image->chromaticity.red_primary.y>0.3299f &&
4154 image->chromaticity.red_primary.y<0.3301f &&
4155 image->chromaticity.green_primary.x>0.2999f &&
4156 image->chromaticity.green_primary.x<0.3001f &&
4157 image->chromaticity.green_primary.y>0.5999f &&
4158 image->chromaticity.green_primary.y<0.6001f &&
4159 image->chromaticity.blue_primary.x>0.1499f &&
4160 image->chromaticity.blue_primary.x<0.1501f &&
4161 image->chromaticity.blue_primary.y>0.0599f &&
4162 image->chromaticity.blue_primary.y<0.0601f &&
4163 image->chromaticity.white_point.x>0.3126f &&
4164 image->chromaticity.white_point.x<0.3128f &&
4165 image->chromaticity.white_point.y>0.3289f &&
4166 image->chromaticity.white_point.y<0.3291f))
cristy72715f52012-06-26 17:55:16 +00004167 SetImageColorspace(image,RGBColorspace,exception);
4168
cristy3ed852e2009-09-05 21:47:34 +00004169 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00004170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4171 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4172 (double) image->page.width,(double) image->page.height,
4173 (double) image->page.x,(double) image->page.y);
4174
4175 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004177
cristy3ed852e2009-09-05 21:47:34 +00004178 return(image);
4179}
4180
4181
4182
4183#if defined(JNG_SUPPORTED)
4184/*
4185%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4186% %
4187% %
4188% %
4189% R e a d O n e J N G I m a g e %
4190% %
4191% %
4192% %
4193%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4194%
4195% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4196% (minus the 8-byte signature) and returns it. It allocates the memory
4197% necessary for the new Image structure and returns a pointer to the new
4198% image.
4199%
4200% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4201%
4202% The format of the ReadOneJNGImage method is:
4203%
4204% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4205% ExceptionInfo *exception)
4206%
4207% A description of each parameter follows:
4208%
4209% o mng_info: Specifies a pointer to a MngInfo structure.
4210%
4211% o image_info: the image info.
4212%
4213% o exception: return any errors or warnings in this structure.
4214%
4215*/
4216static Image *ReadOneJNGImage(MngInfo *mng_info,
4217 const ImageInfo *image_info, ExceptionInfo *exception)
4218{
4219 Image
4220 *alpha_image,
4221 *color_image,
4222 *image,
4223 *jng_image;
4224
4225 ImageInfo
4226 *alpha_image_info,
4227 *color_image_info;
4228
cristy4383ec82011-01-05 15:42:32 +00004229 MagickBooleanType
4230 logging;
4231
cristybb503372010-05-27 20:51:26 +00004232 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004233 y;
4234
4235 MagickBooleanType
4236 status;
4237
4238 png_uint_32
4239 jng_height,
4240 jng_width;
4241
4242 png_byte
4243 jng_color_type,
4244 jng_image_sample_depth,
4245 jng_image_compression_method,
4246 jng_image_interlace_method,
4247 jng_alpha_sample_depth,
4248 jng_alpha_compression_method,
4249 jng_alpha_filter_method,
4250 jng_alpha_interlace_method;
4251
cristy16ea1392012-03-21 20:38:41 +00004252 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00004253 *s;
4254
cristybb503372010-05-27 20:51:26 +00004255 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004256 i,
4257 x;
4258
cristy16ea1392012-03-21 20:38:41 +00004259 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004260 *q;
4261
4262 register unsigned char
4263 *p;
4264
4265 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00004266 read_JSEP,
glennrpe0421fe2014-06-08 02:04:34 +00004267 reading_idat;
cristy3ed852e2009-09-05 21:47:34 +00004268
cristybb503372010-05-27 20:51:26 +00004269 size_t
cristy3ed852e2009-09-05 21:47:34 +00004270 length;
4271
4272 jng_alpha_compression_method=0;
4273 jng_alpha_sample_depth=8;
4274 jng_color_type=0;
4275 jng_height=0;
4276 jng_width=0;
4277 alpha_image=(Image *) NULL;
4278 color_image=(Image *) NULL;
4279 alpha_image_info=(ImageInfo *) NULL;
4280 color_image_info=(ImageInfo *) NULL;
4281
4282 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00004283 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004284
4285 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00004286
cristy16ea1392012-03-21 20:38:41 +00004287 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004288 {
4289 /*
4290 Allocate next image structure.
4291 */
4292 if (logging != MagickFalse)
4293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4294 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004295
cristy16ea1392012-03-21 20:38:41 +00004296 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004297
cristy3ed852e2009-09-05 21:47:34 +00004298 if (GetNextImageInList(image) == (Image *) NULL)
4299 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004300
cristy3ed852e2009-09-05 21:47:34 +00004301 image=SyncNextImageInList(image);
4302 }
4303 mng_info->image=image;
4304
4305 /*
4306 Signature bytes have already been read.
4307 */
4308
4309 read_JSEP=MagickFalse;
4310 reading_idat=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004311 for (;;)
4312 {
4313 char
4314 type[MaxTextExtent];
4315
4316 unsigned char
4317 *chunk;
4318
4319 unsigned int
4320 count;
4321
4322 /*
4323 Read a new JNG chunk.
4324 */
4325 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4326 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004327
cristy3ed852e2009-09-05 21:47:34 +00004328 if (status == MagickFalse)
4329 break;
glennrp0fe50b42010-11-16 03:52:51 +00004330
cristy3ed852e2009-09-05 21:47:34 +00004331 type[0]='\0';
4332 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4333 length=ReadBlobMSBLong(image);
4334 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4335
4336 if (logging != MagickFalse)
4337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004338 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4339 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004340
4341 if (length > PNG_UINT_31_MAX || count == 0)
4342 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004343
cristy3ed852e2009-09-05 21:47:34 +00004344 p=NULL;
4345 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00004346
cristy3ed852e2009-09-05 21:47:34 +00004347 if (length)
4348 {
4349 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00004350
cristy3ed852e2009-09-05 21:47:34 +00004351 if (chunk == (unsigned char *) NULL)
4352 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004353
cristybb503372010-05-27 20:51:26 +00004354 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004355 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00004356
cristy3ed852e2009-09-05 21:47:34 +00004357 p=chunk;
4358 }
glennrp47b9dd52010-11-24 18:12:06 +00004359
cristy3ed852e2009-09-05 21:47:34 +00004360 (void) ReadBlobMSBLong(image); /* read crc word */
4361
cristy3ed852e2009-09-05 21:47:34 +00004362 if (memcmp(type,mng_JHDR,4) == 0)
4363 {
4364 if (length == 16)
4365 {
cristybb503372010-05-27 20:51:26 +00004366 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristye2440c22014-05-18 12:54:47 +00004367 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00004368 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristye2440c22014-05-18 12:54:47 +00004369 (p[6] << 8) | p[7]);
4370 if ((jng_width == 0) || (jng_height == 0))
4371 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
cristy3ed852e2009-09-05 21:47:34 +00004372 jng_color_type=p[8];
4373 jng_image_sample_depth=p[9];
4374 jng_image_compression_method=p[10];
4375 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00004376
cristy3ed852e2009-09-05 21:47:34 +00004377 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4378 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00004379
cristy3ed852e2009-09-05 21:47:34 +00004380 jng_alpha_sample_depth=p[12];
4381 jng_alpha_compression_method=p[13];
4382 jng_alpha_filter_method=p[14];
4383 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00004384
cristy3ed852e2009-09-05 21:47:34 +00004385 if (logging != MagickFalse)
4386 {
4387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00004388 " jng_width: %16lu, jng_height: %16lu\n"
4389 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
cristy3ed852e2009-09-05 21:47:34 +00004390 " jng_image_compression_method:%3d",
glennrp2dd19062014-07-28 00:22:24 +00004391 (unsigned long) jng_width, (unsigned long) jng_height,
4392 jng_color_type, jng_image_sample_depth,
cristy3ed852e2009-09-05 21:47:34 +00004393 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004394
cristy3ed852e2009-09-05 21:47:34 +00004395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00004396 " jng_image_interlace_method: %3d"
cristy3ed852e2009-09-05 21:47:34 +00004397 " jng_alpha_sample_depth: %3d",
glennrp2dd19062014-07-28 00:22:24 +00004398 jng_image_interlace_method,
cristy3ed852e2009-09-05 21:47:34 +00004399 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004400
cristy3ed852e2009-09-05 21:47:34 +00004401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00004402 " jng_alpha_compression_method:%3d\n"
4403 " jng_alpha_filter_method: %3d\n"
cristy3ed852e2009-09-05 21:47:34 +00004404 " jng_alpha_interlace_method: %3d",
glennrp2dd19062014-07-28 00:22:24 +00004405 jng_alpha_compression_method,
4406 jng_alpha_filter_method,
cristy3ed852e2009-09-05 21:47:34 +00004407 jng_alpha_interlace_method);
4408 }
4409 }
glennrp47b9dd52010-11-24 18:12:06 +00004410
cristy3ed852e2009-09-05 21:47:34 +00004411 if (length)
4412 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004413
cristy3ed852e2009-09-05 21:47:34 +00004414 continue;
4415 }
4416
4417
4418 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4419 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4420 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4421 {
4422 /*
4423 o create color_image
4424 o open color_blob, attached to color_image
4425 o if (color type has alpha)
4426 open alpha_blob, attached to alpha_image
4427 */
4428
cristy73bd4a52010-10-05 11:24:23 +00004429 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004430
cristy3ed852e2009-09-05 21:47:34 +00004431 if (color_image_info == (ImageInfo *) NULL)
4432 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004433
cristy3ed852e2009-09-05 21:47:34 +00004434 GetImageInfo(color_image_info);
cristy16ea1392012-03-21 20:38:41 +00004435 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004436
cristy3ed852e2009-09-05 21:47:34 +00004437 if (color_image == (Image *) NULL)
4438 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4439
4440 if (logging != MagickFalse)
4441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4442 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004443
cristy3ed852e2009-09-05 21:47:34 +00004444 (void) AcquireUniqueFilename(color_image->filename);
4445 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4446 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004447
cristy3ed852e2009-09-05 21:47:34 +00004448 if (status == MagickFalse)
4449 return((Image *) NULL);
4450
4451 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4452 {
4453 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004454 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004455
cristy3ed852e2009-09-05 21:47:34 +00004456 if (alpha_image_info == (ImageInfo *) NULL)
4457 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004458
cristy3ed852e2009-09-05 21:47:34 +00004459 GetImageInfo(alpha_image_info);
cristy16ea1392012-03-21 20:38:41 +00004460 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004461
cristy3ed852e2009-09-05 21:47:34 +00004462 if (alpha_image == (Image *) NULL)
4463 {
4464 alpha_image=DestroyImage(alpha_image);
4465 ThrowReaderException(ResourceLimitError,
4466 "MemoryAllocationFailed");
4467 }
glennrp0fe50b42010-11-16 03:52:51 +00004468
cristy3ed852e2009-09-05 21:47:34 +00004469 if (logging != MagickFalse)
4470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4471 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004472
cristy3ed852e2009-09-05 21:47:34 +00004473 (void) AcquireUniqueFilename(alpha_image->filename);
4474 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4475 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004476
cristy3ed852e2009-09-05 21:47:34 +00004477 if (status == MagickFalse)
4478 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004479
cristy3ed852e2009-09-05 21:47:34 +00004480 if (jng_alpha_compression_method == 0)
4481 {
4482 unsigned char
4483 data[18];
4484
4485 if (logging != MagickFalse)
4486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4487 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004488
cristy3ed852e2009-09-05 21:47:34 +00004489 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4490 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004491
cristy3ed852e2009-09-05 21:47:34 +00004492 (void) WriteBlobMSBULong(alpha_image,13L);
4493 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004494 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004495 PNGLong(data+4,jng_width);
4496 PNGLong(data+8,jng_height);
4497 data[12]=jng_alpha_sample_depth;
4498 data[13]=0; /* color_type gray */
4499 data[14]=0; /* compression method 0 */
4500 data[15]=0; /* filter_method 0 */
4501 data[16]=0; /* interlace_method 0 */
4502 (void) WriteBlob(alpha_image,17,data);
4503 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4504 }
4505 }
4506 reading_idat=MagickTrue;
4507 }
4508
4509 if (memcmp(type,mng_JDAT,4) == 0)
4510 {
glennrp47b9dd52010-11-24 18:12:06 +00004511 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004512
4513 if (logging != MagickFalse)
4514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4515 " Copying JDAT chunk data to color_blob.");
4516
4517 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004518
cristy3ed852e2009-09-05 21:47:34 +00004519 if (length)
4520 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004521
cristy3ed852e2009-09-05 21:47:34 +00004522 continue;
4523 }
4524
4525 if (memcmp(type,mng_IDAT,4) == 0)
4526 {
4527 png_byte
4528 data[5];
4529
glennrp47b9dd52010-11-24 18:12:06 +00004530 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004531
4532 if (image_info->ping == MagickFalse)
4533 {
4534 if (logging != MagickFalse)
4535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4536 " Copying IDAT chunk data to alpha_blob.");
4537
cristybb503372010-05-27 20:51:26 +00004538 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004539 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004540 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004541 (void) WriteBlob(alpha_image,4,data);
4542 (void) WriteBlob(alpha_image,length,chunk);
4543 (void) WriteBlobMSBULong(alpha_image,
4544 crc32(crc32(0,data,4),chunk,(uInt) length));
4545 }
glennrp0fe50b42010-11-16 03:52:51 +00004546
cristy3ed852e2009-09-05 21:47:34 +00004547 if (length)
4548 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004549
cristy3ed852e2009-09-05 21:47:34 +00004550 continue;
4551 }
4552
4553 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4554 {
glennrp47b9dd52010-11-24 18:12:06 +00004555 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004556
4557 if (image_info->ping == MagickFalse)
4558 {
4559 if (logging != MagickFalse)
4560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4561 " Copying JDAA chunk data to alpha_blob.");
4562
4563 (void) WriteBlob(alpha_image,length,chunk);
4564 }
glennrp0fe50b42010-11-16 03:52:51 +00004565
cristy3ed852e2009-09-05 21:47:34 +00004566 if (length)
4567 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004568
cristy3ed852e2009-09-05 21:47:34 +00004569 continue;
4570 }
4571
4572 if (memcmp(type,mng_JSEP,4) == 0)
4573 {
4574 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004575
cristy3ed852e2009-09-05 21:47:34 +00004576 if (length)
4577 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004578
cristy3ed852e2009-09-05 21:47:34 +00004579 continue;
4580 }
4581
4582 if (memcmp(type,mng_bKGD,4) == 0)
4583 {
4584 if (length == 2)
4585 {
4586 image->background_color.red=ScaleCharToQuantum(p[1]);
4587 image->background_color.green=image->background_color.red;
4588 image->background_color.blue=image->background_color.red;
4589 }
glennrp0fe50b42010-11-16 03:52:51 +00004590
cristy3ed852e2009-09-05 21:47:34 +00004591 if (length == 6)
4592 {
4593 image->background_color.red=ScaleCharToQuantum(p[1]);
4594 image->background_color.green=ScaleCharToQuantum(p[3]);
4595 image->background_color.blue=ScaleCharToQuantum(p[5]);
4596 }
glennrp0fe50b42010-11-16 03:52:51 +00004597
cristy3ed852e2009-09-05 21:47:34 +00004598 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4599 continue;
4600 }
4601
4602 if (memcmp(type,mng_gAMA,4) == 0)
4603 {
4604 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004605 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004606
cristy3ed852e2009-09-05 21:47:34 +00004607 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4608 continue;
4609 }
4610
4611 if (memcmp(type,mng_cHRM,4) == 0)
4612 {
4613 if (length == 32)
4614 {
cristy8182b072010-05-30 20:10:53 +00004615 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4616 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4617 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4618 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4619 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4620 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4621 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4622 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004623 }
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_sRGB,4) == 0)
4630 {
4631 if (length == 1)
4632 {
glennrpe610a072010-08-05 17:08:46 +00004633 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004634 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristyda7803d2012-05-03 01:22:45 +00004635 image->gamma=1.000f/2.200f;
cristy3ed852e2009-09-05 21:47:34 +00004636 image->chromaticity.red_primary.x=0.6400f;
4637 image->chromaticity.red_primary.y=0.3300f;
4638 image->chromaticity.green_primary.x=0.3000f;
4639 image->chromaticity.green_primary.y=0.6000f;
4640 image->chromaticity.blue_primary.x=0.1500f;
4641 image->chromaticity.blue_primary.y=0.0600f;
4642 image->chromaticity.white_point.x=0.3127f;
4643 image->chromaticity.white_point.y=0.3290f;
4644 }
glennrp47b9dd52010-11-24 18:12:06 +00004645
cristy3ed852e2009-09-05 21:47:34 +00004646 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4647 continue;
4648 }
4649
4650 if (memcmp(type,mng_oFFs,4) == 0)
4651 {
4652 if (length > 8)
4653 {
glennrp5eae7602011-02-22 15:21:32 +00004654 image->page.x=(ssize_t) mng_get_long(p);
4655 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004656
cristy3ed852e2009-09-05 21:47:34 +00004657 if ((int) p[8] != 0)
4658 {
4659 image->page.x/=10000;
4660 image->page.y/=10000;
4661 }
4662 }
glennrp47b9dd52010-11-24 18:12:06 +00004663
cristy3ed852e2009-09-05 21:47:34 +00004664 if (length)
4665 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004666
cristy3ed852e2009-09-05 21:47:34 +00004667 continue;
4668 }
4669
4670 if (memcmp(type,mng_pHYs,4) == 0)
4671 {
4672 if (length > 8)
4673 {
cristy16ea1392012-03-21 20:38:41 +00004674 image->resolution.x=(double) mng_get_long(p);
4675 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004676 if ((int) p[8] == PNG_RESOLUTION_METER)
4677 {
4678 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00004679 image->resolution.x=image->resolution.x/100.0f;
4680 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004681 }
4682 }
glennrp0fe50b42010-11-16 03:52:51 +00004683
cristy3ed852e2009-09-05 21:47:34 +00004684 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4685 continue;
4686 }
4687
4688#if 0
4689 if (memcmp(type,mng_iCCP,4) == 0)
4690 {
glennrpfd05d622011-02-25 04:10:33 +00004691 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004692 if (length)
4693 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004694
cristy3ed852e2009-09-05 21:47:34 +00004695 continue;
4696 }
4697#endif
4698
4699 if (length)
4700 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4701
4702 if (memcmp(type,mng_IEND,4))
4703 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004704
cristy3ed852e2009-09-05 21:47:34 +00004705 break;
4706 }
4707
4708
4709 /* IEND found */
4710
4711 /*
4712 Finish up reading image data:
4713
4714 o read main image from color_blob.
4715
4716 o close color_blob.
4717
4718 o if (color_type has alpha)
4719 if alpha_encoding is PNG
4720 read secondary image from alpha_blob via ReadPNG
4721 if alpha_encoding is JPEG
4722 read secondary image from alpha_blob via ReadJPEG
4723
4724 o close alpha_blob.
4725
4726 o copy intensity of secondary image into
cristy16ea1392012-03-21 20:38:41 +00004727 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004728
4729 o destroy the secondary image.
4730 */
4731
4732 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004733
cristy3ed852e2009-09-05 21:47:34 +00004734 if (logging != MagickFalse)
4735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4736 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004737
cristyf0eec752014-01-16 01:28:43 +00004738 assert(color_image_info != (ImageInfo *) NULL);
cristy3b6fd2e2011-05-20 12:53:50 +00004739 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004740 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004741
cristy3ed852e2009-09-05 21:47:34 +00004742 color_image_info->ping=MagickFalse; /* To do: avoid this */
4743 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004744
cristy3ed852e2009-09-05 21:47:34 +00004745 if (jng_image == (Image *) NULL)
4746 return((Image *) NULL);
4747
4748 (void) RelinquishUniqueFileResource(color_image->filename);
4749 color_image=DestroyImage(color_image);
4750 color_image_info=DestroyImageInfo(color_image_info);
4751
4752 if (jng_image == (Image *) NULL)
4753 return((Image *) NULL);
4754
4755 if (logging != MagickFalse)
4756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4757 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004758
cristy3ed852e2009-09-05 21:47:34 +00004759 image->rows=jng_height;
4760 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004761
cristybb503372010-05-27 20:51:26 +00004762 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004763 {
cristy16ea1392012-03-21 20:38:41 +00004764 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004765 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00004766 for (x=(ssize_t) image->columns; x != 0; x--)
4767 {
4768 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4769 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4770 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4771 q+=GetPixelChannels(image);
4772 s+=GetPixelChannels(jng_image);
4773 }
glennrp47b9dd52010-11-24 18:12:06 +00004774
cristy3ed852e2009-09-05 21:47:34 +00004775 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4776 break;
4777 }
glennrp0fe50b42010-11-16 03:52:51 +00004778
cristy3ed852e2009-09-05 21:47:34 +00004779 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004780
cristy3ed852e2009-09-05 21:47:34 +00004781 if (image_info->ping == MagickFalse)
4782 {
4783 if (jng_color_type >= 12)
4784 {
4785 if (jng_alpha_compression_method == 0)
4786 {
4787 png_byte
4788 data[5];
4789 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4790 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004791 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004792 (void) WriteBlob(alpha_image,4,data);
4793 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4794 }
glennrp0fe50b42010-11-16 03:52:51 +00004795
cristy3ed852e2009-09-05 21:47:34 +00004796 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004797
cristy3ed852e2009-09-05 21:47:34 +00004798 if (logging != MagickFalse)
4799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00004800 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004801
cristy3b6fd2e2011-05-20 12:53:50 +00004802 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004803 "%s",alpha_image->filename);
4804
4805 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004806
cristy3ed852e2009-09-05 21:47:34 +00004807 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004808 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004809 {
4810 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy16ea1392012-03-21 20:38:41 +00004811 exception);
cristy3ed852e2009-09-05 21:47:34 +00004812 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004813
cristy8a46d822012-08-28 23:32:39 +00004814 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00004815 for (x=(ssize_t) image->columns; x != 0; x--)
4816 {
4817 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4818 q+=GetPixelChannels(image);
4819 s+=GetPixelChannels(jng_image);
4820 }
glennrp0fe50b42010-11-16 03:52:51 +00004821
cristy3ed852e2009-09-05 21:47:34 +00004822 else
cristy16ea1392012-03-21 20:38:41 +00004823 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004824 {
cristy16ea1392012-03-21 20:38:41 +00004825 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4826 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy8a46d822012-08-28 23:32:39 +00004827 image->alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00004828 q+=GetPixelChannels(image);
4829 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004830 }
glennrp0fe50b42010-11-16 03:52:51 +00004831
cristy3ed852e2009-09-05 21:47:34 +00004832 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4833 break;
4834 }
4835 (void) RelinquishUniqueFileResource(alpha_image->filename);
4836 alpha_image=DestroyImage(alpha_image);
4837 alpha_image_info=DestroyImageInfo(alpha_image_info);
4838 if (jng_image != (Image *) NULL)
4839 jng_image=DestroyImage(jng_image);
4840 }
4841 }
4842
glennrp47b9dd52010-11-24 18:12:06 +00004843 /* Read the JNG image. */
4844
cristy3ed852e2009-09-05 21:47:34 +00004845 if (mng_info->mng_type == 0)
4846 {
4847 mng_info->mng_width=jng_width;
4848 mng_info->mng_height=jng_height;
4849 }
glennrp0fe50b42010-11-16 03:52:51 +00004850
cristy3ed852e2009-09-05 21:47:34 +00004851 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004852 {
4853 image->page.width=jng_width;
4854 image->page.height=jng_height;
4855 }
4856
cristy3ed852e2009-09-05 21:47:34 +00004857 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004858 {
4859 image->page.x=mng_info->x_off[mng_info->object_id];
4860 image->page.y=mng_info->y_off[mng_info->object_id];
4861 }
4862
cristy3ed852e2009-09-05 21:47:34 +00004863 else
glennrp0fe50b42010-11-16 03:52:51 +00004864 {
4865 image->page.y=mng_info->y_off[mng_info->object_id];
4866 }
4867
cristy3ed852e2009-09-05 21:47:34 +00004868 mng_info->image_found++;
4869 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4870 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004871
cristy3ed852e2009-09-05 21:47:34 +00004872 if (logging != MagickFalse)
4873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4874 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004875
cristy3ed852e2009-09-05 21:47:34 +00004876 return(image);
4877}
4878
4879/*
4880%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4881% %
4882% %
4883% %
4884% R e a d J N G I m a g e %
4885% %
4886% %
4887% %
4888%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4889%
4890% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4891% (including the 8-byte signature) and returns it. It allocates the memory
4892% necessary for the new Image structure and returns a pointer to the new
4893% image.
4894%
4895% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4896%
4897% The format of the ReadJNGImage method is:
4898%
4899% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4900% *exception)
4901%
4902% A description of each parameter follows:
4903%
4904% o image_info: the image info.
4905%
4906% o exception: return any errors or warnings in this structure.
4907%
4908*/
4909
4910static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4911{
4912 Image
4913 *image,
4914 *previous;
4915
4916 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004917 have_mng_structure,
4918 logging,
cristy3ed852e2009-09-05 21:47:34 +00004919 status;
4920
4921 MngInfo
4922 *mng_info;
4923
4924 char
4925 magic_number[MaxTextExtent];
4926
cristy3ed852e2009-09-05 21:47:34 +00004927 size_t
4928 count;
4929
4930 /*
4931 Open image file.
4932 */
4933 assert(image_info != (const ImageInfo *) NULL);
4934 assert(image_info->signature == MagickSignature);
4935 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4936 assert(exception != (ExceptionInfo *) NULL);
4937 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004938 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004939 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004940 mng_info=(MngInfo *) NULL;
4941 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004942
cristy3ed852e2009-09-05 21:47:34 +00004943 if (status == MagickFalse)
4944 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004945
cristy3ed852e2009-09-05 21:47:34 +00004946 if (LocaleCompare(image_info->magick,"JNG") != 0)
4947 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004948
glennrp47b9dd52010-11-24 18:12:06 +00004949 /* Verify JNG signature. */
4950
cristy3ed852e2009-09-05 21:47:34 +00004951 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004952
glennrp3b8763e2011-02-21 12:08:18 +00004953 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004954 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004955
glennrp47b9dd52010-11-24 18:12:06 +00004956 /* Allocate a MngInfo structure. */
4957
cristy3ed852e2009-09-05 21:47:34 +00004958 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004959 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004960
cristy3ed852e2009-09-05 21:47:34 +00004961 if (mng_info == (MngInfo *) NULL)
4962 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004963
glennrp47b9dd52010-11-24 18:12:06 +00004964 /* Initialize members of the MngInfo structure. */
4965
cristy3ed852e2009-09-05 21:47:34 +00004966 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4967 have_mng_structure=MagickTrue;
4968
4969 mng_info->image=image;
4970 previous=image;
4971 image=ReadOneJNGImage(mng_info,image_info,exception);
4972 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004973
cristy3ed852e2009-09-05 21:47:34 +00004974 if (image == (Image *) NULL)
4975 {
4976 if (IsImageObject(previous) != MagickFalse)
4977 {
4978 (void) CloseBlob(previous);
4979 (void) DestroyImageList(previous);
4980 }
glennrp0fe50b42010-11-16 03:52:51 +00004981
cristy3ed852e2009-09-05 21:47:34 +00004982 if (logging != MagickFalse)
4983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4984 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004985
cristy3ed852e2009-09-05 21:47:34 +00004986 return((Image *) NULL);
4987 }
4988 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004989
cristy3ed852e2009-09-05 21:47:34 +00004990 if (image->columns == 0 || image->rows == 0)
4991 {
4992 if (logging != MagickFalse)
4993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4994 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004995
cristy3ed852e2009-09-05 21:47:34 +00004996 ThrowReaderException(CorruptImageError,"CorruptImage");
4997 }
glennrp0fe50b42010-11-16 03:52:51 +00004998
cristy3ed852e2009-09-05 21:47:34 +00004999 if (logging != MagickFalse)
5000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00005001
cristy3ed852e2009-09-05 21:47:34 +00005002 return(image);
5003}
5004#endif
5005
5006static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
5007{
5008 char
5009 page_geometry[MaxTextExtent];
5010
5011 Image
5012 *image,
5013 *previous;
5014
cristy4383ec82011-01-05 15:42:32 +00005015 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00005016 logging,
5017 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00005018
cristy3ed852e2009-09-05 21:47:34 +00005019 volatile int
5020 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00005021 object_id,
5022 term_chunk_found,
5023 skip_to_iend;
5024
cristybb503372010-05-27 20:51:26 +00005025 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005026 image_count=0;
5027
5028 MagickBooleanType
5029 status;
5030
5031 MagickOffsetType
5032 offset;
5033
5034 MngInfo
5035 *mng_info;
5036
5037 MngBox
5038 default_fb,
5039 fb,
5040 previous_fb;
5041
5042#if defined(MNG_INSERT_LAYERS)
cristy16ea1392012-03-21 20:38:41 +00005043 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00005044 mng_background_color;
5045#endif
5046
5047 register unsigned char
5048 *p;
5049
cristybb503372010-05-27 20:51:26 +00005050 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005051 i;
5052
5053 size_t
5054 count;
5055
cristybb503372010-05-27 20:51:26 +00005056 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005057 loop_level;
5058
5059 volatile short
5060 skipping_loop;
5061
5062#if defined(MNG_INSERT_LAYERS)
5063 unsigned int
5064 mandatory_back=0;
5065#endif
5066
5067 volatile unsigned int
5068#ifdef MNG_OBJECT_BUFFERS
5069 mng_background_object=0,
5070#endif
5071 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5072
cristybb503372010-05-27 20:51:26 +00005073 size_t
cristy3ed852e2009-09-05 21:47:34 +00005074 default_frame_timeout,
5075 frame_timeout,
5076#if defined(MNG_INSERT_LAYERS)
5077 image_height,
5078 image_width,
5079#endif
5080 length;
5081
glennrp38ea0832010-06-02 18:50:28 +00005082 /* These delays are all measured in image ticks_per_second,
5083 * not in MNG ticks_per_second
5084 */
cristybb503372010-05-27 20:51:26 +00005085 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00005086 default_frame_delay,
5087 final_delay,
5088 final_image_delay,
5089 frame_delay,
5090#if defined(MNG_INSERT_LAYERS)
5091 insert_layers,
5092#endif
5093 mng_iterations=1,
5094 simplicity=0,
5095 subframe_height=0,
5096 subframe_width=0;
5097
5098 previous_fb.top=0;
5099 previous_fb.bottom=0;
5100 previous_fb.left=0;
5101 previous_fb.right=0;
5102 default_fb.top=0;
5103 default_fb.bottom=0;
5104 default_fb.left=0;
5105 default_fb.right=0;
5106
glennrp47b9dd52010-11-24 18:12:06 +00005107 /* Open image file. */
5108
cristy3ed852e2009-09-05 21:47:34 +00005109 assert(image_info != (const ImageInfo *) NULL);
5110 assert(image_info->signature == MagickSignature);
5111 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
5112 assert(exception != (ExceptionInfo *) NULL);
5113 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00005114 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy16ea1392012-03-21 20:38:41 +00005115 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005116 mng_info=(MngInfo *) NULL;
5117 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005118
cristy3ed852e2009-09-05 21:47:34 +00005119 if (status == MagickFalse)
5120 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00005121
cristy3ed852e2009-09-05 21:47:34 +00005122 first_mng_object=MagickFalse;
5123 skipping_loop=(-1);
5124 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005125
5126 /* Allocate a MngInfo structure. */
5127
cristy73bd4a52010-10-05 11:24:23 +00005128 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00005129
cristy3ed852e2009-09-05 21:47:34 +00005130 if (mng_info == (MngInfo *) NULL)
5131 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00005132
glennrp47b9dd52010-11-24 18:12:06 +00005133 /* Initialize members of the MngInfo structure. */
5134
cristy3ed852e2009-09-05 21:47:34 +00005135 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5136 mng_info->image=image;
5137 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00005138
5139 if (LocaleCompare(image_info->magick,"MNG") == 0)
5140 {
5141 char
5142 magic_number[MaxTextExtent];
5143
glennrp47b9dd52010-11-24 18:12:06 +00005144 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00005145 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5146 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5147 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005148
5149 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00005150 for (i=0; i < MNG_MAX_OBJECTS; i++)
5151 {
cristybb503372010-05-27 20:51:26 +00005152 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5153 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00005154 }
5155 mng_info->exists[0]=MagickTrue;
5156 }
glennrp47b9dd52010-11-24 18:12:06 +00005157
cristy3ed852e2009-09-05 21:47:34 +00005158 first_mng_object=MagickTrue;
5159 mng_type=0;
5160#if defined(MNG_INSERT_LAYERS)
5161 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5162#endif
5163 default_frame_delay=0;
5164 default_frame_timeout=0;
5165 frame_delay=0;
5166 final_delay=1;
5167 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5168 object_id=0;
5169 skip_to_iend=MagickFalse;
5170 term_chunk_found=MagickFalse;
5171 mng_info->framing_mode=1;
5172#if defined(MNG_INSERT_LAYERS)
5173 mandatory_back=MagickFalse;
5174#endif
5175#if defined(MNG_INSERT_LAYERS)
5176 mng_background_color=image->background_color;
5177#endif
5178 default_fb=mng_info->frame;
5179 previous_fb=mng_info->frame;
5180 do
5181 {
5182 char
5183 type[MaxTextExtent];
5184
5185 if (LocaleCompare(image_info->magick,"MNG") == 0)
5186 {
5187 unsigned char
5188 *chunk;
5189
5190 /*
5191 Read a new chunk.
5192 */
5193 type[0]='\0';
5194 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5195 length=ReadBlobMSBLong(image);
5196 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5197
5198 if (logging != MagickFalse)
5199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005200 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5201 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00005202
5203 if (length > PNG_UINT_31_MAX)
5204 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005205
cristy3ed852e2009-09-05 21:47:34 +00005206 if (count == 0)
5207 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00005208
cristy3ed852e2009-09-05 21:47:34 +00005209 p=NULL;
5210 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00005211
cristy3ed852e2009-09-05 21:47:34 +00005212 if (length)
5213 {
5214 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00005215
cristy3ed852e2009-09-05 21:47:34 +00005216 if (chunk == (unsigned char *) NULL)
5217 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00005218
cristybb503372010-05-27 20:51:26 +00005219 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005220 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00005221
cristy3ed852e2009-09-05 21:47:34 +00005222 p=chunk;
5223 }
glennrp0fe50b42010-11-16 03:52:51 +00005224
cristy3ed852e2009-09-05 21:47:34 +00005225 (void) ReadBlobMSBLong(image); /* read crc word */
5226
5227#if !defined(JNG_SUPPORTED)
5228 if (memcmp(type,mng_JHDR,4) == 0)
5229 {
5230 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005231
cristy3ed852e2009-09-05 21:47:34 +00005232 if (mng_info->jhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005233 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005234 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005235
cristy3ed852e2009-09-05 21:47:34 +00005236 mng_info->jhdr_warning++;
5237 }
5238#endif
5239 if (memcmp(type,mng_DHDR,4) == 0)
5240 {
5241 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005242
cristy3ed852e2009-09-05 21:47:34 +00005243 if (mng_info->dhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005244 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005245 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005246
cristy3ed852e2009-09-05 21:47:34 +00005247 mng_info->dhdr_warning++;
5248 }
5249 if (memcmp(type,mng_MEND,4) == 0)
5250 break;
glennrp47b9dd52010-11-24 18:12:06 +00005251
cristy3ed852e2009-09-05 21:47:34 +00005252 if (skip_to_iend)
5253 {
5254 if (memcmp(type,mng_IEND,4) == 0)
5255 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005256
cristy3ed852e2009-09-05 21:47:34 +00005257 if (length)
5258 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005259
cristy3ed852e2009-09-05 21:47:34 +00005260 if (logging != MagickFalse)
5261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5262 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00005263
cristy3ed852e2009-09-05 21:47:34 +00005264 continue;
5265 }
glennrp0fe50b42010-11-16 03:52:51 +00005266
cristy3ed852e2009-09-05 21:47:34 +00005267 if (memcmp(type,mng_MHDR,4) == 0)
5268 {
cristybb503372010-05-27 20:51:26 +00005269 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005270 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00005271
cristybb503372010-05-27 20:51:26 +00005272 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005273 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00005274
cristy3ed852e2009-09-05 21:47:34 +00005275 if (logging != MagickFalse)
5276 {
5277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005278 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00005279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005280 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005281 }
glennrp0fe50b42010-11-16 03:52:51 +00005282
cristy3ed852e2009-09-05 21:47:34 +00005283 p+=8;
cristy8182b072010-05-30 20:10:53 +00005284 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005285
cristy3ed852e2009-09-05 21:47:34 +00005286 if (mng_info->ticks_per_second == 0)
5287 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005288
cristy3ed852e2009-09-05 21:47:34 +00005289 else
5290 default_frame_delay=1UL*image->ticks_per_second/
5291 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005292
cristy3ed852e2009-09-05 21:47:34 +00005293 frame_delay=default_frame_delay;
5294 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00005295
cristy3ed852e2009-09-05 21:47:34 +00005296 if (length > 16)
5297 {
5298 p+=16;
cristy8182b072010-05-30 20:10:53 +00005299 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005300 }
glennrp0fe50b42010-11-16 03:52:51 +00005301
cristy3ed852e2009-09-05 21:47:34 +00005302 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00005303
cristy3ed852e2009-09-05 21:47:34 +00005304 if ((simplicity != 0) && ((simplicity | 11) == 11))
5305 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00005306
cristy3ed852e2009-09-05 21:47:34 +00005307 if ((simplicity != 0) && ((simplicity | 9) == 9))
5308 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00005309
cristy3ed852e2009-09-05 21:47:34 +00005310#if defined(MNG_INSERT_LAYERS)
5311 if (mng_type != 3)
5312 insert_layers=MagickTrue;
5313#endif
cristy16ea1392012-03-21 20:38:41 +00005314 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005315 {
glennrp47b9dd52010-11-24 18:12:06 +00005316 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005317 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005318
cristy3ed852e2009-09-05 21:47:34 +00005319 if (GetNextImageInList(image) == (Image *) NULL)
5320 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00005321
cristy3ed852e2009-09-05 21:47:34 +00005322 image=SyncNextImageInList(image);
5323 mng_info->image=image;
5324 }
5325
5326 if ((mng_info->mng_width > 65535L) ||
5327 (mng_info->mng_height > 65535L))
5328 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00005329
cristy3b6fd2e2011-05-20 12:53:50 +00005330 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00005331 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00005332 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00005333
cristy3ed852e2009-09-05 21:47:34 +00005334 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00005335 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00005336 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00005337 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00005338 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005339
cristy3ed852e2009-09-05 21:47:34 +00005340 for (i=0; i < MNG_MAX_OBJECTS; i++)
5341 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005342
cristy3ed852e2009-09-05 21:47:34 +00005343 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5344 continue;
5345 }
5346
5347 if (memcmp(type,mng_TERM,4) == 0)
5348 {
5349 int
5350 repeat=0;
5351
5352
5353 if (length)
5354 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005355
cristy3ed852e2009-09-05 21:47:34 +00005356 if (repeat == 3)
5357 {
cristy8182b072010-05-30 20:10:53 +00005358 final_delay=(png_uint_32) mng_get_long(&p[2]);
5359 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00005360
cristy3ed852e2009-09-05 21:47:34 +00005361 if (mng_iterations == PNG_UINT_31_MAX)
5362 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00005363
cristy3ed852e2009-09-05 21:47:34 +00005364 image->iterations=mng_iterations;
5365 term_chunk_found=MagickTrue;
5366 }
glennrp0fe50b42010-11-16 03:52:51 +00005367
cristy3ed852e2009-09-05 21:47:34 +00005368 if (logging != MagickFalse)
5369 {
5370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp280283d2014-08-01 15:02:04 +00005371 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5372 repeat,(double) final_delay, (double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00005373 }
glennrp0fe50b42010-11-16 03:52:51 +00005374
cristy3ed852e2009-09-05 21:47:34 +00005375 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5376 continue;
5377 }
5378 if (memcmp(type,mng_DEFI,4) == 0)
5379 {
5380 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005381 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005382 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5383 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005384
cristy3ed852e2009-09-05 21:47:34 +00005385 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005386
cristy3ed852e2009-09-05 21:47:34 +00005387 if (mng_type == 2 && object_id != 0)
cristy16ea1392012-03-21 20:38:41 +00005388 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005389 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5390 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005391
cristy3ed852e2009-09-05 21:47:34 +00005392 if (object_id > MNG_MAX_OBJECTS)
5393 {
5394 /*
glennrpedaa0382012-04-12 14:16:21 +00005395 Instead of using a warning we should allocate a larger
cristy3ed852e2009-09-05 21:47:34 +00005396 MngInfo structure and continue.
5397 */
cristy16ea1392012-03-21 20:38:41 +00005398 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005399 CoderError,"object id too large","`%s'",image->filename);
5400 object_id=MNG_MAX_OBJECTS;
5401 }
glennrp0fe50b42010-11-16 03:52:51 +00005402
cristy3ed852e2009-09-05 21:47:34 +00005403 if (mng_info->exists[object_id])
5404 if (mng_info->frozen[object_id])
5405 {
5406 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristy16ea1392012-03-21 20:38:41 +00005407 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005408 GetMagickModule(),CoderError,
5409 "DEFI cannot redefine a frozen MNG object","`%s'",
5410 image->filename);
5411 continue;
5412 }
glennrp0fe50b42010-11-16 03:52:51 +00005413
cristy3ed852e2009-09-05 21:47:34 +00005414 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005415
cristy3ed852e2009-09-05 21:47:34 +00005416 if (length > 2)
5417 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005418
cristy3ed852e2009-09-05 21:47:34 +00005419 /*
5420 Extract object offset info.
5421 */
5422 if (length > 11)
5423 {
glennrp0fe50b42010-11-16 03:52:51 +00005424 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5425 (p[5] << 16) | (p[6] << 8) | p[7]);
5426
5427 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5428 (p[9] << 16) | (p[10] << 8) | p[11]);
5429
cristy3ed852e2009-09-05 21:47:34 +00005430 if (logging != MagickFalse)
5431 {
5432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp280283d2014-08-01 15:02:04 +00005433 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5434 object_id,(double) mng_info->x_off[object_id],
5435 object_id,(double) mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005436 }
5437 }
glennrp0fe50b42010-11-16 03:52:51 +00005438
cristy3ed852e2009-09-05 21:47:34 +00005439 /*
5440 Extract object clipping info.
5441 */
5442 if (length > 27)
5443 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5444 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005445
cristy3ed852e2009-09-05 21:47:34 +00005446 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5447 continue;
5448 }
5449 if (memcmp(type,mng_bKGD,4) == 0)
5450 {
5451 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005452
cristy3ed852e2009-09-05 21:47:34 +00005453 if (length > 5)
5454 {
5455 mng_info->mng_global_bkgd.red=
5456 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005457
cristy3ed852e2009-09-05 21:47:34 +00005458 mng_info->mng_global_bkgd.green=
5459 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005460
cristy3ed852e2009-09-05 21:47:34 +00005461 mng_info->mng_global_bkgd.blue=
5462 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005463
cristy3ed852e2009-09-05 21:47:34 +00005464 mng_info->have_global_bkgd=MagickTrue;
5465 }
glennrp0fe50b42010-11-16 03:52:51 +00005466
cristy3ed852e2009-09-05 21:47:34 +00005467 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5468 continue;
5469 }
5470 if (memcmp(type,mng_BACK,4) == 0)
5471 {
5472#if defined(MNG_INSERT_LAYERS)
5473 if (length > 6)
5474 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005475
cristy3ed852e2009-09-05 21:47:34 +00005476 else
5477 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005478
cristy3ed852e2009-09-05 21:47:34 +00005479 if (mandatory_back && length > 5)
5480 {
5481 mng_background_color.red=
5482 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005483
cristy3ed852e2009-09-05 21:47:34 +00005484 mng_background_color.green=
5485 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005486
cristy3ed852e2009-09-05 21:47:34 +00005487 mng_background_color.blue=
5488 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005489
cristy16ea1392012-03-21 20:38:41 +00005490 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005491 }
glennrp0fe50b42010-11-16 03:52:51 +00005492
cristy3ed852e2009-09-05 21:47:34 +00005493#ifdef MNG_OBJECT_BUFFERS
5494 if (length > 8)
5495 mng_background_object=(p[7] << 8) | p[8];
5496#endif
5497#endif
5498 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5499 continue;
5500 }
glennrp47b9dd52010-11-24 18:12:06 +00005501
cristy3ed852e2009-09-05 21:47:34 +00005502 if (memcmp(type,mng_PLTE,4) == 0)
5503 {
glennrp47b9dd52010-11-24 18:12:06 +00005504 /* Read global PLTE. */
5505
cristy3ed852e2009-09-05 21:47:34 +00005506 if (length && (length < 769))
5507 {
5508 if (mng_info->global_plte == (png_colorp) NULL)
5509 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5510 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005511
cristybb503372010-05-27 20:51:26 +00005512 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005513 {
5514 mng_info->global_plte[i].red=p[3*i];
5515 mng_info->global_plte[i].green=p[3*i+1];
5516 mng_info->global_plte[i].blue=p[3*i+2];
5517 }
glennrp0fe50b42010-11-16 03:52:51 +00005518
cristy35ef8242010-06-03 16:24:13 +00005519 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005520 }
5521#ifdef MNG_LOOSE
5522 for ( ; i < 256; i++)
5523 {
5524 mng_info->global_plte[i].red=i;
5525 mng_info->global_plte[i].green=i;
5526 mng_info->global_plte[i].blue=i;
5527 }
glennrp0fe50b42010-11-16 03:52:51 +00005528
cristy3ed852e2009-09-05 21:47:34 +00005529 if (length)
5530 mng_info->global_plte_length=256;
5531#endif
5532 else
5533 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005534
cristy3ed852e2009-09-05 21:47:34 +00005535 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5536 continue;
5537 }
glennrp47b9dd52010-11-24 18:12:06 +00005538
cristy3ed852e2009-09-05 21:47:34 +00005539 if (memcmp(type,mng_tRNS,4) == 0)
5540 {
5541 /* read global tRNS */
5542
5543 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005544 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005545 mng_info->global_trns[i]=p[i];
5546
5547#ifdef MNG_LOOSE
5548 for ( ; i < 256; i++)
5549 mng_info->global_trns[i]=255;
5550#endif
cristy12560f32010-06-03 16:51:08 +00005551 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005552 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5553 continue;
5554 }
5555 if (memcmp(type,mng_gAMA,4) == 0)
5556 {
5557 if (length == 4)
5558 {
cristybb503372010-05-27 20:51:26 +00005559 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005560 igamma;
5561
cristy8182b072010-05-30 20:10:53 +00005562 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005563 mng_info->global_gamma=((float) igamma)*0.00001;
5564 mng_info->have_global_gama=MagickTrue;
5565 }
glennrp0fe50b42010-11-16 03:52:51 +00005566
cristy3ed852e2009-09-05 21:47:34 +00005567 else
5568 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005569
cristy3ed852e2009-09-05 21:47:34 +00005570 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5571 continue;
5572 }
5573
5574 if (memcmp(type,mng_cHRM,4) == 0)
5575 {
glennrp47b9dd52010-11-24 18:12:06 +00005576 /* Read global cHRM */
5577
cristy3ed852e2009-09-05 21:47:34 +00005578 if (length == 32)
5579 {
cristy8182b072010-05-30 20:10:53 +00005580 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5581 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5582 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005583 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005584 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005585 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005586 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005587 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005588 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005589 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005590 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005591 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005592 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005593 mng_info->have_global_chrm=MagickTrue;
5594 }
5595 else
5596 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005597
cristy3ed852e2009-09-05 21:47:34 +00005598 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5599 continue;
5600 }
glennrp47b9dd52010-11-24 18:12:06 +00005601
cristy3ed852e2009-09-05 21:47:34 +00005602 if (memcmp(type,mng_sRGB,4) == 0)
5603 {
5604 /*
5605 Read global sRGB.
5606 */
5607 if (length)
5608 {
glennrpe610a072010-08-05 17:08:46 +00005609 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005610 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005611 mng_info->have_global_srgb=MagickTrue;
5612 }
5613 else
5614 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005615
cristy3ed852e2009-09-05 21:47:34 +00005616 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5617 continue;
5618 }
glennrp47b9dd52010-11-24 18:12:06 +00005619
cristy3ed852e2009-09-05 21:47:34 +00005620 if (memcmp(type,mng_iCCP,4) == 0)
5621 {
glennrpfd05d622011-02-25 04:10:33 +00005622 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005623
5624 /*
5625 Read global iCCP.
5626 */
5627 if (length)
5628 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005629
cristy3ed852e2009-09-05 21:47:34 +00005630 continue;
5631 }
glennrp47b9dd52010-11-24 18:12:06 +00005632
cristy3ed852e2009-09-05 21:47:34 +00005633 if (memcmp(type,mng_FRAM,4) == 0)
5634 {
5635 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005636 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005637 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5638 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005639
cristy3ed852e2009-09-05 21:47:34 +00005640 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5641 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005642
cristy3ed852e2009-09-05 21:47:34 +00005643 frame_delay=default_frame_delay;
5644 frame_timeout=default_frame_timeout;
5645 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005646
cristy3ed852e2009-09-05 21:47:34 +00005647 if (length)
5648 if (p[0])
5649 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005650
cristy3ed852e2009-09-05 21:47:34 +00005651 if (logging != MagickFalse)
5652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5653 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005654
cristy3ed852e2009-09-05 21:47:34 +00005655 if (length > 6)
5656 {
glennrp47b9dd52010-11-24 18:12:06 +00005657 /* Note the delay and frame clipping boundaries. */
5658
cristy3ed852e2009-09-05 21:47:34 +00005659 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005660
cristybb503372010-05-27 20:51:26 +00005661 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005662 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005663
cristy3ed852e2009-09-05 21:47:34 +00005664 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005665
cristybb503372010-05-27 20:51:26 +00005666 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005667 {
5668 int
5669 change_delay,
5670 change_timeout,
5671 change_clipping;
5672
5673 change_delay=(*p++);
5674 change_timeout=(*p++);
5675 change_clipping=(*p++);
5676 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005677
cristy3ed852e2009-09-05 21:47:34 +00005678 if (change_delay)
5679 {
cristy8182b072010-05-30 20:10:53 +00005680 frame_delay=1UL*image->ticks_per_second*
5681 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005682
cristy8182b072010-05-30 20:10:53 +00005683 if (mng_info->ticks_per_second != 0)
5684 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005685
glennrpbb010dd2010-06-01 13:07:15 +00005686 else
5687 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005688
cristy3ed852e2009-09-05 21:47:34 +00005689 if (change_delay == 2)
5690 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005691
cristy3ed852e2009-09-05 21:47:34 +00005692 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005693
cristy3ed852e2009-09-05 21:47:34 +00005694 if (logging != MagickFalse)
5695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005696 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005697 }
glennrp47b9dd52010-11-24 18:12:06 +00005698
cristy3ed852e2009-09-05 21:47:34 +00005699 if (change_timeout)
5700 {
glennrpbb010dd2010-06-01 13:07:15 +00005701 frame_timeout=1UL*image->ticks_per_second*
5702 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005703
glennrpbb010dd2010-06-01 13:07:15 +00005704 if (mng_info->ticks_per_second != 0)
5705 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005706
glennrpbb010dd2010-06-01 13:07:15 +00005707 else
5708 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 if (change_delay == 2)
5711 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005712
cristy3ed852e2009-09-05 21:47:34 +00005713 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005714
cristy3ed852e2009-09-05 21:47:34 +00005715 if (logging != MagickFalse)
5716 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005717 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005718 }
glennrp47b9dd52010-11-24 18:12:06 +00005719
cristy3ed852e2009-09-05 21:47:34 +00005720 if (change_clipping)
5721 {
5722 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5723 p+=17;
5724 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005725
cristy3ed852e2009-09-05 21:47:34 +00005726 if (logging != MagickFalse)
5727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005728 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005729 (double) fb.left,(double) fb.right,(double) fb.top,
5730 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005731
cristy3ed852e2009-09-05 21:47:34 +00005732 if (change_clipping == 2)
5733 default_fb=fb;
5734 }
5735 }
5736 }
5737 mng_info->clip=fb;
5738 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005739
cristybb503372010-05-27 20:51:26 +00005740 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005741 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005742
cristybb503372010-05-27 20:51:26 +00005743 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005744 -mng_info->clip.top);
5745 /*
5746 Insert a background layer behind the frame if framing_mode is 4.
5747 */
5748#if defined(MNG_INSERT_LAYERS)
5749 if (logging != MagickFalse)
5750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005751 " subframe_width=%.20g, subframe_height=%.20g",(double)
5752 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005753
cristy3ed852e2009-09-05 21:47:34 +00005754 if (insert_layers && (mng_info->framing_mode == 4) &&
5755 (subframe_width) && (subframe_height))
5756 {
glennrp47b9dd52010-11-24 18:12:06 +00005757 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005758 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005759 {
cristy16ea1392012-03-21 20:38:41 +00005760 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005761
cristy3ed852e2009-09-05 21:47:34 +00005762 if (GetNextImageInList(image) == (Image *) NULL)
5763 {
5764 image=DestroyImageList(image);
5765 MngInfoFreeStruct(mng_info,&have_mng_structure);
5766 return((Image *) NULL);
5767 }
glennrp47b9dd52010-11-24 18:12:06 +00005768
cristy3ed852e2009-09-05 21:47:34 +00005769 image=SyncNextImageInList(image);
5770 }
glennrp0fe50b42010-11-16 03:52:51 +00005771
cristy3ed852e2009-09-05 21:47:34 +00005772 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005773
cristy3ed852e2009-09-05 21:47:34 +00005774 if (term_chunk_found)
5775 {
5776 image->start_loop=MagickTrue;
5777 image->iterations=mng_iterations;
5778 term_chunk_found=MagickFalse;
5779 }
glennrp0fe50b42010-11-16 03:52:51 +00005780
cristy3ed852e2009-09-05 21:47:34 +00005781 else
5782 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005783
cristy3ed852e2009-09-05 21:47:34 +00005784 image->columns=subframe_width;
5785 image->rows=subframe_height;
5786 image->page.width=subframe_width;
5787 image->page.height=subframe_height;
5788 image->page.x=mng_info->clip.left;
5789 image->page.y=mng_info->clip.top;
5790 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00005791 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00005792 image->delay=0;
cristy16ea1392012-03-21 20:38:41 +00005793 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005794
cristy3ed852e2009-09-05 21:47:34 +00005795 if (logging != MagickFalse)
5796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005797 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005798 (double) mng_info->clip.left,(double) mng_info->clip.right,
5799 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005800 }
5801#endif
5802 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5803 continue;
5804 }
5805 if (memcmp(type,mng_CLIP,4) == 0)
5806 {
5807 unsigned int
5808 first_object,
5809 last_object;
5810
5811 /*
5812 Read CLIP.
5813 */
5814 first_object=(p[0] << 8) | p[1];
5815 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 for (i=(int) first_object; i <= (int) last_object; i++)
5818 {
5819 if (mng_info->exists[i] && !mng_info->frozen[i])
5820 {
5821 MngBox
5822 box;
5823
5824 box=mng_info->object_clip[i];
5825 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5826 }
5827 }
glennrp47b9dd52010-11-24 18:12:06 +00005828
cristy3ed852e2009-09-05 21:47:34 +00005829 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5830 continue;
5831 }
5832 if (memcmp(type,mng_SAVE,4) == 0)
5833 {
5834 for (i=1; i < MNG_MAX_OBJECTS; i++)
5835 if (mng_info->exists[i])
5836 {
5837 mng_info->frozen[i]=MagickTrue;
5838#ifdef MNG_OBJECT_BUFFERS
5839 if (mng_info->ob[i] != (MngBuffer *) NULL)
5840 mng_info->ob[i]->frozen=MagickTrue;
5841#endif
5842 }
glennrp0fe50b42010-11-16 03:52:51 +00005843
cristy3ed852e2009-09-05 21:47:34 +00005844 if (length)
5845 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005846
cristy3ed852e2009-09-05 21:47:34 +00005847 continue;
5848 }
5849
5850 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5851 {
glennrp47b9dd52010-11-24 18:12:06 +00005852 /* Read DISC or SEEK. */
5853
cristy3ed852e2009-09-05 21:47:34 +00005854 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5855 {
5856 for (i=1; i < MNG_MAX_OBJECTS; i++)
5857 MngInfoDiscardObject(mng_info,i);
5858 }
glennrp0fe50b42010-11-16 03:52:51 +00005859
cristy3ed852e2009-09-05 21:47:34 +00005860 else
5861 {
cristybb503372010-05-27 20:51:26 +00005862 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005863 j;
5864
cristybb503372010-05-27 20:51:26 +00005865 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005866 {
5867 i=p[j] << 8 | p[j+1];
5868 MngInfoDiscardObject(mng_info,i);
5869 }
5870 }
glennrp0fe50b42010-11-16 03:52:51 +00005871
cristy3ed852e2009-09-05 21:47:34 +00005872 if (length)
5873 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005874
cristy3ed852e2009-09-05 21:47:34 +00005875 continue;
5876 }
glennrp47b9dd52010-11-24 18:12:06 +00005877
cristy3ed852e2009-09-05 21:47:34 +00005878 if (memcmp(type,mng_MOVE,4) == 0)
5879 {
cristybb503372010-05-27 20:51:26 +00005880 size_t
cristy3ed852e2009-09-05 21:47:34 +00005881 first_object,
5882 last_object;
5883
glennrp47b9dd52010-11-24 18:12:06 +00005884 /* read MOVE */
5885
cristy3ed852e2009-09-05 21:47:34 +00005886 first_object=(p[0] << 8) | p[1];
5887 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005888 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005889 {
5890 if (mng_info->exists[i] && !mng_info->frozen[i])
5891 {
5892 MngPair
5893 new_pair;
5894
5895 MngPair
5896 old_pair;
5897
5898 old_pair.a=mng_info->x_off[i];
5899 old_pair.b=mng_info->y_off[i];
5900 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5901 mng_info->x_off[i]=new_pair.a;
5902 mng_info->y_off[i]=new_pair.b;
5903 }
5904 }
glennrp47b9dd52010-11-24 18:12:06 +00005905
cristy3ed852e2009-09-05 21:47:34 +00005906 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5907 continue;
5908 }
5909
5910 if (memcmp(type,mng_LOOP,4) == 0)
5911 {
cristybb503372010-05-27 20:51:26 +00005912 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005913 loop_level=chunk[0];
5914 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005915
5916 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005917 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005918
cristy3ed852e2009-09-05 21:47:34 +00005919 if (logging != MagickFalse)
5920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005921 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5922 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005923
cristy3ed852e2009-09-05 21:47:34 +00005924 if (loop_iters == 0)
5925 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005926
cristy3ed852e2009-09-05 21:47:34 +00005927 else
5928 {
5929 mng_info->loop_jump[loop_level]=TellBlob(image);
5930 mng_info->loop_count[loop_level]=loop_iters;
5931 }
glennrp0fe50b42010-11-16 03:52:51 +00005932
cristy3ed852e2009-09-05 21:47:34 +00005933 mng_info->loop_iteration[loop_level]=0;
5934 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5935 continue;
5936 }
glennrp47b9dd52010-11-24 18:12:06 +00005937
cristy3ed852e2009-09-05 21:47:34 +00005938 if (memcmp(type,mng_ENDL,4) == 0)
5939 {
5940 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005941
cristy3ed852e2009-09-05 21:47:34 +00005942 if (skipping_loop > 0)
5943 {
5944 if (skipping_loop == loop_level)
5945 {
5946 /*
5947 Found end of zero-iteration loop.
5948 */
5949 skipping_loop=(-1);
5950 mng_info->loop_active[loop_level]=0;
5951 }
5952 }
glennrp47b9dd52010-11-24 18:12:06 +00005953
cristy3ed852e2009-09-05 21:47:34 +00005954 else
5955 {
5956 if (mng_info->loop_active[loop_level] == 1)
5957 {
5958 mng_info->loop_count[loop_level]--;
5959 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005960
cristy3ed852e2009-09-05 21:47:34 +00005961 if (logging != MagickFalse)
5962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005963 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005964 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005965 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005966
cristy3ed852e2009-09-05 21:47:34 +00005967 if (mng_info->loop_count[loop_level] != 0)
5968 {
5969 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5970 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005971
cristy3ed852e2009-09-05 21:47:34 +00005972 if (offset < 0)
5973 ThrowReaderException(CorruptImageError,
5974 "ImproperImageHeader");
5975 }
glennrp47b9dd52010-11-24 18:12:06 +00005976
cristy3ed852e2009-09-05 21:47:34 +00005977 else
5978 {
5979 short
5980 last_level;
5981
5982 /*
5983 Finished loop.
5984 */
5985 mng_info->loop_active[loop_level]=0;
5986 last_level=(-1);
5987 for (i=0; i < loop_level; i++)
5988 if (mng_info->loop_active[i] == 1)
5989 last_level=(short) i;
5990 loop_level=last_level;
5991 }
5992 }
5993 }
glennrp47b9dd52010-11-24 18:12:06 +00005994
cristy3ed852e2009-09-05 21:47:34 +00005995 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5996 continue;
5997 }
glennrp47b9dd52010-11-24 18:12:06 +00005998
cristy3ed852e2009-09-05 21:47:34 +00005999 if (memcmp(type,mng_CLON,4) == 0)
6000 {
6001 if (mng_info->clon_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006002 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006003 CoderError,"CLON is not implemented yet","`%s'",
6004 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006005
cristy3ed852e2009-09-05 21:47:34 +00006006 mng_info->clon_warning++;
6007 }
glennrp47b9dd52010-11-24 18:12:06 +00006008
cristy3ed852e2009-09-05 21:47:34 +00006009 if (memcmp(type,mng_MAGN,4) == 0)
6010 {
6011 png_uint_16
6012 magn_first,
6013 magn_last,
6014 magn_mb,
6015 magn_ml,
6016 magn_mr,
6017 magn_mt,
6018 magn_mx,
6019 magn_my,
6020 magn_methx,
6021 magn_methy;
6022
6023 if (length > 1)
6024 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00006025
cristy3ed852e2009-09-05 21:47:34 +00006026 else
6027 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00006028
cristy3ed852e2009-09-05 21:47:34 +00006029 if (length > 3)
6030 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00006031
cristy3ed852e2009-09-05 21:47:34 +00006032 else
6033 magn_last=magn_first;
6034#ifndef MNG_OBJECT_BUFFERS
6035 if (magn_first || magn_last)
6036 if (mng_info->magn_warning == 0)
6037 {
cristy16ea1392012-03-21 20:38:41 +00006038 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00006039 GetMagickModule(),CoderError,
6040 "MAGN is not implemented yet for nonzero objects",
6041 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006042
cristy3ed852e2009-09-05 21:47:34 +00006043 mng_info->magn_warning++;
6044 }
6045#endif
6046 if (length > 4)
6047 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00006048
cristy3ed852e2009-09-05 21:47:34 +00006049 else
6050 magn_methx=0;
6051
6052 if (length > 6)
6053 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00006054
cristy3ed852e2009-09-05 21:47:34 +00006055 else
6056 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00006057
cristy3ed852e2009-09-05 21:47:34 +00006058 if (magn_mx == 0)
6059 magn_mx=1;
6060
6061 if (length > 8)
6062 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00006063
cristy3ed852e2009-09-05 21:47:34 +00006064 else
6065 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006066
cristy3ed852e2009-09-05 21:47:34 +00006067 if (magn_my == 0)
6068 magn_my=1;
6069
6070 if (length > 10)
6071 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00006072
cristy3ed852e2009-09-05 21:47:34 +00006073 else
6074 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006075
cristy3ed852e2009-09-05 21:47:34 +00006076 if (magn_ml == 0)
6077 magn_ml=1;
6078
6079 if (length > 12)
6080 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00006081
cristy3ed852e2009-09-05 21:47:34 +00006082 else
6083 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006084
cristy3ed852e2009-09-05 21:47:34 +00006085 if (magn_mr == 0)
6086 magn_mr=1;
6087
6088 if (length > 14)
6089 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00006090
cristy3ed852e2009-09-05 21:47:34 +00006091 else
6092 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006093
cristy3ed852e2009-09-05 21:47:34 +00006094 if (magn_mt == 0)
6095 magn_mt=1;
6096
6097 if (length > 16)
6098 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00006099
cristy3ed852e2009-09-05 21:47:34 +00006100 else
6101 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006102
cristy3ed852e2009-09-05 21:47:34 +00006103 if (magn_mb == 0)
6104 magn_mb=1;
6105
6106 if (length > 17)
6107 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00006108
cristy3ed852e2009-09-05 21:47:34 +00006109 else
6110 magn_methy=magn_methx;
6111
glennrp47b9dd52010-11-24 18:12:06 +00006112
cristy3ed852e2009-09-05 21:47:34 +00006113 if (magn_methx > 5 || magn_methy > 5)
6114 if (mng_info->magn_warning == 0)
6115 {
cristy16ea1392012-03-21 20:38:41 +00006116 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00006117 GetMagickModule(),CoderError,
6118 "Unknown MAGN method in MNG datastream","`%s'",
6119 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006120
cristy3ed852e2009-09-05 21:47:34 +00006121 mng_info->magn_warning++;
6122 }
6123#ifdef MNG_OBJECT_BUFFERS
6124 /* Magnify existing objects in the range magn_first to magn_last */
6125#endif
6126 if (magn_first == 0 || magn_last == 0)
6127 {
6128 /* Save the magnification factors for object 0 */
6129 mng_info->magn_mb=magn_mb;
6130 mng_info->magn_ml=magn_ml;
6131 mng_info->magn_mr=magn_mr;
6132 mng_info->magn_mt=magn_mt;
6133 mng_info->magn_mx=magn_mx;
6134 mng_info->magn_my=magn_my;
6135 mng_info->magn_methx=magn_methx;
6136 mng_info->magn_methy=magn_methy;
6137 }
6138 }
glennrp47b9dd52010-11-24 18:12:06 +00006139
cristy3ed852e2009-09-05 21:47:34 +00006140 if (memcmp(type,mng_PAST,4) == 0)
6141 {
6142 if (mng_info->past_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006143 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006144 CoderError,"PAST is not implemented yet","`%s'",
6145 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006146
cristy3ed852e2009-09-05 21:47:34 +00006147 mng_info->past_warning++;
6148 }
glennrp47b9dd52010-11-24 18:12:06 +00006149
cristy3ed852e2009-09-05 21:47:34 +00006150 if (memcmp(type,mng_SHOW,4) == 0)
6151 {
6152 if (mng_info->show_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006153 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006154 CoderError,"SHOW is not implemented yet","`%s'",
6155 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006156
cristy3ed852e2009-09-05 21:47:34 +00006157 mng_info->show_warning++;
6158 }
glennrp47b9dd52010-11-24 18:12:06 +00006159
cristy3ed852e2009-09-05 21:47:34 +00006160 if (memcmp(type,mng_sBIT,4) == 0)
6161 {
6162 if (length < 4)
6163 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006164
cristy3ed852e2009-09-05 21:47:34 +00006165 else
6166 {
6167 mng_info->global_sbit.gray=p[0];
6168 mng_info->global_sbit.red=p[0];
6169 mng_info->global_sbit.green=p[1];
6170 mng_info->global_sbit.blue=p[2];
6171 mng_info->global_sbit.alpha=p[3];
6172 mng_info->have_global_sbit=MagickTrue;
6173 }
6174 }
6175 if (memcmp(type,mng_pHYs,4) == 0)
6176 {
6177 if (length > 8)
6178 {
6179 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00006180 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00006181 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00006182 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00006183 mng_info->global_phys_unit_type=p[8];
6184 mng_info->have_global_phys=MagickTrue;
6185 }
glennrp47b9dd52010-11-24 18:12:06 +00006186
cristy3ed852e2009-09-05 21:47:34 +00006187 else
6188 mng_info->have_global_phys=MagickFalse;
6189 }
6190 if (memcmp(type,mng_pHYg,4) == 0)
6191 {
6192 if (mng_info->phyg_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006193 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006194 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006195
cristy3ed852e2009-09-05 21:47:34 +00006196 mng_info->phyg_warning++;
6197 }
6198 if (memcmp(type,mng_BASI,4) == 0)
6199 {
6200 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00006201
cristy3ed852e2009-09-05 21:47:34 +00006202 if (mng_info->basi_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006203 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006204 CoderError,"BASI is not implemented yet","`%s'",
6205 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006206
cristy3ed852e2009-09-05 21:47:34 +00006207 mng_info->basi_warning++;
6208#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00006209 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00006210 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00006211 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00006212 (p[6] << 8) | p[7]);
6213 basi_color_type=p[8];
6214 basi_compression_method=p[9];
6215 basi_filter_type=p[10];
6216 basi_interlace_method=p[11];
6217 if (length > 11)
6218 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00006219
cristy3ed852e2009-09-05 21:47:34 +00006220 else
6221 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00006222
cristy3ed852e2009-09-05 21:47:34 +00006223 if (length > 13)
6224 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00006225
cristy3ed852e2009-09-05 21:47:34 +00006226 else
6227 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00006228
cristy3ed852e2009-09-05 21:47:34 +00006229 if (length > 15)
6230 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00006231
cristy3ed852e2009-09-05 21:47:34 +00006232 else
6233 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00006234
cristy3ed852e2009-09-05 21:47:34 +00006235 if (length > 17)
6236 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00006237
cristy3ed852e2009-09-05 21:47:34 +00006238 else
6239 {
6240 if (basi_sample_depth == 16)
6241 basi_alpha=65535L;
6242 else
6243 basi_alpha=255;
6244 }
glennrp47b9dd52010-11-24 18:12:06 +00006245
cristy3ed852e2009-09-05 21:47:34 +00006246 if (length > 19)
6247 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00006248
cristy3ed852e2009-09-05 21:47:34 +00006249 else
6250 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00006251
cristy3ed852e2009-09-05 21:47:34 +00006252#endif
6253 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6254 continue;
6255 }
glennrp47b9dd52010-11-24 18:12:06 +00006256
cristy3ed852e2009-09-05 21:47:34 +00006257 if (memcmp(type,mng_IHDR,4)
6258#if defined(JNG_SUPPORTED)
6259 && memcmp(type,mng_JHDR,4)
6260#endif
6261 )
6262 {
6263 /* Not an IHDR or JHDR chunk */
6264 if (length)
6265 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00006266
cristy3ed852e2009-09-05 21:47:34 +00006267 continue;
6268 }
6269/* Process IHDR */
6270 if (logging != MagickFalse)
6271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6272 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006273
cristy3ed852e2009-09-05 21:47:34 +00006274 mng_info->exists[object_id]=MagickTrue;
6275 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00006276
cristy3ed852e2009-09-05 21:47:34 +00006277 if (mng_info->invisible[object_id])
6278 {
6279 if (logging != MagickFalse)
6280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6281 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00006282
cristy3ed852e2009-09-05 21:47:34 +00006283 skip_to_iend=MagickTrue;
6284 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6285 continue;
6286 }
6287#if defined(MNG_INSERT_LAYERS)
6288 if (length < 8)
6289 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00006290
cristy8182b072010-05-30 20:10:53 +00006291 image_width=(size_t) mng_get_long(p);
6292 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00006293#endif
6294 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6295
6296 /*
6297 Insert a transparent background layer behind the entire animation
6298 if it is not full screen.
6299 */
6300#if defined(MNG_INSERT_LAYERS)
6301 if (insert_layers && mng_type && first_mng_object)
6302 {
6303 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6304 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00006305 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00006306 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00006307 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00006308 {
cristy16ea1392012-03-21 20:38:41 +00006309 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006310 {
6311 /*
6312 Allocate next image structure.
6313 */
cristy16ea1392012-03-21 20:38:41 +00006314 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006315
cristy3ed852e2009-09-05 21:47:34 +00006316 if (GetNextImageInList(image) == (Image *) NULL)
6317 {
6318 image=DestroyImageList(image);
6319 MngInfoFreeStruct(mng_info,&have_mng_structure);
6320 return((Image *) NULL);
6321 }
glennrp47b9dd52010-11-24 18:12:06 +00006322
cristy3ed852e2009-09-05 21:47:34 +00006323 image=SyncNextImageInList(image);
6324 }
6325 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00006326
cristy3ed852e2009-09-05 21:47:34 +00006327 if (term_chunk_found)
6328 {
6329 image->start_loop=MagickTrue;
6330 image->iterations=mng_iterations;
6331 term_chunk_found=MagickFalse;
6332 }
glennrp47b9dd52010-11-24 18:12:06 +00006333
cristy3ed852e2009-09-05 21:47:34 +00006334 else
6335 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006336
6337 /* Make a background rectangle. */
6338
cristy3ed852e2009-09-05 21:47:34 +00006339 image->delay=0;
6340 image->columns=mng_info->mng_width;
6341 image->rows=mng_info->mng_height;
6342 image->page.width=mng_info->mng_width;
6343 image->page.height=mng_info->mng_height;
6344 image->page.x=0;
6345 image->page.y=0;
6346 image->background_color=mng_background_color;
cristy16ea1392012-03-21 20:38:41 +00006347 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006348 if (logging != MagickFalse)
6349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006350 " Inserted transparent background layer, W=%.20g, H=%.20g",
6351 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00006352 }
6353 }
6354 /*
6355 Insert a background layer behind the upcoming image if
6356 framing_mode is 3, and we haven't already inserted one.
6357 */
6358 if (insert_layers && (mng_info->framing_mode == 3) &&
6359 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6360 (simplicity & 0x08)))
6361 {
cristy16ea1392012-03-21 20:38:41 +00006362 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006363 {
6364 /*
6365 Allocate next image structure.
6366 */
cristy16ea1392012-03-21 20:38:41 +00006367 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006368
cristy3ed852e2009-09-05 21:47:34 +00006369 if (GetNextImageInList(image) == (Image *) NULL)
6370 {
6371 image=DestroyImageList(image);
6372 MngInfoFreeStruct(mng_info,&have_mng_structure);
6373 return((Image *) NULL);
6374 }
glennrp47b9dd52010-11-24 18:12:06 +00006375
cristy3ed852e2009-09-05 21:47:34 +00006376 image=SyncNextImageInList(image);
6377 }
glennrp0fe50b42010-11-16 03:52:51 +00006378
cristy3ed852e2009-09-05 21:47:34 +00006379 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00006380
cristy3ed852e2009-09-05 21:47:34 +00006381 if (term_chunk_found)
6382 {
6383 image->start_loop=MagickTrue;
6384 image->iterations=mng_iterations;
6385 term_chunk_found=MagickFalse;
6386 }
glennrp0fe50b42010-11-16 03:52:51 +00006387
cristy3ed852e2009-09-05 21:47:34 +00006388 else
6389 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006390
cristy3ed852e2009-09-05 21:47:34 +00006391 image->delay=0;
6392 image->columns=subframe_width;
6393 image->rows=subframe_height;
6394 image->page.width=subframe_width;
6395 image->page.height=subframe_height;
6396 image->page.x=mng_info->clip.left;
6397 image->page.y=mng_info->clip.top;
6398 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00006399 image->alpha_trait=UndefinedPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00006400 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006401
cristy3ed852e2009-09-05 21:47:34 +00006402 if (logging != MagickFalse)
6403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006404 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006405 (double) mng_info->clip.left,(double) mng_info->clip.right,
6406 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006407 }
6408#endif /* MNG_INSERT_LAYERS */
6409 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006410
cristy16ea1392012-03-21 20:38:41 +00006411 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006412 {
6413 /*
6414 Allocate next image structure.
6415 */
cristy16ea1392012-03-21 20:38:41 +00006416 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006417
cristy3ed852e2009-09-05 21:47:34 +00006418 if (GetNextImageInList(image) == (Image *) NULL)
6419 {
6420 image=DestroyImageList(image);
6421 MngInfoFreeStruct(mng_info,&have_mng_structure);
6422 return((Image *) NULL);
6423 }
glennrp47b9dd52010-11-24 18:12:06 +00006424
cristy3ed852e2009-09-05 21:47:34 +00006425 image=SyncNextImageInList(image);
6426 }
6427 mng_info->image=image;
6428 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6429 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006430
cristy3ed852e2009-09-05 21:47:34 +00006431 if (status == MagickFalse)
6432 break;
glennrp0fe50b42010-11-16 03:52:51 +00006433
cristy3ed852e2009-09-05 21:47:34 +00006434 if (term_chunk_found)
6435 {
6436 image->start_loop=MagickTrue;
6437 term_chunk_found=MagickFalse;
6438 }
glennrp0fe50b42010-11-16 03:52:51 +00006439
cristy3ed852e2009-09-05 21:47:34 +00006440 else
6441 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006442
cristy3ed852e2009-09-05 21:47:34 +00006443 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6444 {
6445 image->delay=frame_delay;
6446 frame_delay=default_frame_delay;
6447 }
glennrp0fe50b42010-11-16 03:52:51 +00006448
cristy3ed852e2009-09-05 21:47:34 +00006449 else
6450 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006451
cristy3ed852e2009-09-05 21:47:34 +00006452 image->page.width=mng_info->mng_width;
6453 image->page.height=mng_info->mng_height;
6454 image->page.x=mng_info->x_off[object_id];
6455 image->page.y=mng_info->y_off[object_id];
6456 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006457
cristy3ed852e2009-09-05 21:47:34 +00006458 /*
6459 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6460 */
glennrp47b9dd52010-11-24 18:12:06 +00006461
cristy3ed852e2009-09-05 21:47:34 +00006462 if (logging != MagickFalse)
6463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6464 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6465 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006466
cristybb503372010-05-27 20:51:26 +00006467 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006468
cristy3ed852e2009-09-05 21:47:34 +00006469 if (offset < 0)
6470 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6471 }
6472
6473 previous=image;
6474 mng_info->image=image;
6475 mng_info->mng_type=mng_type;
6476 mng_info->object_id=object_id;
6477
6478 if (memcmp(type,mng_IHDR,4) == 0)
6479 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006480
cristy3ed852e2009-09-05 21:47:34 +00006481#if defined(JNG_SUPPORTED)
6482 else
6483 image=ReadOneJNGImage(mng_info,image_info,exception);
6484#endif
6485
6486 if (image == (Image *) NULL)
6487 {
6488 if (IsImageObject(previous) != MagickFalse)
6489 {
6490 (void) DestroyImageList(previous);
6491 (void) CloseBlob(previous);
6492 }
glennrp47b9dd52010-11-24 18:12:06 +00006493
cristy3ed852e2009-09-05 21:47:34 +00006494 MngInfoFreeStruct(mng_info,&have_mng_structure);
6495 return((Image *) NULL);
6496 }
glennrp0fe50b42010-11-16 03:52:51 +00006497
cristy3ed852e2009-09-05 21:47:34 +00006498 if (image->columns == 0 || image->rows == 0)
6499 {
6500 (void) CloseBlob(image);
6501 image=DestroyImageList(image);
6502 MngInfoFreeStruct(mng_info,&have_mng_structure);
6503 return((Image *) NULL);
6504 }
glennrp0fe50b42010-11-16 03:52:51 +00006505
cristy3ed852e2009-09-05 21:47:34 +00006506 mng_info->image=image;
6507
6508 if (mng_type)
6509 {
6510 MngBox
6511 crop_box;
6512
6513 if (mng_info->magn_methx || mng_info->magn_methy)
6514 {
6515 png_uint_32
6516 magnified_height,
6517 magnified_width;
6518
6519 if (logging != MagickFalse)
6520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6521 " Processing MNG MAGN chunk");
6522
6523 if (mng_info->magn_methx == 1)
6524 {
6525 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006526
cristy3ed852e2009-09-05 21:47:34 +00006527 if (image->columns > 1)
6528 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006529
cristy3ed852e2009-09-05 21:47:34 +00006530 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006531 magnified_width += (png_uint_32)
6532 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006533 }
glennrp47b9dd52010-11-24 18:12:06 +00006534
cristy3ed852e2009-09-05 21:47:34 +00006535 else
6536 {
cristy4e5bc842010-06-09 13:56:01 +00006537 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006538
cristy3ed852e2009-09-05 21:47:34 +00006539 if (image->columns > 1)
6540 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006541
cristy3ed852e2009-09-05 21:47:34 +00006542 if (image->columns > 2)
6543 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006544
cristy3ed852e2009-09-05 21:47:34 +00006545 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006546 magnified_width += (png_uint_32)
6547 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006548 }
glennrp47b9dd52010-11-24 18:12:06 +00006549
cristy3ed852e2009-09-05 21:47:34 +00006550 if (mng_info->magn_methy == 1)
6551 {
6552 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006553
cristy3ed852e2009-09-05 21:47:34 +00006554 if (image->rows > 1)
6555 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006556
cristy3ed852e2009-09-05 21:47:34 +00006557 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006558 magnified_height += (png_uint_32)
6559 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006560 }
glennrp47b9dd52010-11-24 18:12:06 +00006561
cristy3ed852e2009-09-05 21:47:34 +00006562 else
6563 {
cristy4e5bc842010-06-09 13:56:01 +00006564 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006565
cristy3ed852e2009-09-05 21:47:34 +00006566 if (image->rows > 1)
6567 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006568
cristy3ed852e2009-09-05 21:47:34 +00006569 if (image->rows > 2)
6570 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006571
cristy3ed852e2009-09-05 21:47:34 +00006572 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006573 magnified_height += (png_uint_32)
6574 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006575 }
glennrp47b9dd52010-11-24 18:12:06 +00006576
cristy3ed852e2009-09-05 21:47:34 +00006577 if (magnified_height > image->rows ||
6578 magnified_width > image->columns)
6579 {
6580 Image
6581 *large_image;
6582
6583 int
6584 yy;
6585
cristy16ea1392012-03-21 20:38:41 +00006586 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006587 *next,
6588 *prev;
6589
6590 png_uint_16
6591 magn_methx,
6592 magn_methy;
6593
cristy16ea1392012-03-21 20:38:41 +00006594 ssize_t
6595 m,
6596 y;
6597
6598 register Quantum
6599 *n,
6600 *q;
6601
6602 register ssize_t
6603 x;
6604
glennrp47b9dd52010-11-24 18:12:06 +00006605 /* Allocate next image structure. */
6606
cristy3ed852e2009-09-05 21:47:34 +00006607 if (logging != MagickFalse)
6608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6609 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006610
cristy16ea1392012-03-21 20:38:41 +00006611 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006612
cristy3ed852e2009-09-05 21:47:34 +00006613 if (GetNextImageInList(image) == (Image *) NULL)
6614 {
6615 image=DestroyImageList(image);
6616 MngInfoFreeStruct(mng_info,&have_mng_structure);
6617 return((Image *) NULL);
6618 }
6619
6620 large_image=SyncNextImageInList(image);
6621
6622 large_image->columns=magnified_width;
6623 large_image->rows=magnified_height;
6624
6625 magn_methx=mng_info->magn_methx;
6626 magn_methy=mng_info->magn_methy;
6627
glennrp3faa9a32011-04-23 14:00:25 +00006628#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006629#define QM unsigned short
6630 if (magn_methx != 1 || magn_methy != 1)
6631 {
6632 /*
6633 Scale pixels to unsigned shorts to prevent
6634 overflow of intermediate values of interpolations
6635 */
cristybb503372010-05-27 20:51:26 +00006636 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006637 {
6638 q=GetAuthenticPixels(image,0,y,image->columns,1,
6639 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006640
cristybb503372010-05-27 20:51:26 +00006641 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006642 {
cristy16ea1392012-03-21 20:38:41 +00006643 SetPixelRed(image,ScaleQuantumToShort(
6644 GetPixelRed(image,q)),q);
6645 SetPixelGreen(image,ScaleQuantumToShort(
6646 GetPixelGreen(image,q)),q);
6647 SetPixelBlue(image,ScaleQuantumToShort(
6648 GetPixelBlue(image,q)),q);
6649 SetPixelAlpha(image,ScaleQuantumToShort(
6650 GetPixelAlpha(image,q)),q);
6651 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006652 }
glennrp47b9dd52010-11-24 18:12:06 +00006653
cristy3ed852e2009-09-05 21:47:34 +00006654 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6655 break;
6656 }
6657 }
6658#else
6659#define QM Quantum
6660#endif
6661
cristy8a46d822012-08-28 23:32:39 +00006662 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006663 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006664
cristy3ed852e2009-09-05 21:47:34 +00006665 else
6666 {
cristy16ea1392012-03-21 20:38:41 +00006667 large_image->background_color.alpha=OpaqueAlpha;
6668 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006669
cristy3ed852e2009-09-05 21:47:34 +00006670 if (magn_methx == 4)
6671 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006672
cristy3ed852e2009-09-05 21:47:34 +00006673 if (magn_methx == 5)
6674 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006675
cristy3ed852e2009-09-05 21:47:34 +00006676 if (magn_methy == 4)
6677 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006678
cristy3ed852e2009-09-05 21:47:34 +00006679 if (magn_methy == 5)
6680 magn_methy=3;
6681 }
6682
6683 /* magnify the rows into the right side of the large image */
6684
6685 if (logging != MagickFalse)
6686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006687 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006688 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006689 yy=0;
cristy16ea1392012-03-21 20:38:41 +00006690 length=(size_t) image->columns*GetPixelChannels(image);
6691 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6692 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006693
cristy16ea1392012-03-21 20:38:41 +00006694 if ((prev == (Quantum *) NULL) ||
6695 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006696 {
6697 image=DestroyImageList(image);
6698 MngInfoFreeStruct(mng_info,&have_mng_structure);
6699 ThrowReaderException(ResourceLimitError,
6700 "MemoryAllocationFailed");
6701 }
glennrp47b9dd52010-11-24 18:12:06 +00006702
cristy3ed852e2009-09-05 21:47:34 +00006703 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6704 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006705
cristybb503372010-05-27 20:51:26 +00006706 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006707 {
6708 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006709 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006710
cristybb503372010-05-27 20:51:26 +00006711 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6712 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006713
cristybb503372010-05-27 20:51:26 +00006714 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6715 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006716
cristybb503372010-05-27 20:51:26 +00006717 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006718 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006719
cristy3ed852e2009-09-05 21:47:34 +00006720 else
cristybb503372010-05-27 20:51:26 +00006721 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006722
cristy3ed852e2009-09-05 21:47:34 +00006723 n=prev;
6724 prev=next;
6725 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006726
cristybb503372010-05-27 20:51:26 +00006727 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006728 {
6729 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6730 exception);
6731 (void) CopyMagickMemory(next,n,length);
6732 }
glennrp47b9dd52010-11-24 18:12:06 +00006733
cristy3ed852e2009-09-05 21:47:34 +00006734 for (i=0; i < m; i++, yy++)
6735 {
cristy16ea1392012-03-21 20:38:41 +00006736 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006737 *pixels;
6738
cristybb503372010-05-27 20:51:26 +00006739 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006740 pixels=prev;
6741 n=next;
6742 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006743 1,exception);
cristy16ea1392012-03-21 20:38:41 +00006744 q+=(large_image->columns-image->columns)*
6745 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006746
cristybb503372010-05-27 20:51:26 +00006747 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006748 {
glennrpfd05d622011-02-25 04:10:33 +00006749 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006750 /*
6751 if (image->storage_class == PseudoClass)
6752 {
6753 }
6754 */
6755
6756 if (magn_methy <= 1)
6757 {
glennrpbb4f99d2011-05-22 11:13:17 +00006758 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006759 SetPixelRed(large_image,GetPixelRed(image,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);
cristy3ed852e2009-09-05 21:47:34 +00006766 }
glennrp47b9dd52010-11-24 18:12:06 +00006767
cristy3ed852e2009-09-05 21:47:34 +00006768 else if (magn_methy == 2 || magn_methy == 4)
6769 {
6770 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006771 {
cristy16ea1392012-03-21 20:38:41 +00006772 SetPixelRed(large_image,GetPixelRed(image,
6773 pixels),q);
6774 SetPixelGreen(large_image,GetPixelGreen(image,
6775 pixels),q);
6776 SetPixelBlue(large_image,GetPixelBlue(image,
6777 pixels),q);
6778 SetPixelAlpha(large_image,GetPixelAlpha(image,
6779 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006780 }
glennrp47b9dd52010-11-24 18:12:06 +00006781
cristy3ed852e2009-09-05 21:47:34 +00006782 else
6783 {
6784 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006785 SetPixelRed(large_image,((QM) (((ssize_t)
6786 (2*i*(GetPixelRed(image,n)
6787 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006788 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006789 +GetPixelRed(image,pixels)))),q);
6790 SetPixelGreen(large_image,((QM) (((ssize_t)
6791 (2*i*(GetPixelGreen(image,n)
6792 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006793 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006794 +GetPixelGreen(image,pixels)))),q);
6795 SetPixelBlue(large_image,((QM) (((ssize_t)
6796 (2*i*(GetPixelBlue(image,n)
6797 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006798 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006799 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006800
cristy8a46d822012-08-28 23:32:39 +00006801 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006802 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6803 (2*i*(GetPixelAlpha(image,n)
6804 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006805 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006806 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006807 }
glennrp47b9dd52010-11-24 18:12:06 +00006808
cristy3ed852e2009-09-05 21:47:34 +00006809 if (magn_methy == 4)
6810 {
6811 /* Replicate nearest */
6812 if (i <= ((m+1) << 1))
cristy16ea1392012-03-21 20:38:41 +00006813 SetPixelAlpha(large_image,GetPixelAlpha(image,
6814 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006815 else
cristy16ea1392012-03-21 20:38:41 +00006816 SetPixelAlpha(large_image,GetPixelAlpha(image,
6817 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006818 }
6819 }
glennrp47b9dd52010-11-24 18:12:06 +00006820
cristy3ed852e2009-09-05 21:47:34 +00006821 else /* if (magn_methy == 3 || magn_methy == 5) */
6822 {
6823 /* Replicate nearest */
6824 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006825 {
cristy16ea1392012-03-21 20:38:41 +00006826 SetPixelRed(large_image,GetPixelRed(image,
6827 pixels),q);
6828 SetPixelGreen(large_image,GetPixelGreen(image,
6829 pixels),q);
6830 SetPixelBlue(large_image,GetPixelBlue(image,
6831 pixels),q);
6832 SetPixelAlpha(large_image,GetPixelAlpha(image,
6833 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006834 }
glennrp47b9dd52010-11-24 18:12:06 +00006835
cristy3ed852e2009-09-05 21:47:34 +00006836 else
glennrpbb4f99d2011-05-22 11:13:17 +00006837 {
cristy16ea1392012-03-21 20:38:41 +00006838 SetPixelRed(large_image,GetPixelRed(image,n),q);
6839 SetPixelGreen(large_image,GetPixelGreen(image,n),
6840 q);
6841 SetPixelBlue(large_image,GetPixelBlue(image,n),
6842 q);
6843 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6844 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006845 }
glennrp47b9dd52010-11-24 18:12:06 +00006846
cristy3ed852e2009-09-05 21:47:34 +00006847 if (magn_methy == 5)
6848 {
cristy16ea1392012-03-21 20:38:41 +00006849 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6850 (GetPixelAlpha(image,n)
6851 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006852 +m))/((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006853 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006854 }
6855 }
cristy16ea1392012-03-21 20:38:41 +00006856 n+=GetPixelChannels(image);
6857 q+=GetPixelChannels(large_image);
6858 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006859 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006860
cristy3ed852e2009-09-05 21:47:34 +00006861 if (SyncAuthenticPixels(large_image,exception) == 0)
6862 break;
glennrp47b9dd52010-11-24 18:12:06 +00006863
cristy3ed852e2009-09-05 21:47:34 +00006864 } /* i */
6865 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006866
cristy16ea1392012-03-21 20:38:41 +00006867 prev=(Quantum *) RelinquishMagickMemory(prev);
6868 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006869
6870 length=image->columns;
6871
6872 if (logging != MagickFalse)
6873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6874 " Delete original image");
6875
6876 DeleteImageFromList(&image);
6877
6878 image=large_image;
6879
6880 mng_info->image=image;
6881
6882 /* magnify the columns */
6883 if (logging != MagickFalse)
6884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006885 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006886
cristybb503372010-05-27 20:51:26 +00006887 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006888 {
cristy16ea1392012-03-21 20:38:41 +00006889 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006890 *pixels;
6891
6892 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00006893 pixels=q+(image->columns-length)*GetPixelChannels(image);
6894 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006895
cristybb503372010-05-27 20:51:26 +00006896 for (x=(ssize_t) (image->columns-length);
6897 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006898 {
cristy16ea1392012-03-21 20:38:41 +00006899 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006900
cristybb503372010-05-27 20:51:26 +00006901 if (x == (ssize_t) (image->columns-length))
6902 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006903
cristybb503372010-05-27 20:51:26 +00006904 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6905 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006906
cristybb503372010-05-27 20:51:26 +00006907 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6908 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006909
cristybb503372010-05-27 20:51:26 +00006910 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006911 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006912
cristy3ed852e2009-09-05 21:47:34 +00006913 else
cristybb503372010-05-27 20:51:26 +00006914 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006915
cristy3ed852e2009-09-05 21:47:34 +00006916 for (i=0; i < m; i++)
6917 {
6918 if (magn_methx <= 1)
6919 {
6920 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006921 SetPixelRed(image,GetPixelRed(image,pixels),q);
6922 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6923 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6924 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006925 }
glennrp47b9dd52010-11-24 18:12:06 +00006926
cristy3ed852e2009-09-05 21:47:34 +00006927 else if (magn_methx == 2 || magn_methx == 4)
6928 {
6929 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006930 {
cristy16ea1392012-03-21 20:38:41 +00006931 SetPixelRed(image,GetPixelRed(image,pixels),q);
6932 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6933 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6934 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006935 }
glennrp47b9dd52010-11-24 18:12:06 +00006936
cristy16ea1392012-03-21 20:38:41 +00006937 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006938 else
6939 {
6940 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006941 SetPixelRed(image,(QM) ((2*i*(
6942 GetPixelRed(image,n)
6943 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006944 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006945 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006946
cristy16ea1392012-03-21 20:38:41 +00006947 SetPixelGreen(image,(QM) ((2*i*(
6948 GetPixelGreen(image,n)
6949 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006950 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006951 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006952
cristy16ea1392012-03-21 20:38:41 +00006953 SetPixelBlue(image,(QM) ((2*i*(
6954 GetPixelBlue(image,n)
6955 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006956 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006957 GetPixelBlue(image,pixels)),q);
cristy8a46d822012-08-28 23:32:39 +00006958 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006959 SetPixelAlpha(image,(QM) ((2*i*(
6960 GetPixelAlpha(image,n)
6961 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006962 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006963 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006964 }
glennrp47b9dd52010-11-24 18:12:06 +00006965
cristy3ed852e2009-09-05 21:47:34 +00006966 if (magn_methx == 4)
6967 {
6968 /* Replicate nearest */
6969 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006970 {
cristy16ea1392012-03-21 20:38:41 +00006971 SetPixelAlpha(image,
6972 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006973 }
cristy3ed852e2009-09-05 21:47:34 +00006974 else
glennrpbb4f99d2011-05-22 11:13:17 +00006975 {
cristy16ea1392012-03-21 20:38:41 +00006976 SetPixelAlpha(image,
6977 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006978 }
cristy3ed852e2009-09-05 21:47:34 +00006979 }
6980 }
glennrp47b9dd52010-11-24 18:12:06 +00006981
cristy3ed852e2009-09-05 21:47:34 +00006982 else /* if (magn_methx == 3 || magn_methx == 5) */
6983 {
6984 /* Replicate nearest */
6985 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006986 {
cristy16ea1392012-03-21 20:38:41 +00006987 SetPixelRed(image,GetPixelRed(image,pixels),q);
6988 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6989 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6990 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006991 }
glennrp47b9dd52010-11-24 18:12:06 +00006992
cristy3ed852e2009-09-05 21:47:34 +00006993 else
glennrpbb4f99d2011-05-22 11:13:17 +00006994 {
cristy16ea1392012-03-21 20:38:41 +00006995 SetPixelRed(image,GetPixelRed(image,n),q);
6996 SetPixelGreen(image,GetPixelGreen(image,n),q);
6997 SetPixelBlue(image,GetPixelBlue(image,n),q);
6998 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006999 }
glennrp47b9dd52010-11-24 18:12:06 +00007000
cristy3ed852e2009-09-05 21:47:34 +00007001 if (magn_methx == 5)
7002 {
7003 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00007004 SetPixelAlpha(image,
7005 (QM) ((2*i*( GetPixelAlpha(image,n)
7006 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00007007 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00007008 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00007009 }
7010 }
cristy16ea1392012-03-21 20:38:41 +00007011 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00007012 }
cristy16ea1392012-03-21 20:38:41 +00007013 n+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00007014 }
glennrp47b9dd52010-11-24 18:12:06 +00007015
cristy3ed852e2009-09-05 21:47:34 +00007016 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7017 break;
7018 }
glennrp3faa9a32011-04-23 14:00:25 +00007019#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00007020 if (magn_methx != 1 || magn_methy != 1)
7021 {
7022 /*
7023 Rescale pixels to Quantum
7024 */
cristybb503372010-05-27 20:51:26 +00007025 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00007026 {
7027 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00007028
cristybb503372010-05-27 20:51:26 +00007029 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00007030 {
cristy16ea1392012-03-21 20:38:41 +00007031 SetPixelRed(image,ScaleShortToQuantum(
7032 GetPixelRed(image,q)),q);
7033 SetPixelGreen(image,ScaleShortToQuantum(
7034 GetPixelGreen(image,q)),q);
7035 SetPixelBlue(image,ScaleShortToQuantum(
7036 GetPixelBlue(image,q)),q);
7037 SetPixelAlpha(image,ScaleShortToQuantum(
7038 GetPixelAlpha(image,q)),q);
7039 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00007040 }
glennrp47b9dd52010-11-24 18:12:06 +00007041
cristy3ed852e2009-09-05 21:47:34 +00007042 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7043 break;
7044 }
7045 }
7046#endif
7047 if (logging != MagickFalse)
7048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7049 " Finished MAGN processing");
7050 }
7051 }
7052
7053 /*
7054 Crop_box is with respect to the upper left corner of the MNG.
7055 */
7056 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7057 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7058 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7059 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7060 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7061 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7062 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7063 if ((crop_box.left != (mng_info->image_box.left
7064 +mng_info->x_off[object_id])) ||
7065 (crop_box.right != (mng_info->image_box.right
7066 +mng_info->x_off[object_id])) ||
7067 (crop_box.top != (mng_info->image_box.top
7068 +mng_info->y_off[object_id])) ||
7069 (crop_box.bottom != (mng_info->image_box.bottom
7070 +mng_info->y_off[object_id])))
7071 {
7072 if (logging != MagickFalse)
7073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7074 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00007075
cristy3ed852e2009-09-05 21:47:34 +00007076 if ((crop_box.left < crop_box.right) &&
7077 (crop_box.top < crop_box.bottom))
7078 {
7079 Image
7080 *im;
7081
7082 RectangleInfo
7083 crop_info;
7084
7085 /*
7086 Crop_info is with respect to the upper left corner of
7087 the image.
7088 */
7089 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7090 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00007091 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7092 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00007093 image->page.width=image->columns;
7094 image->page.height=image->rows;
7095 image->page.x=0;
7096 image->page.y=0;
7097 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00007098
cristy3ed852e2009-09-05 21:47:34 +00007099 if (im != (Image *) NULL)
7100 {
7101 image->columns=im->columns;
7102 image->rows=im->rows;
7103 im=DestroyImage(im);
7104 image->page.width=image->columns;
7105 image->page.height=image->rows;
7106 image->page.x=crop_box.left;
7107 image->page.y=crop_box.top;
7108 }
7109 }
glennrp47b9dd52010-11-24 18:12:06 +00007110
cristy3ed852e2009-09-05 21:47:34 +00007111 else
7112 {
7113 /*
7114 No pixels in crop area. The MNG spec still requires
7115 a layer, though, so make a single transparent pixel in
7116 the top left corner.
7117 */
7118 image->columns=1;
7119 image->rows=1;
7120 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00007121 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007122 image->page.width=1;
7123 image->page.height=1;
7124 image->page.x=0;
7125 image->page.y=0;
7126 }
7127 }
7128#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7129 image=mng_info->image;
7130#endif
7131 }
7132
glennrp2b013e42010-11-24 16:55:50 +00007133#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7134 /* PNG does not handle depths greater than 16 so reduce it even
cristy16ea1392012-03-21 20:38:41 +00007135 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00007136 */
7137 if (image->depth > 16)
7138 image->depth=16;
7139#endif
7140
glennrp3faa9a32011-04-23 14:00:25 +00007141#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00007142 if (image->depth > 8)
7143 {
7144 /* To do: fill low byte properly */
7145 image->depth=16;
7146 }
7147
cristy16ea1392012-03-21 20:38:41 +00007148 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007149 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00007150#endif
glennrpd6afd542010-11-19 01:53:05 +00007151
cristy3ed852e2009-09-05 21:47:34 +00007152 if (image_info->number_scenes != 0)
7153 {
7154 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00007155 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00007156 break;
7157 }
glennrpd6afd542010-11-19 01:53:05 +00007158
cristy3ed852e2009-09-05 21:47:34 +00007159 if (logging != MagickFalse)
7160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7161 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00007162
cristy3ed852e2009-09-05 21:47:34 +00007163 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00007164
cristy3ed852e2009-09-05 21:47:34 +00007165 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00007166
cristy3ed852e2009-09-05 21:47:34 +00007167 if (logging != MagickFalse)
7168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7169 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00007170
cristy3ed852e2009-09-05 21:47:34 +00007171#if defined(MNG_INSERT_LAYERS)
7172 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7173 (mng_info->mng_height))
7174 {
7175 /*
7176 Insert a background layer if nothing else was found.
7177 */
7178 if (logging != MagickFalse)
7179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7180 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00007181
cristy16ea1392012-03-21 20:38:41 +00007182 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00007183 {
7184 /*
7185 Allocate next image structure.
7186 */
cristy16ea1392012-03-21 20:38:41 +00007187 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007188 if (GetNextImageInList(image) == (Image *) NULL)
7189 {
7190 image=DestroyImageList(image);
7191 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00007192
cristy3ed852e2009-09-05 21:47:34 +00007193 if (logging != MagickFalse)
7194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7195 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00007196
cristy3ed852e2009-09-05 21:47:34 +00007197 return((Image *) NULL);
7198 }
7199 image=SyncNextImageInList(image);
7200 }
7201 image->columns=mng_info->mng_width;
7202 image->rows=mng_info->mng_height;
7203 image->page.width=mng_info->mng_width;
7204 image->page.height=mng_info->mng_height;
7205 image->page.x=0;
7206 image->page.y=0;
7207 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00007208 image->alpha_trait=UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00007209
cristy3ed852e2009-09-05 21:47:34 +00007210 if (image_info->ping == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00007211 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007212
cristy3ed852e2009-09-05 21:47:34 +00007213 mng_info->image_found++;
7214 }
7215#endif
7216 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00007217
cristy3ed852e2009-09-05 21:47:34 +00007218 if (mng_iterations == 1)
7219 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00007220
cristy3ed852e2009-09-05 21:47:34 +00007221 while (GetPreviousImageInList(image) != (Image *) NULL)
7222 {
7223 image_count++;
7224 if (image_count > 10*mng_info->image_found)
7225 {
7226 if (logging != MagickFalse)
7227 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00007228
cristy16ea1392012-03-21 20:38:41 +00007229 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007230 CoderError,"Linked list is corrupted, beginning of list not found",
7231 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007232
cristy3ed852e2009-09-05 21:47:34 +00007233 return((Image *) NULL);
7234 }
glennrp0fe50b42010-11-16 03:52:51 +00007235
cristy3ed852e2009-09-05 21:47:34 +00007236 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00007237
cristy3ed852e2009-09-05 21:47:34 +00007238 if (GetNextImageInList(image) == (Image *) NULL)
7239 {
7240 if (logging != MagickFalse)
7241 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00007242
cristy16ea1392012-03-21 20:38:41 +00007243 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007244 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7245 image_info->filename);
7246 }
7247 }
glennrp47b9dd52010-11-24 18:12:06 +00007248
cristy3ed852e2009-09-05 21:47:34 +00007249 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7250 GetNextImageInList(image) ==
7251 (Image *) NULL)
7252 {
7253 if (logging != MagickFalse)
7254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7255 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00007256
cristy16ea1392012-03-21 20:38:41 +00007257 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007258 CoderError,"image->next for first image is NULL but shouldn't be.",
7259 "`%s'",image_info->filename);
7260 }
glennrp47b9dd52010-11-24 18:12:06 +00007261
cristy3ed852e2009-09-05 21:47:34 +00007262 if (mng_info->image_found == 0)
7263 {
7264 if (logging != MagickFalse)
7265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7266 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00007267
cristy16ea1392012-03-21 20:38:41 +00007268 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007269 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007270
cristy3ed852e2009-09-05 21:47:34 +00007271 if (image != (Image *) NULL)
7272 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007273
cristy3ed852e2009-09-05 21:47:34 +00007274 MngInfoFreeStruct(mng_info,&have_mng_structure);
7275 return((Image *) NULL);
7276 }
7277
7278 if (mng_info->ticks_per_second)
7279 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7280 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00007281
cristy3ed852e2009-09-05 21:47:34 +00007282 else
7283 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00007284
cristy3ed852e2009-09-05 21:47:34 +00007285 /* Find final nonzero image delay */
7286 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00007287
cristy3ed852e2009-09-05 21:47:34 +00007288 while (GetNextImageInList(image) != (Image *) NULL)
7289 {
7290 if (image->delay)
7291 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00007292
cristy3ed852e2009-09-05 21:47:34 +00007293 image=GetNextImageInList(image);
7294 }
glennrp0fe50b42010-11-16 03:52:51 +00007295
cristy3ed852e2009-09-05 21:47:34 +00007296 if (final_delay < final_image_delay)
7297 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00007298
cristy3ed852e2009-09-05 21:47:34 +00007299 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00007300
cristy3ed852e2009-09-05 21:47:34 +00007301 if (logging != MagickFalse)
7302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007303 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7304 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00007305
cristy3ed852e2009-09-05 21:47:34 +00007306 if (logging != MagickFalse)
7307 {
7308 int
7309 scene;
7310
7311 scene=0;
7312 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007313
cristy3ed852e2009-09-05 21:47:34 +00007314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7315 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007316
cristy3ed852e2009-09-05 21:47:34 +00007317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007318 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00007319
cristy3ed852e2009-09-05 21:47:34 +00007320 while (GetNextImageInList(image) != (Image *) NULL)
7321 {
7322 image=GetNextImageInList(image);
7323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007324 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00007325 }
7326 }
7327
7328 image=GetFirstImageInList(image);
7329#ifdef MNG_COALESCE_LAYERS
7330 if (insert_layers)
7331 {
7332 Image
7333 *next_image,
7334 *next;
7335
cristybb503372010-05-27 20:51:26 +00007336 size_t
cristy3ed852e2009-09-05 21:47:34 +00007337 scene;
7338
7339 if (logging != MagickFalse)
7340 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00007341
cristy3ed852e2009-09-05 21:47:34 +00007342 scene=image->scene;
cristy16ea1392012-03-21 20:38:41 +00007343 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00007344
cristy3ed852e2009-09-05 21:47:34 +00007345 if (next_image == (Image *) NULL)
7346 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00007347
cristy3ed852e2009-09-05 21:47:34 +00007348 image=DestroyImageList(image);
7349 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00007350
cristy3ed852e2009-09-05 21:47:34 +00007351 for (next=image; next != (Image *) NULL; next=next_image)
7352 {
7353 next->page.width=mng_info->mng_width;
7354 next->page.height=mng_info->mng_height;
7355 next->page.x=0;
7356 next->page.y=0;
7357 next->scene=scene++;
7358 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00007359
cristy3ed852e2009-09-05 21:47:34 +00007360 if (next_image == (Image *) NULL)
7361 break;
glennrp47b9dd52010-11-24 18:12:06 +00007362
cristy3ed852e2009-09-05 21:47:34 +00007363 if (next->delay == 0)
7364 {
7365 scene--;
7366 next_image->previous=GetPreviousImageInList(next);
7367 if (GetPreviousImageInList(next) == (Image *) NULL)
7368 image=next_image;
7369 else
7370 next->previous->next=next_image;
7371 next=DestroyImage(next);
7372 }
7373 }
7374 }
7375#endif
7376
7377 while (GetNextImageInList(image) != (Image *) NULL)
7378 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007379
cristy3ed852e2009-09-05 21:47:34 +00007380 image->dispose=BackgroundDispose;
7381
7382 if (logging != MagickFalse)
7383 {
7384 int
7385 scene;
7386
7387 scene=0;
7388 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007389
cristy3ed852e2009-09-05 21:47:34 +00007390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7391 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007392
cristy3ed852e2009-09-05 21:47:34 +00007393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007394 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7395 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007396
cristy3ed852e2009-09-05 21:47:34 +00007397 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007398 {
7399 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007400
cristyf2faecf2010-05-28 19:19:36 +00007401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007402 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7403 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007404 }
7405 }
glennrp47b9dd52010-11-24 18:12:06 +00007406
cristy3ed852e2009-09-05 21:47:34 +00007407 image=GetFirstImageInList(image);
7408 MngInfoFreeStruct(mng_info,&have_mng_structure);
7409 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007410
cristy3ed852e2009-09-05 21:47:34 +00007411 if (logging != MagickFalse)
7412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007413
cristy3ed852e2009-09-05 21:47:34 +00007414 return(GetFirstImageInList(image));
7415}
glennrp25c1e2b2010-03-25 01:39:56 +00007416#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007417static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7418{
7419 printf("Your PNG library is too old: You have libpng-%s\n",
7420 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007421
cristy3ed852e2009-09-05 21:47:34 +00007422 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7423 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007424
cristy3ed852e2009-09-05 21:47:34 +00007425 return(Image *) NULL;
7426}
glennrp47b9dd52010-11-24 18:12:06 +00007427
cristy3ed852e2009-09-05 21:47:34 +00007428static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7429{
7430 return(ReadPNGImage(image_info,exception));
7431}
glennrp25c1e2b2010-03-25 01:39:56 +00007432#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007433#endif
7434
7435/*
7436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7437% %
7438% %
7439% %
7440% R e g i s t e r P N G I m a g e %
7441% %
7442% %
7443% %
7444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7445%
7446% RegisterPNGImage() adds properties for the PNG image format to
7447% the list of supported formats. The properties include the image format
7448% tag, a method to read and/or write the format, whether the format
7449% supports the saving of more than one frame to the same file or blob,
7450% whether the format supports native in-memory I/O, and a brief
7451% description of the format.
7452%
7453% The format of the RegisterPNGImage method is:
7454%
cristybb503372010-05-27 20:51:26 +00007455% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007456%
7457*/
cristybb503372010-05-27 20:51:26 +00007458ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007459{
7460 char
7461 version[MaxTextExtent];
7462
7463 MagickInfo
7464 *entry;
7465
7466 static const char
7467 *PNGNote=
7468 {
7469 "See http://www.libpng.org/ for details about the PNG format."
7470 },
glennrp47b9dd52010-11-24 18:12:06 +00007471
cristy3ed852e2009-09-05 21:47:34 +00007472 *JNGNote=
7473 {
7474 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7475 "format."
7476 },
glennrp47b9dd52010-11-24 18:12:06 +00007477
cristy3ed852e2009-09-05 21:47:34 +00007478 *MNGNote=
7479 {
7480 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7481 "format."
7482 };
7483
7484 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007485
cristy3ed852e2009-09-05 21:47:34 +00007486#if defined(PNG_LIBPNG_VER_STRING)
7487 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7488 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007489
cristy3ed852e2009-09-05 21:47:34 +00007490 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7491 {
7492 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7493 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7494 MaxTextExtent);
7495 }
7496#endif
glennrp47b9dd52010-11-24 18:12:06 +00007497
cristy3ed852e2009-09-05 21:47:34 +00007498 entry=SetMagickInfo("MNG");
7499 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007500
cristy3ed852e2009-09-05 21:47:34 +00007501#if defined(MAGICKCORE_PNG_DELEGATE)
7502 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7503 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7504#endif
glennrp47b9dd52010-11-24 18:12:06 +00007505
cristy3ed852e2009-09-05 21:47:34 +00007506 entry->magick=(IsImageFormatHandler *) IsMNG;
7507 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007508
cristy3ed852e2009-09-05 21:47:34 +00007509 if (*version != '\0')
7510 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007511
glennrpafc97b12013-08-10 00:39:01 +00007512 entry->mime_type=ConstantString("video/x-mng");
cristy3ed852e2009-09-05 21:47:34 +00007513 entry->module=ConstantString("PNG");
7514 entry->note=ConstantString(MNGNote);
7515 (void) RegisterMagickInfo(entry);
7516
7517 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007518
cristy3ed852e2009-09-05 21:47:34 +00007519#if defined(MAGICKCORE_PNG_DELEGATE)
7520 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7521 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7522#endif
glennrp47b9dd52010-11-24 18:12:06 +00007523
cristy3ed852e2009-09-05 21:47:34 +00007524 entry->magick=(IsImageFormatHandler *) IsPNG;
7525 entry->adjoin=MagickFalse;
7526 entry->description=ConstantString("Portable Network Graphics");
cristyd625c522013-08-09 11:45:57 +00007527 entry->mime_type=ConstantString("image/png");
cristy3ed852e2009-09-05 21:47:34 +00007528 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007529
cristy3ed852e2009-09-05 21:47:34 +00007530 if (*version != '\0')
7531 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007532
cristy3ed852e2009-09-05 21:47:34 +00007533 entry->note=ConstantString(PNGNote);
7534 (void) RegisterMagickInfo(entry);
7535
7536 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007537
cristy3ed852e2009-09-05 21:47:34 +00007538#if defined(MAGICKCORE_PNG_DELEGATE)
7539 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7540 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7541#endif
glennrp47b9dd52010-11-24 18:12:06 +00007542
cristy3ed852e2009-09-05 21:47:34 +00007543 entry->magick=(IsImageFormatHandler *) IsPNG;
7544 entry->adjoin=MagickFalse;
7545 entry->description=ConstantString(
7546 "8-bit indexed with optional binary transparency");
cristyd625c522013-08-09 11:45:57 +00007547 entry->mime_type=ConstantString("image/png");
cristy3ed852e2009-09-05 21:47:34 +00007548 entry->module=ConstantString("PNG");
7549 (void) RegisterMagickInfo(entry);
7550
7551 entry=SetMagickInfo("PNG24");
7552 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007553
cristy3ed852e2009-09-05 21:47:34 +00007554#if defined(ZLIB_VERSION)
7555 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7556 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007557
cristy3ed852e2009-09-05 21:47:34 +00007558 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7559 {
7560 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7561 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7562 }
7563#endif
glennrp47b9dd52010-11-24 18:12:06 +00007564
cristy3ed852e2009-09-05 21:47:34 +00007565 if (*version != '\0')
7566 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007567
cristy3ed852e2009-09-05 21:47:34 +00007568#if defined(MAGICKCORE_PNG_DELEGATE)
7569 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7570 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7571#endif
glennrp47b9dd52010-11-24 18:12:06 +00007572
cristy3ed852e2009-09-05 21:47:34 +00007573 entry->magick=(IsImageFormatHandler *) IsPNG;
7574 entry->adjoin=MagickFalse;
glennrpfd164d22013-01-26 21:10:22 +00007575 entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
cristyd625c522013-08-09 11:45:57 +00007576 entry->mime_type=ConstantString("image/png");
cristy3ed852e2009-09-05 21:47:34 +00007577 entry->module=ConstantString("PNG");
7578 (void) RegisterMagickInfo(entry);
7579
7580 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007581
cristy3ed852e2009-09-05 21:47:34 +00007582#if defined(MAGICKCORE_PNG_DELEGATE)
7583 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7584 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7585#endif
glennrp47b9dd52010-11-24 18:12:06 +00007586
cristy3ed852e2009-09-05 21:47:34 +00007587 entry->magick=(IsImageFormatHandler *) IsPNG;
7588 entry->adjoin=MagickFalse;
7589 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
cristyd625c522013-08-09 11:45:57 +00007590 entry->mime_type=ConstantString("image/png");
cristy3ed852e2009-09-05 21:47:34 +00007591 entry->module=ConstantString("PNG");
7592 (void) RegisterMagickInfo(entry);
7593
glennrpfd164d22013-01-26 21:10:22 +00007594 entry=SetMagickInfo("PNG48");
7595
7596#if defined(MAGICKCORE_PNG_DELEGATE)
7597 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7598 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7599#endif
7600
7601 entry->magick=(IsImageFormatHandler *) IsPNG;
7602 entry->adjoin=MagickFalse;
7603 entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
cristyd625c522013-08-09 11:45:57 +00007604 entry->mime_type=ConstantString("image/png");
glennrpfd164d22013-01-26 21:10:22 +00007605 entry->module=ConstantString("PNG");
7606 (void) RegisterMagickInfo(entry);
7607
7608 entry=SetMagickInfo("PNG64");
7609
7610#if defined(MAGICKCORE_PNG_DELEGATE)
7611 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7612 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7613#endif
7614
7615 entry->magick=(IsImageFormatHandler *) IsPNG;
7616 entry->adjoin=MagickFalse;
7617 entry->description=ConstantString("opaque or transparent 64-bit RGBA");
cristyd625c522013-08-09 11:45:57 +00007618 entry->mime_type=ConstantString("image/png");
glennrpfd164d22013-01-26 21:10:22 +00007619 entry->module=ConstantString("PNG");
7620 (void) RegisterMagickInfo(entry);
7621
glennrp5830fbc2013-01-27 06:11:45 +00007622 entry=SetMagickInfo("PNG00");
7623
7624#if defined(MAGICKCORE_PNG_DELEGATE)
7625 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7626 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7627#endif
7628
7629 entry->magick=(IsImageFormatHandler *) IsPNG;
7630 entry->adjoin=MagickFalse;
glennrp62708572013-02-15 12:51:48 +00007631 entry->description=ConstantString(
7632 "PNG inheriting bit-depth and color-type from original");
cristyd625c522013-08-09 11:45:57 +00007633 entry->mime_type=ConstantString("image/png");
glennrp5830fbc2013-01-27 06:11:45 +00007634 entry->module=ConstantString("PNG");
7635 (void) RegisterMagickInfo(entry);
7636
cristy3ed852e2009-09-05 21:47:34 +00007637 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007638
cristy3ed852e2009-09-05 21:47:34 +00007639#if defined(JNG_SUPPORTED)
7640#if defined(MAGICKCORE_PNG_DELEGATE)
7641 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7642 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7643#endif
7644#endif
glennrp47b9dd52010-11-24 18:12:06 +00007645
cristy3ed852e2009-09-05 21:47:34 +00007646 entry->magick=(IsImageFormatHandler *) IsJNG;
7647 entry->adjoin=MagickFalse;
7648 entry->description=ConstantString("JPEG Network Graphics");
glennrp7fee3292013-08-09 15:25:52 +00007649 entry->mime_type=ConstantString("image/x-jng");
cristy3ed852e2009-09-05 21:47:34 +00007650 entry->module=ConstantString("PNG");
7651 entry->note=ConstantString(JNGNote);
7652 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007653
glennrp868fff32014-03-16 22:09:06 +00007654#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
cristy3d162a92014-02-16 14:05:06 +00007655 ping_semaphore=AcquireSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007656#endif
glennrp47b9dd52010-11-24 18:12:06 +00007657
cristy3ed852e2009-09-05 21:47:34 +00007658 return(MagickImageCoderSignature);
7659}
7660
7661/*
7662%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7663% %
7664% %
7665% %
7666% U n r e g i s t e r P N G I m a g e %
7667% %
7668% %
7669% %
7670%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7671%
7672% UnregisterPNGImage() removes format registrations made by the
7673% PNG module from the list of supported formats.
7674%
7675% The format of the UnregisterPNGImage method is:
7676%
7677% UnregisterPNGImage(void)
7678%
7679*/
7680ModuleExport void UnregisterPNGImage(void)
7681{
7682 (void) UnregisterMagickInfo("MNG");
7683 (void) UnregisterMagickInfo("PNG");
7684 (void) UnregisterMagickInfo("PNG8");
7685 (void) UnregisterMagickInfo("PNG24");
7686 (void) UnregisterMagickInfo("PNG32");
glennrpfd164d22013-01-26 21:10:22 +00007687 (void) UnregisterMagickInfo("PNG48");
7688 (void) UnregisterMagickInfo("PNG64");
glennrp5830fbc2013-01-27 06:11:45 +00007689 (void) UnregisterMagickInfo("PNG00");
cristy3ed852e2009-09-05 21:47:34 +00007690 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007691
glennrp868fff32014-03-16 22:09:06 +00007692#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007693 if (ping_semaphore != (SemaphoreInfo *) NULL)
cristy3d162a92014-02-16 14:05:06 +00007694 RelinquishSemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007695#endif
7696}
7697
7698#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007699#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007700/*
7701%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7702% %
7703% %
7704% %
7705% W r i t e M N G I m a g e %
7706% %
7707% %
7708% %
7709%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7710%
7711% WriteMNGImage() writes an image in the Portable Network Graphics
7712% Group's "Multiple-image Network Graphics" encoded image format.
7713%
7714% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7715%
7716% The format of the WriteMNGImage method is:
7717%
cristy16ea1392012-03-21 20:38:41 +00007718% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7719% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007720%
7721% A description of each parameter follows.
7722%
7723% o image_info: the image info.
7724%
7725% o image: The image.
7726%
cristy16ea1392012-03-21 20:38:41 +00007727% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007728%
7729% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7730% "To do" under ReadPNGImage):
7731%
cristy3ed852e2009-09-05 21:47:34 +00007732% Preserve all unknown and not-yet-handled known chunks found in input
7733% PNG file and copy them into output PNG files according to the PNG
7734% copying rules.
7735%
7736% Write the iCCP chunk at MNG level when (icc profile length > 0)
7737%
7738% Improve selection of color type (use indexed-colour or indexed-colour
7739% with tRNS when 256 or fewer unique RGBA values are present).
7740%
7741% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7742% This will be complicated if we limit ourselves to generating MNG-LC
7743% files. For now we ignore disposal method 3 and simply overlay the next
7744% image on it.
7745%
7746% Check for identical PLTE's or PLTE/tRNS combinations and use a
7747% global MNG PLTE or PLTE/tRNS combination when appropriate.
7748% [mostly done 15 June 1999 but still need to take care of tRNS]
7749%
7750% Check for identical sRGB and replace with a global sRGB (and remove
7751% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7752% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7753% local gAMA/cHRM with local sRGB if appropriate).
7754%
7755% Check for identical sBIT chunks and write global ones.
7756%
7757% Provide option to skip writing the signature tEXt chunks.
7758%
7759% Use signatures to detect identical objects and reuse the first
7760% instance of such objects instead of writing duplicate objects.
7761%
7762% Use a smaller-than-32k value of compression window size when
7763% appropriate.
7764%
7765% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7766% ancillary text chunks and save profiles.
7767%
7768% Provide an option to force LC files (to ensure exact framing rate)
7769% instead of VLC.
7770%
7771% Provide an option to force VLC files instead of LC, even when offsets
7772% are present. This will involve expanding the embedded images with a
7773% transparent region at the top and/or left.
7774*/
7775
cristy3ed852e2009-09-05 21:47:34 +00007776static void
glennrpcf002022011-01-30 02:38:15 +00007777Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007778 png_info *ping_info, unsigned char *profile_type, unsigned char
7779 *profile_description, unsigned char *profile_data, png_uint_32 length)
7780{
cristy3ed852e2009-09-05 21:47:34 +00007781 png_textp
7782 text;
7783
cristybb503372010-05-27 20:51:26 +00007784 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007785 i;
7786
7787 unsigned char
7788 *sp;
7789
7790 png_charp
7791 dp;
7792
7793 png_uint_32
7794 allocated_length,
7795 description_length;
7796
7797 unsigned char
7798 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007799
cristy3ed852e2009-09-05 21:47:34 +00007800 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7801 return;
7802
7803 if (image_info->verbose)
7804 {
glennrp0fe50b42010-11-16 03:52:51 +00007805 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7806 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007807 }
glennrp0fe50b42010-11-16 03:52:51 +00007808
glennrpecab7d72013-05-14 22:50:32 +00007809#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +00007810 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7811#else
7812 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7813#endif
cristy3ed852e2009-09-05 21:47:34 +00007814 description_length=(png_uint_32) strlen((const char *) profile_description);
7815 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7816 + description_length);
glennrpecab7d72013-05-14 22:50:32 +00007817#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +00007818 text[0].text=(png_charp) png_malloc(ping,
7819 (png_alloc_size_t) allocated_length);
7820 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7821#else
7822 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7823 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7824#endif
cristy3ed852e2009-09-05 21:47:34 +00007825 text[0].key[0]='\0';
7826 (void) ConcatenateMagickString(text[0].key,
7827 "Raw profile type ",MaxTextExtent);
7828 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7829 sp=profile_data;
7830 dp=text[0].text;
7831 *dp++='\n';
7832 (void) CopyMagickString(dp,(const char *) profile_description,
7833 allocated_length);
7834 dp+=description_length;
7835 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007836 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007837 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007838 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007839
cristybb503372010-05-27 20:51:26 +00007840 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007841 {
7842 if (i%36 == 0)
7843 *dp++='\n';
7844 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7845 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7846 }
glennrp47b9dd52010-11-24 18:12:06 +00007847
cristy3ed852e2009-09-05 21:47:34 +00007848 *dp++='\n';
7849 *dp='\0';
7850 text[0].text_length=(png_size_t) (dp-text[0].text);
7851 text[0].compression=image_info->compression == NoCompression ||
7852 (image_info->compression == UndefinedCompression &&
7853 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007854
cristy3ed852e2009-09-05 21:47:34 +00007855 if (text[0].text_length <= allocated_length)
7856 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007857
cristy3ed852e2009-09-05 21:47:34 +00007858 png_free(ping,text[0].text);
7859 png_free(ping,text[0].key);
7860 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007861}
7862
glennrpcf002022011-01-30 02:38:15 +00007863static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007864 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007865{
7866 char
7867 *name;
7868
7869 const StringInfo
7870 *profile;
7871
7872 unsigned char
7873 *data;
7874
7875 png_uint_32 length;
7876
7877 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007878
7879 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7880 {
cristy3ed852e2009-09-05 21:47:34 +00007881 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007882
cristy3ed852e2009-09-05 21:47:34 +00007883 if (profile != (const StringInfo *) NULL)
7884 {
7885 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007886 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007887
glennrp47b9dd52010-11-24 18:12:06 +00007888 if (LocaleNCompare(name,string,11) == 0)
7889 {
7890 if (logging != MagickFalse)
7891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7892 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007893
glennrpcf002022011-01-30 02:38:15 +00007894 ping_profile=CloneStringInfo(profile);
7895 data=GetStringInfoDatum(ping_profile),
7896 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007897 data[4]=data[3];
7898 data[3]=data[2];
7899 data[2]=data[1];
7900 data[1]=data[0];
7901 (void) WriteBlobMSBULong(image,length-5); /* data length */
7902 (void) WriteBlob(image,length-1,data+1);
7903 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007904 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007905 }
cristy3ed852e2009-09-05 21:47:34 +00007906 }
glennrp47b9dd52010-11-24 18:12:06 +00007907
cristy3ed852e2009-09-05 21:47:34 +00007908 name=GetNextImageProfile(image);
7909 }
glennrp47b9dd52010-11-24 18:12:06 +00007910
cristy3ed852e2009-09-05 21:47:34 +00007911 return(MagickTrue);
7912}
7913
glennrpb9cfe272010-12-21 15:08:06 +00007914
cristy3ed852e2009-09-05 21:47:34 +00007915/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007916static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +00007917 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007918{
cristy09973322013-06-30 21:06:30 +00007919 char
7920 im_vers[32],
7921 libpng_runv[32],
7922 libpng_vers[32],
7923 zlib_runv[32],
7924 zlib_vers[32];
7925
cristy16ea1392012-03-21 20:38:41 +00007926 Image
7927 *image;
7928
7929 ImageInfo
7930 *image_info;
7931
cristy3ed852e2009-09-05 21:47:34 +00007932 char
7933 s[2];
7934
7935 const char
7936 *name,
7937 *property,
7938 *value;
7939
7940 const StringInfo
7941 *profile;
7942
cristy3ed852e2009-09-05 21:47:34 +00007943 int
cristy3ed852e2009-09-05 21:47:34 +00007944 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007945 pass;
7946
glennrpe9c26dc2010-05-30 01:56:35 +00007947 png_byte
7948 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007949
glennrp39992b42010-11-14 00:03:43 +00007950 png_color
7951 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007952
glennrp5af765f2010-03-30 11:12:18 +00007953 png_color_16
7954 ping_background,
7955 ping_trans_color;
7956
cristy3ed852e2009-09-05 21:47:34 +00007957 png_info
7958 *ping_info;
7959
7960 png_struct
7961 *ping;
7962
glennrp5af765f2010-03-30 11:12:18 +00007963 png_uint_32
7964 ping_height,
7965 ping_width;
7966
cristybb503372010-05-27 20:51:26 +00007967 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007968 y;
7969
7970 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007971 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007972 logging,
glennrp58e01762011-01-07 15:28:54 +00007973 matte,
7974
glennrpda8f3a72011-02-27 23:54:12 +00007975 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007976 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007977 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007978 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007979 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007980 ping_have_bKGD,
glennrp918b9dc2013-04-03 13:41:41 +00007981 ping_have_iCCP,
glennrp991d11d2010-11-12 21:55:28 +00007982 ping_have_pHYs,
glennrp918b9dc2013-04-03 13:41:41 +00007983 ping_have_sRGB,
glennrp991d11d2010-11-12 21:55:28 +00007984 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007985
7986 ping_exclude_bKGD,
7987 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007988 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007989 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007990 ping_exclude_gAMA,
7991 ping_exclude_iCCP,
7992 /* ping_exclude_iTXt, */
7993 ping_exclude_oFFs,
7994 ping_exclude_pHYs,
7995 ping_exclude_sRGB,
7996 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007997 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007998 ping_exclude_vpAg,
7999 ping_exclude_zCCP, /* hex-encoded iCCP */
8000 ping_exclude_zTXt,
8001
glennrp8d3d6e52011-04-19 04:39:51 +00008002 ping_preserve_colormap,
glennrpecab7d72013-05-14 22:50:32 +00008003 ping_preserve_iCCP,
glennrp0e8ea192010-12-24 18:00:33 +00008004 ping_need_colortype_warning,
8005
glennrp82b3c532011-03-22 19:20:54 +00008006 status,
glennrp8ca51ad2011-05-12 21:22:32 +00008007 tried_332,
glennrpd3371642011-03-22 19:42:23 +00008008 tried_333,
8009 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00008010
cristy09973322013-06-30 21:06:30 +00008011 MemoryInfo
cristyaf1534a2013-06-30 21:12:25 +00008012 *volatile pixel_info;
cristy09973322013-06-30 21:06:30 +00008013
cristy3ed852e2009-09-05 21:47:34 +00008014 QuantumInfo
8015 *quantum_info;
8016
cristy16ea1392012-03-21 20:38:41 +00008017 PNGErrorInfo
8018 error_info;
8019
cristybb503372010-05-27 20:51:26 +00008020 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00008021 i,
8022 x;
8023
8024 unsigned char
cristy09973322013-06-30 21:06:30 +00008025 *ping_pixels;
glennrpd0cae252013-03-15 22:30:41 +00008026
glennrp5af765f2010-03-30 11:12:18 +00008027 volatile int
glennrpf09bded2011-01-08 01:15:59 +00008028 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00008029 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00008030 ping_color_type,
8031 ping_interlace_method,
8032 ping_compression_method,
8033 ping_filter_method,
8034 ping_num_trans;
8035
cristybb503372010-05-27 20:51:26 +00008036 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00008037 image_depth,
8038 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008039
cristybb503372010-05-27 20:51:26 +00008040 size_t
cristy3ed852e2009-09-05 21:47:34 +00008041 quality,
8042 rowbytes,
8043 save_image_depth;
8044
glennrpdfd70802010-11-14 01:23:35 +00008045 int
glennrpfd05d622011-02-25 04:10:33 +00008046 j,
glennrpf09bded2011-01-08 01:15:59 +00008047 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00008048 number_opaque,
8049 number_semitransparent,
8050 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00008051 ping_pHYs_unit_type;
8052
8053 png_uint_32
8054 ping_pHYs_x_resolution,
8055 ping_pHYs_y_resolution;
8056
cristy3ed852e2009-09-05 21:47:34 +00008057 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00008058 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00008059
cristy16ea1392012-03-21 20:38:41 +00008060 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8061 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8062 if (image_info == (ImageInfo *) NULL)
8063 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00008064
glennrpd0cae252013-03-15 22:30:41 +00008065 /* Define these outside of the following "if logging()" block so they will
8066 * show in debuggers.
8067 */
8068 *im_vers='\0';
8069 (void) ConcatenateMagickString(im_vers,
8070 MagickLibVersionText,MaxTextExtent);
8071 (void) ConcatenateMagickString(im_vers,
8072 MagickLibAddendum,MaxTextExtent);
glennrpec0ddbc2013-03-16 12:40:12 +00008073
glennrpd0cae252013-03-15 22:30:41 +00008074 *libpng_vers='\0';
8075 (void) ConcatenateMagickString(libpng_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00008076 PNG_LIBPNG_VER_STRING,32);
8077 *libpng_runv='\0';
8078 (void) ConcatenateMagickString(libpng_runv,
8079 png_get_libpng_ver(NULL),32);
8080
glennrpd0cae252013-03-15 22:30:41 +00008081 *zlib_vers='\0';
8082 (void) ConcatenateMagickString(zlib_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00008083 ZLIB_VERSION,32);
8084 *zlib_runv='\0';
8085 (void) ConcatenateMagickString(zlib_runv,
8086 zlib_version,32);
8087
glennrpd0cae252013-03-15 22:30:41 +00008088 if (logging)
8089 {
8090 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8091 im_vers);
8092 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8093 libpng_vers);
glennrpec0ddbc2013-03-16 12:40:12 +00008094 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8095 {
8096 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8097 libpng_runv);
8098 }
glennrpd0cae252013-03-15 22:30:41 +00008099 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8100 zlib_vers);
glennrpec0ddbc2013-03-16 12:40:12 +00008101 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8102 {
8103 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8104 zlib_runv);
8105 }
glennrpd0cae252013-03-15 22:30:41 +00008106 }
8107
glennrp5af765f2010-03-30 11:12:18 +00008108 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00008109 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00008110 ping_color_type=0,
8111 ping_interlace_method=0,
8112 ping_compression_method=0,
8113 ping_filter_method=0,
8114 ping_num_trans = 0;
8115
8116 ping_background.red = 0;
8117 ping_background.green = 0;
8118 ping_background.blue = 0;
8119 ping_background.gray = 0;
8120 ping_background.index = 0;
8121
8122 ping_trans_color.red=0;
8123 ping_trans_color.green=0;
8124 ping_trans_color.blue=0;
8125 ping_trans_color.gray=0;
8126
glennrpdfd70802010-11-14 01:23:35 +00008127 ping_pHYs_unit_type = 0;
8128 ping_pHYs_x_resolution = 0;
8129 ping_pHYs_y_resolution = 0;
8130
glennrpda8f3a72011-02-27 23:54:12 +00008131 ping_have_blob=MagickFalse;
glennrpf70c4d22013-03-19 15:26:48 +00008132 ping_have_cheap_transparency=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00008133 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00008134 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00008135 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00008136 ping_have_bKGD=MagickFalse;
glennrp918b9dc2013-04-03 13:41:41 +00008137 ping_have_iCCP=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00008138 ping_have_pHYs=MagickFalse;
glennrp918b9dc2013-04-03 13:41:41 +00008139 ping_have_sRGB=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00008140 ping_have_tRNS=MagickFalse;
8141
glennrp0e8ea192010-12-24 18:00:33 +00008142 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8143 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00008144 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00008145 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00008146 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00008147 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8148 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8149 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8150 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8151 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8152 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00008153 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00008154 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8155 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8156 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8157
glennrp8d3d6e52011-04-19 04:39:51 +00008158 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrpecab7d72013-05-14 22:50:32 +00008159 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
glennrp0e8ea192010-12-24 18:00:33 +00008160 ping_need_colortype_warning = MagickFalse;
8161
cristy0d57eec2011-09-04 22:13:56 +00008162 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8163 * i.e., eliminate the ICC profile and set image->rendering_intent.
8164 * Note that this will not involve any changes to the actual pixels
8165 * but merely passes information to applications that read the resulting
8166 * PNG image.
glennrpecab7d72013-05-14 22:50:32 +00008167 *
8168 * To do: recognize other variants of the sRGB profile, using the CRC to
8169 * verify all recognized variants including the 7 already known.
8170 *
8171 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8172 *
8173 * Use something other than image->rendering_intent to record the fact
8174 * that the sRGB profile was found.
8175 *
8176 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8177 * profile. Record the Blackpoint Compensation, if any.
cristy0d57eec2011-09-04 22:13:56 +00008178 */
glennrpecab7d72013-05-14 22:50:32 +00008179 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
cristy0d57eec2011-09-04 22:13:56 +00008180 {
8181 char
8182 *name;
8183
8184 const StringInfo
8185 *profile;
8186
8187 ResetImageProfileIterator(image);
8188 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8189 {
8190 profile=GetImageProfile(image,name);
8191
8192 if (profile != (StringInfo *) NULL)
8193 {
8194 if ((LocaleCompare(name,"ICC") == 0) ||
glennrpecab7d72013-05-14 22:50:32 +00008195 (LocaleCompare(name,"ICM") == 0))
glennrpee7b4c02011-10-04 01:21:09 +00008196
glennrpecab7d72013-05-14 22:50:32 +00008197 {
8198 int
8199 icheck,
8200 got_crc=0;
8201
glennrpee7b4c02011-10-04 01:21:09 +00008202
8203 png_uint_32
8204 length,
glennrpecab7d72013-05-14 22:50:32 +00008205 profile_crc=0;
glennrpee7b4c02011-10-04 01:21:09 +00008206
cristy0d57eec2011-09-04 22:13:56 +00008207 unsigned char
8208 *data;
8209
glennrp29a106e2011-09-06 17:11:42 +00008210 length=(png_uint_32) GetStringInfoLength(profile);
8211
glennrpecab7d72013-05-14 22:50:32 +00008212 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
cristy0d57eec2011-09-04 22:13:56 +00008213 {
glennrpecab7d72013-05-14 22:50:32 +00008214 if (length == sRGB_info[icheck].len)
glennrp29a106e2011-09-06 17:11:42 +00008215 {
glennrpecab7d72013-05-14 22:50:32 +00008216 if (got_crc == 0)
8217 {
8218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00008219 " Got a %lu-byte ICC profile (potentially sRGB)",
8220 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00008221
glennrpecab7d72013-05-14 22:50:32 +00008222 data=GetStringInfoDatum(profile);
8223 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00008224
glennrpecab7d72013-05-14 22:50:32 +00008225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8226 " with crc=%8x",(unsigned int) profile_crc);
8227 got_crc++;
8228 }
glennrpee7b4c02011-10-04 01:21:09 +00008229
glennrpecab7d72013-05-14 22:50:32 +00008230 if (profile_crc == sRGB_info[icheck].crc)
glennrpee7b4c02011-10-04 01:21:09 +00008231 {
8232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpecab7d72013-05-14 22:50:32 +00008233 " It is sRGB with rendering intent = %s",
8234 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8235 sRGB_info[icheck].intent));
glennrpee7b4c02011-10-04 01:21:09 +00008236 if (image->rendering_intent==UndefinedIntent)
glennrpecab7d72013-05-14 22:50:32 +00008237 {
8238 image->rendering_intent=
8239 Magick_RenderingIntent_from_PNG_RenderingIntent(
8240 sRGB_info[icheck].intent);
8241 }
8242 ping_exclude_iCCP = MagickTrue;
8243 ping_exclude_zCCP = MagickTrue;
8244 ping_have_sRGB = MagickTrue;
glennrpee7b4c02011-10-04 01:21:09 +00008245 break;
8246 }
glennrp29a106e2011-09-06 17:11:42 +00008247 }
glennrp29a106e2011-09-06 17:11:42 +00008248 }
glennrpecab7d72013-05-14 22:50:32 +00008249 if (sRGB_info[icheck].len == 0)
glennrp29a106e2011-09-06 17:11:42 +00008250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpecab7d72013-05-14 22:50:32 +00008251 " Got a %lu-byte ICC profile not recognized as sRGB",
glennrp29a106e2011-09-06 17:11:42 +00008252 (unsigned long) length);
8253 }
cristy0d57eec2011-09-04 22:13:56 +00008254 }
8255 name=GetNextImageProfile(image);
8256 }
8257 }
8258
glennrp8bb3a022010-12-13 20:40:04 +00008259 number_opaque = 0;
8260 number_semitransparent = 0;
8261 number_transparent = 0;
8262
glennrpfd05d622011-02-25 04:10:33 +00008263 if (logging != MagickFalse)
8264 {
8265 if (image->storage_class == UndefinedClass)
8266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8267 " storage_class=UndefinedClass");
8268 if (image->storage_class == DirectClass)
8269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8270 " storage_class=DirectClass");
8271 if (image->storage_class == PseudoClass)
8272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8273 " storage_class=PseudoClass");
8274 }
glennrp28af3712011-04-06 18:07:30 +00008275
glennrp750105b2012-04-25 16:20:45 +00008276 if (image->storage_class == PseudoClass &&
glennrp7e65e932011-08-19 02:31:16 +00008277 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
glennrpfd164d22013-01-26 21:10:22 +00008278 mng_info->write_png48 || mng_info->write_png64 ||
8279 (mng_info->write_png_colortype != 1 &&
8280 mng_info->write_png_colortype != 5)))
glennrp7e65e932011-08-19 02:31:16 +00008281 {
cristy16ea1392012-03-21 20:38:41 +00008282 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00008283 image->storage_class = DirectClass;
8284 }
8285
glennrpc6c391a2011-04-27 02:23:56 +00008286 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00008287 {
glennrpc6c391a2011-04-27 02:23:56 +00008288 if (image->storage_class != PseudoClass && image->colormap != NULL)
8289 {
8290 /* Free the bogus colormap; it can cause trouble later */
8291 if (logging != MagickFalse)
8292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8293 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00008294 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00008295 image->colormap=NULL;
8296 }
glennrp28af3712011-04-06 18:07:30 +00008297 }
glennrpbb4f99d2011-05-22 11:13:17 +00008298
glennrpc28acd62013-12-05 20:21:45 +00008299 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00008300 (void) TransformImageColorspace(image,sRGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00008301
glennrp3241bd02010-12-12 04:36:28 +00008302 /*
8303 Sometimes we get PseudoClass images whose RGB values don't match
8304 the colors in the colormap. This code syncs the RGB values.
8305 */
8306 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristy16ea1392012-03-21 20:38:41 +00008307 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00008308
glennrpa6a06632011-01-19 15:15:34 +00008309#if (MAGICKCORE_QUANTUM_DEPTH == 8)
8310 if (image->depth > 8)
8311 {
8312 if (logging != MagickFalse)
8313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8314 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8315
8316 image->depth=8;
8317 }
8318#endif
8319
glennrp8e58efd2011-05-20 12:16:29 +00008320 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00008321 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
8322 {
cristy16ea1392012-03-21 20:38:41 +00008323 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00008324 *r;
8325
glennrp8e58efd2011-05-20 12:16:29 +00008326 if (image->depth > 8)
8327 {
8328#if MAGICKCORE_QUANTUM_DEPTH > 16
8329 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00008330 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008331
8332 for (y=0; y < (ssize_t) image->rows; y++)
8333 {
cristy16ea1392012-03-21 20:38:41 +00008334 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008335
cristy16ea1392012-03-21 20:38:41 +00008336 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008337 break;
8338
8339 for (x=0; x < (ssize_t) image->columns; x++)
8340 {
cristy16ea1392012-03-21 20:38:41 +00008341 LBR16PixelRGBA(r);
8342 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008343 }
glennrpbb4f99d2011-05-22 11:13:17 +00008344
glennrp8e58efd2011-05-20 12:16:29 +00008345 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8346 break;
8347 }
8348
8349 if (image->storage_class == PseudoClass && image->colormap != NULL)
8350 {
cristy3e08f112011-05-24 13:19:30 +00008351 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008352 {
glennrp91d99252011-06-25 14:30:13 +00008353 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008354 }
8355 }
8356#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8357 }
8358
8359 else if (image->depth > 4)
8360 {
8361#if MAGICKCORE_QUANTUM_DEPTH > 8
8362 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00008363 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008364
8365 for (y=0; y < (ssize_t) image->rows; y++)
8366 {
cristy16ea1392012-03-21 20:38:41 +00008367 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008368
cristy16ea1392012-03-21 20:38:41 +00008369 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008370 break;
8371
8372 for (x=0; x < (ssize_t) image->columns; x++)
8373 {
cristy16ea1392012-03-21 20:38:41 +00008374 LBR08PixelRGBA(r);
8375 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008376 }
glennrpbb4f99d2011-05-22 11:13:17 +00008377
glennrp8e58efd2011-05-20 12:16:29 +00008378 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8379 break;
8380 }
8381
8382 if (image->storage_class == PseudoClass && image->colormap != NULL)
8383 {
cristy3e08f112011-05-24 13:19:30 +00008384 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008385 {
glennrp91d99252011-06-25 14:30:13 +00008386 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008387 }
8388 }
8389#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8390 }
8391 else
8392 if (image->depth > 2)
8393 {
8394 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00008395 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008396
8397 for (y=0; y < (ssize_t) image->rows; y++)
8398 {
cristy16ea1392012-03-21 20:38:41 +00008399 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008400
cristy16ea1392012-03-21 20:38:41 +00008401 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008402 break;
8403
8404 for (x=0; x < (ssize_t) image->columns; x++)
8405 {
cristy16ea1392012-03-21 20:38:41 +00008406 LBR04PixelRGBA(r);
8407 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008408 }
glennrpbb4f99d2011-05-22 11:13:17 +00008409
glennrp8e58efd2011-05-20 12:16:29 +00008410 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8411 break;
8412 }
8413
8414 if (image->storage_class == PseudoClass && image->colormap != NULL)
8415 {
cristy3e08f112011-05-24 13:19:30 +00008416 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008417 {
glennrp91d99252011-06-25 14:30:13 +00008418 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008419 }
8420 }
8421 }
8422
8423 else if (image->depth > 1)
8424 {
8425 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00008426 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008427
8428 for (y=0; y < (ssize_t) image->rows; y++)
8429 {
cristy16ea1392012-03-21 20:38:41 +00008430 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008431
cristy16ea1392012-03-21 20:38:41 +00008432 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008433 break;
8434
8435 for (x=0; x < (ssize_t) image->columns; x++)
8436 {
cristy16ea1392012-03-21 20:38:41 +00008437 LBR02PixelRGBA(r);
8438 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008439 }
glennrpbb4f99d2011-05-22 11:13:17 +00008440
glennrp8e58efd2011-05-20 12:16:29 +00008441 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8442 break;
8443 }
8444
8445 if (image->storage_class == PseudoClass && image->colormap != NULL)
8446 {
cristy3e08f112011-05-24 13:19:30 +00008447 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008448 {
glennrp91d99252011-06-25 14:30:13 +00008449 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008450 }
8451 }
8452 }
8453 else
8454 {
8455 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00008456 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008457
8458 for (y=0; y < (ssize_t) image->rows; y++)
8459 {
cristy16ea1392012-03-21 20:38:41 +00008460 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008461
cristy16ea1392012-03-21 20:38:41 +00008462 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008463 break;
8464
8465 for (x=0; x < (ssize_t) image->columns; x++)
8466 {
cristy16ea1392012-03-21 20:38:41 +00008467 LBR01PixelRGBA(r);
8468 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008469 }
glennrpbb4f99d2011-05-22 11:13:17 +00008470
glennrp8e58efd2011-05-20 12:16:29 +00008471 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8472 break;
8473 }
8474
8475 if (image->storage_class == PseudoClass && image->colormap != NULL)
8476 {
cristy3e08f112011-05-24 13:19:30 +00008477 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008478 {
glennrp91d99252011-06-25 14:30:13 +00008479 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008480 }
8481 }
8482 }
glennrp9d0ea4d2011-04-22 18:35:57 +00008483 }
8484
glennrp67b9c1a2011-04-22 18:47:36 +00008485 /* To do: set to next higher multiple of 8 */
8486 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00008487 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00008488
glennrp2b013e42010-11-24 16:55:50 +00008489#if (MAGICKCORE_QUANTUM_DEPTH > 16)
8490 /* PNG does not handle depths greater than 16 so reduce it even
8491 * if lossy
8492 */
glennrp8e58efd2011-05-20 12:16:29 +00008493 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00008494 image->depth=16;
8495#endif
8496
glennrp3faa9a32011-04-23 14:00:25 +00008497#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00008498 if (image->depth > 8)
8499 {
8500 /* To do: fill low byte properly */
8501 image->depth=16;
8502 }
8503
glennrpc722dd82011-02-24 05:13:21 +00008504 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristy16ea1392012-03-21 20:38:41 +00008505 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00008506 image->depth = 8;
8507#endif
8508
glennrpd0ee5a22014-03-28 14:28:47 +00008509 image_colors = (int) image->colors;
8510 number_opaque = (int) image->colors;
8511 number_transparent = 0;
8512 number_semitransparent = 0;
8513
glennrp197c8e62014-04-22 23:45:20 +00008514 if (mng_info->write_png_colortype &&
glennrpa8036d62012-11-04 01:46:06 +00008515 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8516 mng_info->write_png_colortype < 4 &&
8517 image->alpha_trait != BlendPixelTrait)))
8518 {
8519 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8520 * are not going to need the result.
8521 */
glennrpa8036d62012-11-04 01:46:06 +00008522 if (mng_info->write_png_colortype == 1 ||
8523 mng_info->write_png_colortype == 5)
8524 ping_have_color=MagickFalse;
glennrpa8036d62012-11-04 01:46:06 +00008525
8526 if (image->alpha_trait == BlendPixelTrait)
8527 {
8528 number_transparent = 2;
8529 number_semitransparent = 1;
8530 }
glennrpa8036d62012-11-04 01:46:06 +00008531 }
8532
glennrp197c8e62014-04-22 23:45:20 +00008533 if (mng_info->write_png_colortype < 7)
glennrpa8036d62012-11-04 01:46:06 +00008534 {
8535 /* BUILD_PALETTE
8536 *
8537 * Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00008538 * we reduce the transparency to binary and run again, then if there
8539 * 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 +00008540 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8541 * palette. Then (To do) we take care of a final reduction that is only
8542 * needed if there are still 256 colors present and one of them has both
8543 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00008544 */
glennrp82b3c532011-03-22 19:20:54 +00008545
glennrp8ca51ad2011-05-12 21:22:32 +00008546 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008547 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00008548 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008549
glennrp8ca51ad2011-05-12 21:22:32 +00008550 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00008551 {
glennrpa8036d62012-11-04 01:46:06 +00008552 /*
glennrpd71e86a2011-02-24 01:28:37 +00008553 * Sometimes we get DirectClass images that have 256 colors or fewer.
8554 * This code will build a colormap.
8555 *
8556 * Also, sometimes we get PseudoClass images with an out-of-date
8557 * colormap. This code will replace the colormap with a new one.
8558 * Sometimes we get PseudoClass images that have more than 256 colors.
8559 * This code will delete the colormap and change the image to
8560 * DirectClass.
8561 *
cristy8a46d822012-08-28 23:32:39 +00008562 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008563 * even though it sometimes contains left-over non-opaque values.
8564 *
8565 * Also we gather some information (number of opaque, transparent,
8566 * and semitransparent pixels, and whether the image has any non-gray
8567 * pixels or only black-and-white pixels) that we might need later.
8568 *
8569 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8570 * we need to check for bogus non-opaque values, at least.
8571 */
glennrp3c218112010-11-27 15:31:26 +00008572
glennrpd71e86a2011-02-24 01:28:37 +00008573 int
8574 n;
glennrp3c218112010-11-27 15:31:26 +00008575
cristy16ea1392012-03-21 20:38:41 +00008576 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008577 opaque[260],
8578 semitransparent[260],
8579 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008580
cristy16ea1392012-03-21 20:38:41 +00008581 register const Quantum
8582 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008583
cristy16ea1392012-03-21 20:38:41 +00008584 register Quantum
8585 *q,
glennrpfd05d622011-02-25 04:10:33 +00008586 *r;
8587
glennrpd71e86a2011-02-24 01:28:37 +00008588 if (logging != MagickFalse)
8589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8590 " Enter BUILD_PALETTE:");
8591
8592 if (logging != MagickFalse)
8593 {
glennrp03812ae2010-12-24 01:31:34 +00008594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008595 " image->columns=%.20g",(double) image->columns);
8596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8597 " image->rows=%.20g",(double) image->rows);
8598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00008599 " image->alpha_trait=%.20g",(double) image->alpha_trait);
glennrpd71e86a2011-02-24 01:28:37 +00008600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8601 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008602
glennrpfd05d622011-02-25 04:10:33 +00008603 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008604 {
8605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008606 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008608 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008609
glennrpd71e86a2011-02-24 01:28:37 +00008610 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008611 {
glennrpd71e86a2011-02-24 01:28:37 +00008612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8613 " %d (%d,%d,%d,%d)",
8614 (int) i,
8615 (int) image->colormap[i].red,
8616 (int) image->colormap[i].green,
8617 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008618 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008619 }
glennrp2cc891a2010-12-24 13:44:32 +00008620
glennrpd71e86a2011-02-24 01:28:37 +00008621 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8622 {
8623 if (i > 255)
8624 {
8625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8626 " %d (%d,%d,%d,%d)",
8627 (int) i,
8628 (int) image->colormap[i].red,
8629 (int) image->colormap[i].green,
8630 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008631 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008632 }
8633 }
glennrp03812ae2010-12-24 01:31:34 +00008634 }
glennrp7ddcc222010-12-11 05:01:05 +00008635
glennrpd71e86a2011-02-24 01:28:37 +00008636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8637 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008638
glennrpd71e86a2011-02-24 01:28:37 +00008639 if (image->colors == 0)
cristy16ea1392012-03-21 20:38:41 +00008640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8641 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008642
glennrp8d3d6e52011-04-19 04:39:51 +00008643 if (ping_preserve_colormap == MagickFalse)
8644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8645 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008646 }
8647
glennrpd71e86a2011-02-24 01:28:37 +00008648 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008649 number_opaque = 0;
8650 number_semitransparent = 0;
8651 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008652
8653 for (y=0; y < (ssize_t) image->rows; y++)
8654 {
8655 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8656
cristy16ea1392012-03-21 20:38:41 +00008657 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008658 break;
8659
8660 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008661 {
cristy8a46d822012-08-28 23:32:39 +00008662 if (image->alpha_trait != BlendPixelTrait ||
cristy16ea1392012-03-21 20:38:41 +00008663 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008664 {
8665 if (number_opaque < 259)
8666 {
8667 if (number_opaque == 0)
8668 {
cristy16ea1392012-03-21 20:38:41 +00008669 GetPixelInfoPixel(image, q, opaque);
8670 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008671 number_opaque=1;
8672 }
glennrp2cc891a2010-12-24 13:44:32 +00008673
glennrpd71e86a2011-02-24 01:28:37 +00008674 for (i=0; i< (ssize_t) number_opaque; i++)
8675 {
cristy16ea1392012-03-21 20:38:41 +00008676 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008677 break;
8678 }
glennrp7ddcc222010-12-11 05:01:05 +00008679
cristy16ea1392012-03-21 20:38:41 +00008680 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008681 {
8682 number_opaque++;
cristy16ea1392012-03-21 20:38:41 +00008683 GetPixelInfoPixel(image, q, opaque+i);
8684 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008685 }
8686 }
8687 }
cristy16ea1392012-03-21 20:38:41 +00008688 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008689 {
8690 if (number_transparent < 259)
8691 {
8692 if (number_transparent == 0)
8693 {
cristy16ea1392012-03-21 20:38:41 +00008694 GetPixelInfoPixel(image, q, transparent);
8695 ping_trans_color.red=(unsigned short)
8696 GetPixelRed(image,q);
8697 ping_trans_color.green=(unsigned short)
8698 GetPixelGreen(image,q);
8699 ping_trans_color.blue=(unsigned short)
8700 GetPixelBlue(image,q);
8701 ping_trans_color.gray=(unsigned short)
cristy972d1c42012-07-14 23:29:14 +00008702 GetPixelGray(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008703 number_transparent = 1;
8704 }
8705
8706 for (i=0; i< (ssize_t) number_transparent; i++)
8707 {
cristy16ea1392012-03-21 20:38:41 +00008708 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008709 break;
8710 }
8711
8712 if (i == (ssize_t) number_transparent &&
8713 number_transparent < 259)
8714 {
8715 number_transparent++;
cristy16ea1392012-03-21 20:38:41 +00008716 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008717 }
8718 }
8719 }
8720 else
8721 {
8722 if (number_semitransparent < 259)
8723 {
8724 if (number_semitransparent == 0)
8725 {
cristy16ea1392012-03-21 20:38:41 +00008726 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008727 number_semitransparent = 1;
8728 }
8729
8730 for (i=0; i< (ssize_t) number_semitransparent; i++)
8731 {
cristy16ea1392012-03-21 20:38:41 +00008732 if (IsPixelEquivalent(image,q, semitransparent+i)
8733 && GetPixelAlpha(image,q) ==
8734 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008735 break;
8736 }
8737
8738 if (i == (ssize_t) number_semitransparent &&
8739 number_semitransparent < 259)
8740 {
8741 number_semitransparent++;
cristy16ea1392012-03-21 20:38:41 +00008742 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008743 }
8744 }
8745 }
cristy16ea1392012-03-21 20:38:41 +00008746 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008747 }
8748 }
8749
cristy4054bfb2011-08-29 23:41:39 +00008750 if (mng_info->write_png8 == MagickFalse &&
8751 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008752 {
8753 /* Add the background color to the palette, if it
8754 * isn't already there.
8755 */
glennrpc6c391a2011-04-27 02:23:56 +00008756 if (logging != MagickFalse)
8757 {
8758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8759 " Check colormap for background (%d,%d,%d)",
8760 (int) image->background_color.red,
8761 (int) image->background_color.green,
8762 (int) image->background_color.blue);
8763 }
glennrpd71e86a2011-02-24 01:28:37 +00008764 for (i=0; i<number_opaque; i++)
8765 {
glennrpca7ad3a2011-04-26 04:44:54 +00008766 if (opaque[i].red == image->background_color.red &&
8767 opaque[i].green == image->background_color.green &&
8768 opaque[i].blue == image->background_color.blue)
8769 break;
glennrpd71e86a2011-02-24 01:28:37 +00008770 }
glennrpd71e86a2011-02-24 01:28:37 +00008771 if (number_opaque < 259 && i == number_opaque)
8772 {
glennrp8e045c82011-04-27 16:40:27 +00008773 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008774 ping_background.index = i;
glennrp388a8c82012-06-27 13:41:51 +00008775 number_opaque++;
glennrpc6c391a2011-04-27 02:23:56 +00008776 if (logging != MagickFalse)
8777 {
8778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8779 " background_color index is %d",(int) i);
8780 }
8781
glennrpd71e86a2011-02-24 01:28:37 +00008782 }
glennrpa080bc32011-03-11 18:03:44 +00008783 else if (logging != MagickFalse)
8784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8785 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008786 }
8787
8788 image_colors=number_opaque+number_transparent+number_semitransparent;
8789
8790 if (logging != MagickFalse)
8791 {
8792 if (image_colors > 256)
8793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8794 " image has more than 256 colors");
8795
8796 else
8797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8798 " image has %d colors",image_colors);
8799 }
8800
glennrp8d3d6e52011-04-19 04:39:51 +00008801 if (ping_preserve_colormap != MagickFalse)
8802 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008803
glennrpfd05d622011-02-25 04:10:33 +00008804 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008805 {
8806 ping_have_color=MagickFalse;
8807 ping_have_non_bw=MagickFalse;
8808
glennrp45d4c342013-12-05 21:24:54 +00008809 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
glennrp0fa25802012-07-20 14:01:06 +00008810 {
glennrp96bc6202013-12-03 19:21:04 +00008811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8812 "incompatible colorspace");
glennrp0fa25802012-07-20 14:01:06 +00008813 ping_have_color=MagickTrue;
glennrp98b95772012-11-29 01:32:00 +00008814 ping_have_non_bw=MagickTrue;
glennrp0fa25802012-07-20 14:01:06 +00008815 }
8816
glennrpd71e86a2011-02-24 01:28:37 +00008817 if(image_colors > 256)
8818 {
8819 for (y=0; y < (ssize_t) image->rows; y++)
8820 {
8821 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8822
cristy16ea1392012-03-21 20:38:41 +00008823 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008824 break;
8825
glennrpe5e6b802011-07-20 14:44:40 +00008826 s=q;
8827 for (x=0; x < (ssize_t) image->columns; x++)
8828 {
cristy16ea1392012-03-21 20:38:41 +00008829 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8830 GetPixelRed(image,s) != GetPixelBlue(image,s))
glennrpe5e6b802011-07-20 14:44:40 +00008831 {
8832 ping_have_color=MagickTrue;
8833 ping_have_non_bw=MagickTrue;
8834 break;
8835 }
cristy16ea1392012-03-21 20:38:41 +00008836 s+=GetPixelChannels(image);
glennrpe5e6b802011-07-20 14:44:40 +00008837 }
8838
8839 if (ping_have_color != MagickFalse)
8840 break;
8841
glennrpd71e86a2011-02-24 01:28:37 +00008842 /* Worst case is black-and-white; we are looking at every
8843 * pixel twice.
8844 */
8845
glennrpd71e86a2011-02-24 01:28:37 +00008846 if (ping_have_non_bw == MagickFalse)
8847 {
8848 s=q;
8849 for (x=0; x < (ssize_t) image->columns; x++)
8850 {
cristy16ea1392012-03-21 20:38:41 +00008851 if (GetPixelRed(image,s) != 0 &&
8852 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008853 {
8854 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008855 break;
glennrpd71e86a2011-02-24 01:28:37 +00008856 }
cristy16ea1392012-03-21 20:38:41 +00008857 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008858 }
glennrpe5e6b802011-07-20 14:44:40 +00008859 }
glennrpd71e86a2011-02-24 01:28:37 +00008860 }
glennrpbb4f99d2011-05-22 11:13:17 +00008861 }
8862 }
glennrpd71e86a2011-02-24 01:28:37 +00008863
8864 if (image_colors < 257)
8865 {
cristy16ea1392012-03-21 20:38:41 +00008866 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008867 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008868
glennrpd71e86a2011-02-24 01:28:37 +00008869 /*
8870 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008871 */
8872
glennrpd71e86a2011-02-24 01:28:37 +00008873 if (logging != MagickFalse)
8874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8875 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008876
glennrpd71e86a2011-02-24 01:28:37 +00008877 /* Sort palette, transparent first */;
8878
8879 n = 0;
8880
8881 for (i=0; i<number_transparent; i++)
8882 colormap[n++] = transparent[i];
8883
8884 for (i=0; i<number_semitransparent; i++)
8885 colormap[n++] = semitransparent[i];
8886
8887 for (i=0; i<number_opaque; i++)
8888 colormap[n++] = opaque[i];
8889
glennrpc6c391a2011-04-27 02:23:56 +00008890 ping_background.index +=
8891 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008892
glennrpd71e86a2011-02-24 01:28:37 +00008893 /* image_colors < 257; search the colormap instead of the pixels
8894 * to get ping_have_color and ping_have_non_bw
8895 */
8896 for (i=0; i<n; i++)
8897 {
8898 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008899 {
glennrpd71e86a2011-02-24 01:28:37 +00008900 if (colormap[i].red != colormap[i].green ||
8901 colormap[i].red != colormap[i].blue)
8902 {
8903 ping_have_color=MagickTrue;
8904 ping_have_non_bw=MagickTrue;
8905 break;
8906 }
8907 }
8908
8909 if (ping_have_non_bw == MagickFalse)
8910 {
8911 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008912 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008913 }
glennrp8bb3a022010-12-13 20:40:04 +00008914 }
8915
glennrpd71e86a2011-02-24 01:28:37 +00008916 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8917 (number_transparent == 0 && number_semitransparent == 0)) &&
8918 (((mng_info->write_png_colortype-1) ==
8919 PNG_COLOR_TYPE_PALETTE) ||
8920 (mng_info->write_png_colortype == 0)))
8921 {
glennrp6185c532011-01-14 17:58:40 +00008922 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008923 {
glennrpd71e86a2011-02-24 01:28:37 +00008924 if (n != (ssize_t) image_colors)
8925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8926 " image_colors (%d) and n (%d) don't match",
8927 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008928
glennrpd71e86a2011-02-24 01:28:37 +00008929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8930 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008931 }
glennrp03812ae2010-12-24 01:31:34 +00008932
glennrpd71e86a2011-02-24 01:28:37 +00008933 image->colors = image_colors;
8934
cristy16ea1392012-03-21 20:38:41 +00008935 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008936 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008937 ThrowWriterException(ResourceLimitError,
8938 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008939
8940 for (i=0; i< (ssize_t) image_colors; i++)
8941 image->colormap[i] = colormap[i];
8942
8943 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008944 {
glennrpd71e86a2011-02-24 01:28:37 +00008945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8946 " image->colors=%d (%d)",
8947 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008948
glennrpd71e86a2011-02-24 01:28:37 +00008949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8950 " Update the pixel indexes");
8951 }
glennrp6185c532011-01-14 17:58:40 +00008952
glennrpfd05d622011-02-25 04:10:33 +00008953 /* Sync the pixel indices with the new colormap */
8954
glennrpd71e86a2011-02-24 01:28:37 +00008955 for (y=0; y < (ssize_t) image->rows; y++)
8956 {
cristy16ea1392012-03-21 20:38:41 +00008957 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008958
cristy16ea1392012-03-21 20:38:41 +00008959 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008960 break;
glennrp6185c532011-01-14 17:58:40 +00008961
glennrpd71e86a2011-02-24 01:28:37 +00008962 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008963 {
glennrpd71e86a2011-02-24 01:28:37 +00008964 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008965 {
cristy8a46d822012-08-28 23:32:39 +00008966 if ((image->alpha_trait != BlendPixelTrait ||
cristy16ea1392012-03-21 20:38:41 +00008967 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8968 image->colormap[i].red == GetPixelRed(image,q) &&
8969 image->colormap[i].green == GetPixelGreen(image,q) &&
8970 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008971 {
cristy16ea1392012-03-21 20:38:41 +00008972 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008973 break;
glennrp6185c532011-01-14 17:58:40 +00008974 }
glennrp6185c532011-01-14 17:58:40 +00008975 }
cristy16ea1392012-03-21 20:38:41 +00008976 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008977 }
glennrp6185c532011-01-14 17:58:40 +00008978
glennrpd71e86a2011-02-24 01:28:37 +00008979 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8980 break;
8981 }
8982 }
8983 }
8984
8985 if (logging != MagickFalse)
8986 {
8987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8988 " image->colors=%d", (int) image->colors);
8989
8990 if (image->colormap != NULL)
8991 {
8992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008993 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008994
8995 for (i=0; i < (ssize_t) image->colors; i++)
8996 {
cristy72988482011-03-29 16:34:38 +00008997 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008998 {
8999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9000 " %d (%d,%d,%d,%d)",
9001 (int) i,
9002 (int) image->colormap[i].red,
9003 (int) image->colormap[i].green,
9004 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00009005 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00009006 }
glennrp6185c532011-01-14 17:58:40 +00009007 }
9008 }
glennrp03812ae2010-12-24 01:31:34 +00009009
glennrpd71e86a2011-02-24 01:28:37 +00009010 if (number_transparent < 257)
9011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9012 " number_transparent = %d",
9013 number_transparent);
9014 else
glennrp03812ae2010-12-24 01:31:34 +00009015
glennrpd71e86a2011-02-24 01:28:37 +00009016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9017 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00009018
glennrpd71e86a2011-02-24 01:28:37 +00009019 if (number_opaque < 257)
9020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9021 " number_opaque = %d",
9022 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00009023
glennrpd71e86a2011-02-24 01:28:37 +00009024 else
9025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9026 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00009027
glennrpd71e86a2011-02-24 01:28:37 +00009028 if (number_semitransparent < 257)
9029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9030 " number_semitransparent = %d",
9031 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00009032
glennrpd71e86a2011-02-24 01:28:37 +00009033 else
9034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9035 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00009036
glennrpd71e86a2011-02-24 01:28:37 +00009037 if (ping_have_non_bw == MagickFalse)
9038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9039 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00009040
glennrpd71e86a2011-02-24 01:28:37 +00009041 else if (ping_have_color == MagickFalse)
9042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043 " All pixels and the background are gray");
9044
9045 else
9046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9047 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00009048
glennrp03812ae2010-12-24 01:31:34 +00009049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9050 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00009051 }
glennrpfd05d622011-02-25 04:10:33 +00009052
glennrpc8c2f062011-02-25 19:00:33 +00009053 if (mng_info->write_png8 == MagickFalse)
9054 break;
glennrpfd05d622011-02-25 04:10:33 +00009055
glennrpc8c2f062011-02-25 19:00:33 +00009056 /* Make any reductions necessary for the PNG8 format */
9057 if (image_colors <= 256 &&
9058 image_colors != 0 && image->colormap != NULL &&
9059 number_semitransparent == 0 &&
9060 number_transparent <= 1)
9061 break;
9062
9063 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00009064 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9065 * transparent color so if more than one is transparent we merge
9066 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00009067 */
glennrp130fc452011-08-20 03:43:18 +00009068 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00009069 {
9070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9071 " Thresholding the alpha channel to binary");
9072
9073 for (y=0; y < (ssize_t) image->rows; y++)
9074 {
cristy16ea1392012-03-21 20:38:41 +00009075 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00009076
cristy16ea1392012-03-21 20:38:41 +00009077 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00009078 break;
9079
9080 for (x=0; x < (ssize_t) image->columns; x++)
9081 {
cristy16ea1392012-03-21 20:38:41 +00009082 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00009083 {
cristy16ea1392012-03-21 20:38:41 +00009084 SetPixelInfoPixel(image,&image->background_color,r);
9085 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00009086 }
9087 else
cristy16ea1392012-03-21 20:38:41 +00009088 SetPixelAlpha(image,OpaqueAlpha,r);
9089 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00009090 }
glennrpbb4f99d2011-05-22 11:13:17 +00009091
glennrpc8c2f062011-02-25 19:00:33 +00009092 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9093 break;
9094
9095 if (image_colors != 0 && image_colors <= 256 &&
9096 image->colormap != NULL)
9097 for (i=0; i<image_colors; i++)
cristy16ea1392012-03-21 20:38:41 +00009098 image->colormap[i].alpha =
9099 (image->colormap[i].alpha > TransparentAlpha/2 ?
9100 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00009101 }
9102 continue;
9103 }
9104
9105 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00009106 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9107 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9108 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00009109 */
glennrpd3371642011-03-22 19:42:23 +00009110 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9111 {
9112 if (logging != MagickFalse)
9113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9114 " Quantizing the background color to 4-4-4");
9115
9116 tried_444 = MagickTrue;
9117
glennrp91d99252011-06-25 14:30:13 +00009118 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00009119
9120 if (logging != MagickFalse)
9121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9122 " Quantizing the pixel colors to 4-4-4");
9123
9124 if (image->colormap == NULL)
9125 {
9126 for (y=0; y < (ssize_t) image->rows; y++)
9127 {
cristy16ea1392012-03-21 20:38:41 +00009128 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00009129
cristy16ea1392012-03-21 20:38:41 +00009130 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00009131 break;
9132
9133 for (x=0; x < (ssize_t) image->columns; x++)
9134 {
cristy16ea1392012-03-21 20:38:41 +00009135 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00009136 LBR04PixelRGB(r);
cristy16ea1392012-03-21 20:38:41 +00009137 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00009138 }
glennrpbb4f99d2011-05-22 11:13:17 +00009139
glennrpd3371642011-03-22 19:42:23 +00009140 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9141 break;
9142 }
9143 }
9144
9145 else /* Should not reach this; colormap already exists and
9146 must be <= 256 */
9147 {
9148 if (logging != MagickFalse)
9149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9150 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00009151
glennrpd3371642011-03-22 19:42:23 +00009152 for (i=0; i<image_colors; i++)
9153 {
glennrp91d99252011-06-25 14:30:13 +00009154 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00009155 }
9156 }
9157 continue;
9158 }
9159
glennrp82b3c532011-03-22 19:20:54 +00009160 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9161 {
9162 if (logging != MagickFalse)
9163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9164 " Quantizing the background color to 3-3-3");
9165
9166 tried_333 = MagickTrue;
9167
glennrp91d99252011-06-25 14:30:13 +00009168 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00009169
9170 if (logging != MagickFalse)
9171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009172 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00009173
9174 if (image->colormap == NULL)
9175 {
9176 for (y=0; y < (ssize_t) image->rows; y++)
9177 {
cristy16ea1392012-03-21 20:38:41 +00009178 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00009179
cristy16ea1392012-03-21 20:38:41 +00009180 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00009181 break;
9182
9183 for (x=0; x < (ssize_t) image->columns; x++)
9184 {
cristy16ea1392012-03-21 20:38:41 +00009185 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9186 LBR03RGB(r);
9187 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00009188 }
glennrpbb4f99d2011-05-22 11:13:17 +00009189
glennrp82b3c532011-03-22 19:20:54 +00009190 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9191 break;
9192 }
9193 }
9194
9195 else /* Should not reach this; colormap already exists and
9196 must be <= 256 */
9197 {
9198 if (logging != MagickFalse)
9199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009200 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00009201 for (i=0; i<image_colors; i++)
9202 {
glennrp91d99252011-06-25 14:30:13 +00009203 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00009204 }
glennrpd3371642011-03-22 19:42:23 +00009205 }
9206 continue;
glennrp82b3c532011-03-22 19:20:54 +00009207 }
glennrpc8c2f062011-02-25 19:00:33 +00009208
glennrp8ca51ad2011-05-12 21:22:32 +00009209 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00009210 {
9211 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00009212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00009213 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00009214
glennrp8ca51ad2011-05-12 21:22:32 +00009215 tried_332 = MagickTrue;
9216
glennrp3faa9a32011-04-23 14:00:25 +00009217 /* Red and green were already done so we only quantize the blue
9218 * channel
9219 */
9220
glennrp91d99252011-06-25 14:30:13 +00009221 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00009222
glennrpc8c2f062011-02-25 19:00:33 +00009223 if (logging != MagickFalse)
9224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009225 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00009226
glennrpc8c2f062011-02-25 19:00:33 +00009227 if (image->colormap == NULL)
9228 {
9229 for (y=0; y < (ssize_t) image->rows; y++)
9230 {
cristy16ea1392012-03-21 20:38:41 +00009231 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00009232
cristy16ea1392012-03-21 20:38:41 +00009233 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00009234 break;
9235
9236 for (x=0; x < (ssize_t) image->columns; x++)
9237 {
cristy16ea1392012-03-21 20:38:41 +00009238 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00009239 LBR02PixelBlue(r);
cristy16ea1392012-03-21 20:38:41 +00009240 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00009241 }
glennrpbb4f99d2011-05-22 11:13:17 +00009242
glennrpc8c2f062011-02-25 19:00:33 +00009243 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9244 break;
9245 }
9246 }
glennrpfd05d622011-02-25 04:10:33 +00009247
glennrpc8c2f062011-02-25 19:00:33 +00009248 else /* Should not reach this; colormap already exists and
9249 must be <= 256 */
9250 {
9251 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00009252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009253 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00009254 for (i=0; i<image_colors; i++)
9255 {
glennrp91d99252011-06-25 14:30:13 +00009256 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00009257 }
9258 }
9259 continue;
9260 }
glennrp8ca51ad2011-05-12 21:22:32 +00009261
9262 if (image_colors == 0 || image_colors > 256)
9263 {
glennrp34ef7202013-09-27 17:36:53 +00009264 /* Take care of special case with 256 opaque colors + 1 transparent
glennrp8ca51ad2011-05-12 21:22:32 +00009265 * color. We don't need to quantize to 2-3-2-1; we only need to
9266 * eliminate one color, so we'll merge the two darkest red
9267 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9268 */
glennrp34ef7202013-09-27 17:36:53 +00009269 if (logging != MagickFalse)
9270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9271 " Merging two dark red background colors to 3-3-2-1");
9272
glennrp8ca51ad2011-05-12 21:22:32 +00009273 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9274 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9275 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9276 {
9277 image->background_color.red=ScaleCharToQuantum(0x24);
9278 }
glennrpbb4f99d2011-05-22 11:13:17 +00009279
glennrp34ef7202013-09-27 17:36:53 +00009280 if (logging != MagickFalse)
9281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9282 " Merging two dark red pixel colors to 3-3-2-1");
9283
glennrp8ca51ad2011-05-12 21:22:32 +00009284 if (image->colormap == NULL)
9285 {
9286 for (y=0; y < (ssize_t) image->rows; y++)
9287 {
cristy16ea1392012-03-21 20:38:41 +00009288 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00009289
cristy16ea1392012-03-21 20:38:41 +00009290 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00009291 break;
glennrpbb4f99d2011-05-22 11:13:17 +00009292
glennrp8ca51ad2011-05-12 21:22:32 +00009293 for (x=0; x < (ssize_t) image->columns; x++)
9294 {
cristy16ea1392012-03-21 20:38:41 +00009295 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9296 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9297 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9298 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00009299 {
cristy16ea1392012-03-21 20:38:41 +00009300 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00009301 }
cristy16ea1392012-03-21 20:38:41 +00009302 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00009303 }
glennrpbb4f99d2011-05-22 11:13:17 +00009304
glennrp8ca51ad2011-05-12 21:22:32 +00009305 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9306 break;
glennrpbb4f99d2011-05-22 11:13:17 +00009307
glennrp8ca51ad2011-05-12 21:22:32 +00009308 }
9309 }
9310
9311 else
9312 {
9313 for (i=0; i<image_colors; i++)
9314 {
9315 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9316 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9317 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9318 {
9319 image->colormap[i].red=ScaleCharToQuantum(0x24);
9320 }
9321 }
9322 }
9323 }
glennrpd71e86a2011-02-24 01:28:37 +00009324 }
glennrpa8036d62012-11-04 01:46:06 +00009325 }
glennrpfd05d622011-02-25 04:10:33 +00009326 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00009327
glennrpfd05d622011-02-25 04:10:33 +00009328 /* If we are excluding the tRNS chunk and there is transparency,
9329 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9330 * PNG.
glennrp8d579662011-02-23 02:05:02 +00009331 */
glennrp0e8ea192010-12-24 18:00:33 +00009332 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9333 (number_transparent != 0 || number_semitransparent != 0))
9334 {
glennrpd17915c2011-04-29 14:24:22 +00009335 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00009336
9337 if (ping_have_color == MagickFalse)
9338 mng_info->write_png_colortype = 5;
9339
9340 else
9341 mng_info->write_png_colortype = 7;
9342
glennrp8d579662011-02-23 02:05:02 +00009343 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00009344 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00009345 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00009346
glennrp0e8ea192010-12-24 18:00:33 +00009347 }
9348
glennrpfd05d622011-02-25 04:10:33 +00009349 /* See if cheap transparency is possible. It is only possible
9350 * when there is a single transparent color, no semitransparent
9351 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00009352 * as the transparent color. We only need this information if
9353 * we are writing a PNG with colortype 0 or 2, and we have not
9354 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00009355 */
glennrp5a39f372011-02-25 04:52:16 +00009356 if (number_transparent == 1 &&
9357 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00009358 {
9359 ping_have_cheap_transparency = MagickTrue;
9360
9361 if (number_semitransparent != 0)
9362 ping_have_cheap_transparency = MagickFalse;
9363
9364 else if (image_colors == 0 || image_colors > 256 ||
9365 image->colormap == NULL)
9366 {
cristy16ea1392012-03-21 20:38:41 +00009367 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00009368 *q;
9369
glennrpfd05d622011-02-25 04:10:33 +00009370 for (y=0; y < (ssize_t) image->rows; y++)
9371 {
9372 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9373
cristy16ea1392012-03-21 20:38:41 +00009374 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00009375 break;
9376
9377 for (x=0; x < (ssize_t) image->columns; x++)
9378 {
cristy16ea1392012-03-21 20:38:41 +00009379 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9380 (unsigned short) GetPixelRed(image,q) ==
9381 ping_trans_color.red &&
9382 (unsigned short) GetPixelGreen(image,q) ==
9383 ping_trans_color.green &&
9384 (unsigned short) GetPixelBlue(image,q) ==
9385 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00009386 {
9387 ping_have_cheap_transparency = MagickFalse;
9388 break;
9389 }
9390
cristy16ea1392012-03-21 20:38:41 +00009391 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00009392 }
glennrpbb4f99d2011-05-22 11:13:17 +00009393
glennrpfd05d622011-02-25 04:10:33 +00009394 if (ping_have_cheap_transparency == MagickFalse)
9395 break;
9396 }
9397 }
9398 else
9399 {
glennrp67b9c1a2011-04-22 18:47:36 +00009400 /* Assuming that image->colormap[0] is the one transparent color
9401 * and that all others are opaque.
9402 */
glennrpfd05d622011-02-25 04:10:33 +00009403 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00009404 for (i=1; i<image_colors; i++)
9405 if (image->colormap[i].red == image->colormap[0].red &&
9406 image->colormap[i].green == image->colormap[0].green &&
9407 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00009408 {
glennrp67b9c1a2011-04-22 18:47:36 +00009409 ping_have_cheap_transparency = MagickFalse;
9410 break;
glennrpfd05d622011-02-25 04:10:33 +00009411 }
9412 }
glennrpbb4f99d2011-05-22 11:13:17 +00009413
glennrpfd05d622011-02-25 04:10:33 +00009414 if (logging != MagickFalse)
9415 {
9416 if (ping_have_cheap_transparency == MagickFalse)
9417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9418 " Cheap transparency is not possible.");
9419
9420 else
9421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9422 " Cheap transparency is possible.");
9423 }
9424 }
9425 else
9426 ping_have_cheap_transparency = MagickFalse;
9427
glennrp8640fb52010-11-23 15:48:26 +00009428 image_depth=image->depth;
9429
glennrp26c990a2010-11-23 02:23:20 +00009430 quantum_info = (QuantumInfo *) NULL;
9431 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00009432 image_colors=(int) image->colors;
cristyb0a657e2012-08-29 00:45:37 +00009433 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
glennrp26c990a2010-11-23 02:23:20 +00009434
glennrp48c20622014-04-23 01:00:37 +00009435 if (mng_info->write_png_colortype < 5)
glennrp197c8e62014-04-22 23:45:20 +00009436 mng_info->IsPalette=image->storage_class == PseudoClass &&
9437 image_colors <= 256 && image->colormap != NULL;
9438 else
9439 mng_info->IsPalette = MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00009440
glennrp52a479c2011-02-26 21:14:38 +00009441 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9442 (image->colors == 0 || image->colormap == NULL))
9443 {
cristy16ea1392012-03-21 20:38:41 +00009444 image_info=DestroyImageInfo(image_info);
9445 image=DestroyImage(image);
9446 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00009447 "Cannot write PNG8 or color-type 3; colormap is NULL",
cristy16ea1392012-03-21 20:38:41 +00009448 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00009449 return(MagickFalse);
9450 }
9451
cristy3ed852e2009-09-05 21:47:34 +00009452 /*
9453 Allocate the PNG structures
9454 */
9455#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00009456 error_info.image=image;
9457 error_info.exception=exception;
9458 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009459 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9460 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00009461
cristy3ed852e2009-09-05 21:47:34 +00009462#else
cristy16ea1392012-03-21 20:38:41 +00009463 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009464 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00009465
cristy3ed852e2009-09-05 21:47:34 +00009466#endif
9467 if (ping == (png_struct *) NULL)
9468 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009469
cristy3ed852e2009-09-05 21:47:34 +00009470 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00009471
cristy3ed852e2009-09-05 21:47:34 +00009472 if (ping_info == (png_info *) NULL)
9473 {
9474 png_destroy_write_struct(&ping,(png_info **) NULL);
9475 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9476 }
glennrp0fe50b42010-11-16 03:52:51 +00009477
cristy3ed852e2009-09-05 21:47:34 +00009478 png_set_write_fn(ping,image,png_put_data,png_flush_data);
cristy09973322013-06-30 21:06:30 +00009479 pixel_info=(MemoryInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00009480
glennrp5af765f2010-03-30 11:12:18 +00009481 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009482 {
9483 /*
9484 PNG write failed.
9485 */
9486#ifdef PNG_DEBUG
9487 if (image_info->verbose)
9488 (void) printf("PNG write has failed.\n");
9489#endif
9490 png_destroy_write_struct(&ping,&ping_info);
glennrp868fff32014-03-16 22:09:06 +00009491#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00009492 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009493#endif
glennrpedaa0382012-04-12 14:16:21 +00009494
cristy09973322013-06-30 21:06:30 +00009495 if (pixel_info != (MemoryInfo *) NULL)
9496 pixel_info=RelinquishVirtualMemory(pixel_info);
glennrpedaa0382012-04-12 14:16:21 +00009497
9498 if (quantum_info != (QuantumInfo *) NULL)
9499 quantum_info=DestroyQuantumInfo(quantum_info);
9500
cristy16ea1392012-03-21 20:38:41 +00009501 if (ping_have_blob != MagickFalse)
9502 (void) CloseBlob(image);
9503 image_info=DestroyImageInfo(image_info);
9504 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009505 return(MagickFalse);
9506 }
glennrpedaa0382012-04-12 14:16:21 +00009507
9508 /* { For navigation to end of SETJMP-protected block. Within this
9509 * block, use png_error() instead of Throwing an Exception, to ensure
9510 * that libpng is able to clean up, and that the semaphore is unlocked.
9511 */
9512
glennrp868fff32014-03-16 22:09:06 +00009513#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpedaa0382012-04-12 14:16:21 +00009514 LockSemaphoreInfo(ping_semaphore);
9515#endif
9516
glennrp943b7d32013-04-21 00:40:38 +00009517#ifdef PNG_BENIGN_ERRORS_SUPPORTED
glennrpa3a5f952013-04-17 12:58:15 +00009518 /* Allow benign errors */
9519 png_set_benign_errors(ping, 1);
9520#endif
9521
cristy3ed852e2009-09-05 21:47:34 +00009522 /*
9523 Prepare PNG for writing.
9524 */
glennrp9bf97b62012-06-06 21:03:14 +00009525
cristy3ed852e2009-09-05 21:47:34 +00009526#if defined(PNG_MNG_FEATURES_SUPPORTED)
9527 if (mng_info->write_mng)
glennrp25024a62012-06-07 11:38:34 +00009528 {
cristy3ed852e2009-09-05 21:47:34 +00009529 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp25024a62012-06-07 11:38:34 +00009530# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9531 /* Disable new libpng-1.5.10 feature when writing a MNG because
9532 * zero-length PLTE is OK
9533 */
9534 png_set_check_for_invalid_index (ping, 0);
9535# endif
9536 }
glennrp2b013e42010-11-24 16:55:50 +00009537
cristy3ed852e2009-09-05 21:47:34 +00009538#else
9539# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9540 if (mng_info->write_mng)
9541 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00009542
cristy3ed852e2009-09-05 21:47:34 +00009543# endif
9544#endif
glennrp2b013e42010-11-24 16:55:50 +00009545
cristy3ed852e2009-09-05 21:47:34 +00009546 x=0;
glennrp2b013e42010-11-24 16:55:50 +00009547
cristy4e5bc842010-06-09 13:56:01 +00009548 ping_width=(png_uint_32) image->columns;
9549 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00009550
cristy3ed852e2009-09-05 21:47:34 +00009551 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9552 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009553
glennrpfd164d22013-01-26 21:10:22 +00009554 if (mng_info->write_png48 || mng_info->write_png64)
9555 image_depth=16;
9556
cristy3ed852e2009-09-05 21:47:34 +00009557 if (mng_info->write_png_depth != 0)
9558 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009559
cristy3ed852e2009-09-05 21:47:34 +00009560 /* Adjust requested depth to next higher valid depth if necessary */
9561 if (image_depth > 8)
9562 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009563
cristy3ed852e2009-09-05 21:47:34 +00009564 if ((image_depth > 4) && (image_depth < 8))
9565 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009566
cristy3ed852e2009-09-05 21:47:34 +00009567 if (image_depth == 3)
9568 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00009569
cristy3ed852e2009-09-05 21:47:34 +00009570 if (logging != MagickFalse)
9571 {
9572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009573 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00009574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009575 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00009576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00009577 " image_matte=%.20g",(double) image->alpha_trait);
cristy3ed852e2009-09-05 21:47:34 +00009578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009579 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00009580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009581 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00009582 }
glennrp8640fb52010-11-23 15:48:26 +00009583
cristy3ed852e2009-09-05 21:47:34 +00009584 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00009585 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00009586
glennrp26f37912010-12-23 16:22:42 +00009587
cristy3ed852e2009-09-05 21:47:34 +00009588#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00009589 if (ping_exclude_pHYs == MagickFalse)
9590 {
cristy16ea1392012-03-21 20:38:41 +00009591 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00009592 (!mng_info->write_mng || !mng_info->equal_physs))
9593 {
glennrp0fe50b42010-11-16 03:52:51 +00009594 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9596 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00009597
9598 if (image->units == PixelsPerInchResolution)
9599 {
glennrpdfd70802010-11-14 01:23:35 +00009600 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009601 ping_pHYs_x_resolution=
cristy16ea1392012-03-21 20:38:41 +00009602 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00009603 ping_pHYs_y_resolution=
cristy16ea1392012-03-21 20:38:41 +00009604 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009605 }
glennrpdfd70802010-11-14 01:23:35 +00009606
cristy3ed852e2009-09-05 21:47:34 +00009607 else if (image->units == PixelsPerCentimeterResolution)
9608 {
glennrpdfd70802010-11-14 01:23:35 +00009609 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy16ea1392012-03-21 20:38:41 +00009610 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9611 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009612 }
glennrp991d11d2010-11-12 21:55:28 +00009613
cristy3ed852e2009-09-05 21:47:34 +00009614 else
9615 {
glennrpdfd70802010-11-14 01:23:35 +00009616 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy16ea1392012-03-21 20:38:41 +00009617 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9618 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009619 }
glennrp991d11d2010-11-12 21:55:28 +00009620
glennrp823b55c2011-03-14 18:46:46 +00009621 if (logging != MagickFalse)
9622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9623 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9624 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9625 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009626 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009627 }
glennrp26f37912010-12-23 16:22:42 +00009628 }
cristy3ed852e2009-09-05 21:47:34 +00009629#endif
glennrpa521b2f2010-10-29 04:11:03 +00009630
glennrp26f37912010-12-23 16:22:42 +00009631 if (ping_exclude_bKGD == MagickFalse)
9632 {
glennrpa521b2f2010-10-29 04:11:03 +00009633 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009634 {
glennrpa521b2f2010-10-29 04:11:03 +00009635 unsigned int
9636 mask;
cristy3ed852e2009-09-05 21:47:34 +00009637
glennrpa521b2f2010-10-29 04:11:03 +00009638 mask=0xffff;
9639 if (ping_bit_depth == 8)
9640 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009641
glennrpa521b2f2010-10-29 04:11:03 +00009642 if (ping_bit_depth == 4)
9643 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009644
glennrpa521b2f2010-10-29 04:11:03 +00009645 if (ping_bit_depth == 2)
9646 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009647
glennrpa521b2f2010-10-29 04:11:03 +00009648 if (ping_bit_depth == 1)
9649 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009650
glennrpa521b2f2010-10-29 04:11:03 +00009651 ping_background.red=(png_uint_16)
9652 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009653
glennrpa521b2f2010-10-29 04:11:03 +00009654 ping_background.green=(png_uint_16)
9655 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009656
glennrpa521b2f2010-10-29 04:11:03 +00009657 ping_background.blue=(png_uint_16)
9658 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009659
9660 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009661 }
cristy3ed852e2009-09-05 21:47:34 +00009662
glennrp0fe50b42010-11-16 03:52:51 +00009663 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009664 {
9665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9666 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9668 " background_color index is %d",
9669 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009670
9671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9672 " ping_bit_depth=%d",ping_bit_depth);
9673 }
glennrp0fe50b42010-11-16 03:52:51 +00009674
9675 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009676 }
glennrp0fe50b42010-11-16 03:52:51 +00009677
cristy3ed852e2009-09-05 21:47:34 +00009678 /*
9679 Select the color type.
9680 */
9681 matte=image_matte;
9682 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009683
glennrp1273f7b2011-02-24 03:20:30 +00009684 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009685 {
glennrpfd05d622011-02-25 04:10:33 +00009686 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009687 for reducing the sample depth from 8. */
9688
glennrp0fe50b42010-11-16 03:52:51 +00009689 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009690
glennrp8bb3a022010-12-13 20:40:04 +00009691 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009692
9693 /*
9694 Set image palette.
9695 */
9696 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9697
glennrp0fe50b42010-11-16 03:52:51 +00009698 if (logging != MagickFalse)
9699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9700 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009701 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009702
9703 for (i=0; i < (ssize_t) number_colors; i++)
9704 {
9705 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9706 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9707 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9708 if (logging != MagickFalse)
9709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009710#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009711 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009712#else
9713 " %5ld (%5d,%5d,%5d)",
9714#endif
glennrp0fe50b42010-11-16 03:52:51 +00009715 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9716
9717 }
glennrp2b013e42010-11-24 16:55:50 +00009718
glennrp8bb3a022010-12-13 20:40:04 +00009719 ping_have_PLTE=MagickTrue;
9720 image_depth=ping_bit_depth;
9721 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009722
glennrp58e01762011-01-07 15:28:54 +00009723 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009724 {
glennrp0fe50b42010-11-16 03:52:51 +00009725 /*
9726 Identify which colormap entry is transparent.
9727 */
9728 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009729 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009730
glennrp8bb3a022010-12-13 20:40:04 +00009731 for (i=0; i < (ssize_t) number_transparent; i++)
9732 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009733
glennrp0fe50b42010-11-16 03:52:51 +00009734
glennrp2cc891a2010-12-24 13:44:32 +00009735 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009736 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009737
9738 if (ping_num_trans == 0)
9739 ping_have_tRNS=MagickFalse;
9740
glennrp8bb3a022010-12-13 20:40:04 +00009741 else
9742 ping_have_tRNS=MagickTrue;
9743 }
glennrp0fe50b42010-11-16 03:52:51 +00009744
glennrp1273f7b2011-02-24 03:20:30 +00009745 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009746 {
glennrp1273f7b2011-02-24 03:20:30 +00009747 /*
9748 * Identify which colormap entry is the background color.
9749 */
9750
glennrp4f25bd02011-01-01 18:51:28 +00009751 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9752 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9753 break;
glennrp0fe50b42010-11-16 03:52:51 +00009754
glennrp4f25bd02011-01-01 18:51:28 +00009755 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009756
9757 if (logging != MagickFalse)
9758 {
9759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9760 " background_color index is %d",
9761 (int) ping_background.index);
9762 }
glennrp4f25bd02011-01-01 18:51:28 +00009763 }
cristy3ed852e2009-09-05 21:47:34 +00009764 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009765
glennrpfd164d22013-01-26 21:10:22 +00009766 else if (mng_info->write_png_colortype == 1)
9767 {
9768 image_matte=MagickFalse;
9769 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9770 }
9771
9772 else if (mng_info->write_png24 || mng_info->write_png48 ||
9773 mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009774 {
9775 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009776 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009777 }
glennrp0fe50b42010-11-16 03:52:51 +00009778
glennrpfd164d22013-01-26 21:10:22 +00009779 else if (mng_info->write_png32 || mng_info->write_png64 ||
9780 mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009781 {
9782 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009783 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009784 }
glennrp0fe50b42010-11-16 03:52:51 +00009785
glennrp8bb3a022010-12-13 20:40:04 +00009786 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009787 {
glennrp5af765f2010-03-30 11:12:18 +00009788 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009789
glennrp8bb3a022010-12-13 20:40:04 +00009790 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009791 {
glennrp5af765f2010-03-30 11:12:18 +00009792 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009793
glennrp5af765f2010-03-30 11:12:18 +00009794 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9795 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009796 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009797
glennrp8bb3a022010-12-13 20:40:04 +00009798 else
9799 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009800
9801 if (logging != MagickFalse)
9802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9803 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009804 }
glennrp0fe50b42010-11-16 03:52:51 +00009805
glennrp7c4c9e62011-03-21 20:23:32 +00009806 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009807 {
9808 if (logging != MagickFalse)
9809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009810 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009811
glennrpd6bf1612010-12-17 17:28:54 +00009812 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009813 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009814
glennrpd6bf1612010-12-17 17:28:54 +00009815 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009816 {
glennrp5af765f2010-03-30 11:12:18 +00009817 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009818 image_matte=MagickFalse;
9819 }
glennrp0fe50b42010-11-16 03:52:51 +00009820
glennrpd6bf1612010-12-17 17:28:54 +00009821 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009822 {
glennrp5af765f2010-03-30 11:12:18 +00009823 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009824 image_matte=MagickTrue;
9825 }
glennrp0fe50b42010-11-16 03:52:51 +00009826
glennrp5aa37f62011-01-02 03:07:57 +00009827 if (image_info->type == PaletteType ||
9828 image_info->type == PaletteMatteType)
9829 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9830
glennrp7c4c9e62011-03-21 20:23:32 +00009831 if (mng_info->write_png_colortype == 0 &&
glennrp261f64e2014-08-09 15:44:51 +00009832 image_info->type == UndefinedType)
cristy3ed852e2009-09-05 21:47:34 +00009833 {
glennrp5aa37f62011-01-02 03:07:57 +00009834 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009835 {
glennrp5aa37f62011-01-02 03:07:57 +00009836 if (image_matte == MagickFalse)
9837 {
9838 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9839 image_matte=MagickFalse;
9840 }
glennrp0fe50b42010-11-16 03:52:51 +00009841
glennrp0b206f52011-01-07 04:55:32 +00009842 else
glennrp5aa37f62011-01-02 03:07:57 +00009843 {
9844 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9845 image_matte=MagickTrue;
9846 }
9847 }
9848 else
glennrp8bb3a022010-12-13 20:40:04 +00009849 {
glennrp5aa37f62011-01-02 03:07:57 +00009850 if (image_matte == MagickFalse)
9851 {
9852 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9853 image_matte=MagickFalse;
9854 }
glennrp8bb3a022010-12-13 20:40:04 +00009855
glennrp0b206f52011-01-07 04:55:32 +00009856 else
glennrp5aa37f62011-01-02 03:07:57 +00009857 {
9858 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9859 image_matte=MagickTrue;
9860 }
9861 }
glennrp0fe50b42010-11-16 03:52:51 +00009862 }
glennrp5aa37f62011-01-02 03:07:57 +00009863
cristy3ed852e2009-09-05 21:47:34 +00009864 }
glennrp0fe50b42010-11-16 03:52:51 +00009865
cristy3ed852e2009-09-05 21:47:34 +00009866 if (logging != MagickFalse)
9867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009868 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009869
glennrp5af765f2010-03-30 11:12:18 +00009870 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009871 {
9872 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9873 ping_color_type == PNG_COLOR_TYPE_RGB ||
9874 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9875 ping_bit_depth=8;
9876 }
cristy3ed852e2009-09-05 21:47:34 +00009877
glennrpd6bf1612010-12-17 17:28:54 +00009878 old_bit_depth=ping_bit_depth;
9879
glennrp5af765f2010-03-30 11:12:18 +00009880 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009881 {
cristy8a46d822012-08-28 23:32:39 +00009882 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00009883 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009884 }
glennrp8640fb52010-11-23 15:48:26 +00009885
glennrp5af765f2010-03-30 11:12:18 +00009886 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009887 {
cristy35ef8242010-06-03 16:24:13 +00009888 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009889 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009890
9891 if (image->colors == 0)
9892 {
glennrp0fe50b42010-11-16 03:52:51 +00009893 /* DO SOMETHING */
glennrpedaa0382012-04-12 14:16:21 +00009894 png_error(ping,"image has 0 colors");
glennrp0f111982010-07-07 20:18:33 +00009895 }
9896
cristy35ef8242010-06-03 16:24:13 +00009897 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009898 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009899 }
glennrp2b013e42010-11-24 16:55:50 +00009900
glennrpd6bf1612010-12-17 17:28:54 +00009901 if (logging != MagickFalse)
9902 {
9903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9904 " Number of colors: %.20g",(double) image_colors);
9905
9906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9907 " Tentative PNG bit depth: %d",ping_bit_depth);
9908 }
9909
9910 if (ping_bit_depth < (int) mng_info->write_png_depth)
9911 ping_bit_depth = mng_info->write_png_depth;
9912 }
glennrp2cc891a2010-12-24 13:44:32 +00009913
glennrp5af765f2010-03-30 11:12:18 +00009914 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009915
cristy3ed852e2009-09-05 21:47:34 +00009916 if (logging != MagickFalse)
9917 {
9918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +00009919 " Tentative PNG color type: %s (%.20g)",
9920 PngColorTypeToString(ping_color_type),
9921 (double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009922
cristy3ed852e2009-09-05 21:47:34 +00009923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009924 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009925
cristy3ed852e2009-09-05 21:47:34 +00009926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009927 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009928
cristy3ed852e2009-09-05 21:47:34 +00009929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009930
glennrp8640fb52010-11-23 15:48:26 +00009931 " image->depth: %.20g",(double) image->depth);
9932
9933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009934 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009935 }
9936
glennrp58e01762011-01-07 15:28:54 +00009937 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009938 {
glennrp4f25bd02011-01-01 18:51:28 +00009939 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009940 {
glennrp7c4c9e62011-03-21 20:23:32 +00009941 if (mng_info->write_png_colortype == 0)
9942 {
9943 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009944
glennrp7c4c9e62011-03-21 20:23:32 +00009945 if (ping_have_color != MagickFalse)
9946 ping_color_type=PNG_COLOR_TYPE_RGBA;
9947 }
glennrp4f25bd02011-01-01 18:51:28 +00009948
9949 /*
9950 * Determine if there is any transparent color.
9951 */
9952 if (number_transparent + number_semitransparent == 0)
9953 {
9954 /*
9955 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9956 */
glennrpa6a06632011-01-19 15:15:34 +00009957
glennrp4f25bd02011-01-01 18:51:28 +00009958 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009959
9960 if (mng_info->write_png_colortype == 0)
9961 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009962 }
9963
9964 else
9965 {
9966 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009967 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009968
9969 mask=0xffff;
9970
9971 if (ping_bit_depth == 8)
9972 mask=0x00ff;
9973
9974 if (ping_bit_depth == 4)
9975 mask=0x000f;
9976
9977 if (ping_bit_depth == 2)
9978 mask=0x0003;
9979
9980 if (ping_bit_depth == 1)
9981 mask=0x0001;
9982
9983 ping_trans_color.red=(png_uint_16)
9984 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9985
9986 ping_trans_color.green=(png_uint_16)
9987 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9988
9989 ping_trans_color.blue=(png_uint_16)
9990 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9991
9992 ping_trans_color.gray=(png_uint_16)
cristy16ea1392012-03-21 20:38:41 +00009993 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009994 image->colormap)) & mask);
9995
9996 ping_trans_color.index=(png_byte) 0;
9997
9998 ping_have_tRNS=MagickTrue;
9999 }
10000
10001 if (ping_have_tRNS != MagickFalse)
10002 {
10003 /*
glennrpfd05d622011-02-25 04:10:33 +000010004 * Determine if there is one and only one transparent color
10005 * and if so if it is fully transparent.
10006 */
10007 if (ping_have_cheap_transparency == MagickFalse)
10008 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +000010009 }
10010
10011 if (ping_have_tRNS != MagickFalse)
10012 {
glennrp7c4c9e62011-03-21 20:23:32 +000010013 if (mng_info->write_png_colortype == 0)
10014 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +000010015
10016 if (image_depth == 8)
10017 {
10018 ping_trans_color.red&=0xff;
10019 ping_trans_color.green&=0xff;
10020 ping_trans_color.blue&=0xff;
10021 ping_trans_color.gray&=0xff;
10022 }
10023 }
10024 }
cristy3ed852e2009-09-05 21:47:34 +000010025 else
10026 {
cristy3ed852e2009-09-05 21:47:34 +000010027 if (image_depth == 8)
10028 {
glennrp5af765f2010-03-30 11:12:18 +000010029 ping_trans_color.red&=0xff;
10030 ping_trans_color.green&=0xff;
10031 ping_trans_color.blue&=0xff;
10032 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +000010033 }
10034 }
10035 }
glennrp8640fb52010-11-23 15:48:26 +000010036
cristy3ed852e2009-09-05 21:47:34 +000010037 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +000010038
glennrp2e09f552010-11-14 00:38:48 +000010039 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010040 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000010041
glennrp39992b42010-11-14 00:03:43 +000010042 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +000010043 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +000010044 ping_have_color == MagickFalse &&
10045 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +000010046 {
cristy35ef8242010-06-03 16:24:13 +000010047 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +000010048
cristy3ed852e2009-09-05 21:47:34 +000010049 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010050 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +000010051
glennrp7c4c9e62011-03-21 20:23:32 +000010052 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +000010053 {
glennrp5af765f2010-03-30 11:12:18 +000010054 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +000010055
cristy3ed852e2009-09-05 21:47:34 +000010056 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +000010057 {
10058 if (logging != MagickFalse)
10059 {
10060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10061 " Scaling ping_trans_color (0)");
10062 }
10063 ping_trans_color.gray*=0x0101;
10064 }
cristy3ed852e2009-09-05 21:47:34 +000010065 }
glennrp0fe50b42010-11-16 03:52:51 +000010066
cristy3ed852e2009-09-05 21:47:34 +000010067 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10068 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +000010069
glennrp136ee3a2011-04-27 15:47:45 +000010070 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +000010071 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +000010072 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010073
cristy3ed852e2009-09-05 21:47:34 +000010074 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +000010075 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +000010076
cristy3ed852e2009-09-05 21:47:34 +000010077 else
10078 {
glennrp5af765f2010-03-30 11:12:18 +000010079 ping_bit_depth=8;
10080 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +000010081 {
10082 if(!mng_info->write_png_depth)
10083 {
glennrp5af765f2010-03-30 11:12:18 +000010084 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +000010085
cristy35ef8242010-06-03 16:24:13 +000010086 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +000010087 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +000010088 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +000010089 }
10090 }
glennrp2b013e42010-11-24 16:55:50 +000010091
glennrp0fe50b42010-11-16 03:52:51 +000010092 else if (ping_color_type ==
10093 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +000010094 mng_info->IsPalette)
10095 {
cristy3ed852e2009-09-05 21:47:34 +000010096 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +000010097
cristy3ed852e2009-09-05 21:47:34 +000010098 int
10099 depth_4_ok=MagickTrue,
10100 depth_2_ok=MagickTrue,
10101 depth_1_ok=MagickTrue;
10102
cristybb503372010-05-27 20:51:26 +000010103 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000010104 {
10105 unsigned char
10106 intensity;
10107
10108 intensity=ScaleQuantumToChar(image->colormap[i].red);
10109
10110 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10111 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10112 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10113 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +000010114 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +000010115 depth_1_ok=MagickFalse;
10116 }
glennrp2b013e42010-11-24 16:55:50 +000010117
cristy3ed852e2009-09-05 21:47:34 +000010118 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +000010119 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +000010120
cristy3ed852e2009-09-05 21:47:34 +000010121 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +000010122 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +000010123
cristy3ed852e2009-09-05 21:47:34 +000010124 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +000010125 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +000010126 }
10127 }
glennrp2b013e42010-11-24 16:55:50 +000010128
glennrp5af765f2010-03-30 11:12:18 +000010129 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +000010130 }
glennrp0fe50b42010-11-16 03:52:51 +000010131
cristy3ed852e2009-09-05 21:47:34 +000010132 else
glennrp0fe50b42010-11-16 03:52:51 +000010133
cristy3ed852e2009-09-05 21:47:34 +000010134 if (mng_info->IsPalette)
10135 {
glennrp17a14852010-05-10 03:01:59 +000010136 number_colors=image_colors;
10137
cristy3ed852e2009-09-05 21:47:34 +000010138 if (image_depth <= 8)
10139 {
cristy3ed852e2009-09-05 21:47:34 +000010140 /*
10141 Set image palette.
10142 */
glennrp5af765f2010-03-30 11:12:18 +000010143 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +000010144
glennrp3d627862013-02-26 00:19:34 +000010145 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +000010146 {
cristybb503372010-05-27 20:51:26 +000010147 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000010148 {
10149 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10150 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10151 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10152 }
glennrp0fe50b42010-11-16 03:52:51 +000010153
glennrp3b51f0e2010-11-27 18:14:08 +000010154 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +000010156 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +000010157 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010158
glennrp39992b42010-11-14 00:03:43 +000010159 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010160 }
glennrp0fe50b42010-11-16 03:52:51 +000010161
cristy3ed852e2009-09-05 21:47:34 +000010162 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +000010163 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +000010164 {
cristybefe4d22010-06-07 01:18:58 +000010165 size_t
10166 one;
10167
glennrp5af765f2010-03-30 11:12:18 +000010168 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +000010169 one=1;
glennrp0fe50b42010-11-16 03:52:51 +000010170
cristy16ea1392012-03-21 20:38:41 +000010171 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +000010172 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +000010173 }
glennrp0fe50b42010-11-16 03:52:51 +000010174
glennrp5af765f2010-03-30 11:12:18 +000010175 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +000010176
glennrp58e01762011-01-07 15:28:54 +000010177 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010178 {
glennrp0fe50b42010-11-16 03:52:51 +000010179 /*
glennrpd6bf1612010-12-17 17:28:54 +000010180 * Set up trans_colors array.
10181 */
glennrp0fe50b42010-11-16 03:52:51 +000010182 assert(number_colors <= 256);
10183
glennrpd6bf1612010-12-17 17:28:54 +000010184 ping_num_trans=(unsigned short) (number_transparent +
10185 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +000010186
10187 if (ping_num_trans == 0)
10188 ping_have_tRNS=MagickFalse;
10189
glennrpd6bf1612010-12-17 17:28:54 +000010190 else
glennrp0fe50b42010-11-16 03:52:51 +000010191 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010192 if (logging != MagickFalse)
10193 {
10194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10195 " Scaling ping_trans_color (1)");
10196 }
glennrpd6bf1612010-12-17 17:28:54 +000010197 ping_have_tRNS=MagickTrue;
10198
10199 for (i=0; i < ping_num_trans; i++)
10200 {
glennrp750105b2012-04-25 16:20:45 +000010201 ping_trans_alpha[i]= (png_byte)
cristy16ea1392012-03-21 20:38:41 +000010202 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +000010203 }
glennrp0fe50b42010-11-16 03:52:51 +000010204 }
10205 }
cristy3ed852e2009-09-05 21:47:34 +000010206 }
10207 }
glennrp0fe50b42010-11-16 03:52:51 +000010208
cristy3ed852e2009-09-05 21:47:34 +000010209 else
10210 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010211
cristy3ed852e2009-09-05 21:47:34 +000010212 if (image_depth < 8)
10213 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010214
cristy3ed852e2009-09-05 21:47:34 +000010215 if ((save_image_depth == 16) && (image_depth == 8))
10216 {
glennrp4f25bd02011-01-01 18:51:28 +000010217 if (logging != MagickFalse)
10218 {
10219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10220 " Scaling ping_trans_color from (%d,%d,%d)",
10221 (int) ping_trans_color.red,
10222 (int) ping_trans_color.green,
10223 (int) ping_trans_color.blue);
10224 }
10225
glennrp5af765f2010-03-30 11:12:18 +000010226 ping_trans_color.red*=0x0101;
10227 ping_trans_color.green*=0x0101;
10228 ping_trans_color.blue*=0x0101;
10229 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +000010230
10231 if (logging != MagickFalse)
10232 {
10233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10234 " to (%d,%d,%d)",
10235 (int) ping_trans_color.red,
10236 (int) ping_trans_color.green,
10237 (int) ping_trans_color.blue);
10238 }
cristy3ed852e2009-09-05 21:47:34 +000010239 }
10240 }
10241
cristy4383ec82011-01-05 15:42:32 +000010242 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10243 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +000010244
cristy3ed852e2009-09-05 21:47:34 +000010245 /*
10246 Adjust background and transparency samples in sub-8-bit grayscale files.
10247 */
glennrp5af765f2010-03-30 11:12:18 +000010248 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +000010249 PNG_COLOR_TYPE_GRAY)
10250 {
10251 png_uint_16
10252 maxval;
10253
cristy35ef8242010-06-03 16:24:13 +000010254 size_t
10255 one=1;
10256
cristy22ffd972010-06-03 16:51:47 +000010257 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +000010258
glennrp4f25bd02011-01-01 18:51:28 +000010259 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010260 {
cristy3ed852e2009-09-05 21:47:34 +000010261
cristy16ea1392012-03-21 20:38:41 +000010262 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10263 (ScaleQuantumToShort(((GetPixelInfoIntensity(
10264 &image->background_color))) +.5)));
10265
cristy3ed852e2009-09-05 21:47:34 +000010266 if (logging != MagickFalse)
10267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +000010268 " Setting up bKGD chunk (2)");
10269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10270 " background_color index is %d",
10271 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +000010272
glennrp991d11d2010-11-12 21:55:28 +000010273 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010274 }
cristy3ed852e2009-09-05 21:47:34 +000010275
glennrp3e3e20f2011-06-09 04:21:43 +000010276 if (logging != MagickFalse)
10277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10278 " Scaling ping_trans_color.gray from %d",
10279 (int)ping_trans_color.gray);
10280
glennrp9be9b1c2011-06-09 12:21:45 +000010281 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +000010282 ping_trans_color.gray)+.5);
10283
10284 if (logging != MagickFalse)
10285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10286 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +000010287 }
glennrp17a14852010-05-10 03:01:59 +000010288
glennrp26f37912010-12-23 16:22:42 +000010289 if (ping_exclude_bKGD == MagickFalse)
10290 {
glennrp1273f7b2011-02-24 03:20:30 +000010291 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +000010292 {
10293 /*
10294 Identify which colormap entry is the background color.
10295 */
10296
glennrp17a14852010-05-10 03:01:59 +000010297 number_colors=image_colors;
10298
glennrpa521b2f2010-10-29 04:11:03 +000010299 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10300 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +000010301 break;
10302
10303 ping_background.index=(png_byte) i;
10304
glennrp3b51f0e2010-11-27 18:14:08 +000010305 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +000010306 {
10307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +000010308 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +000010309 }
glennrp0fe50b42010-11-16 03:52:51 +000010310
cristy13d07042010-11-21 20:56:18 +000010311 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +000010312 {
10313 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +000010314
10315 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010316 {
10317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10318 " background =(%d,%d,%d)",
10319 (int) ping_background.red,
10320 (int) ping_background.green,
10321 (int) ping_background.blue);
10322 }
10323 }
glennrpa521b2f2010-10-29 04:11:03 +000010324
glennrpd6bf1612010-12-17 17:28:54 +000010325 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +000010326 {
glennrp3b51f0e2010-11-27 18:14:08 +000010327 if (logging != MagickFalse)
10328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10329 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +000010330 ping_have_bKGD = MagickFalse;
10331 }
glennrp17a14852010-05-10 03:01:59 +000010332 }
glennrp26f37912010-12-23 16:22:42 +000010333 }
glennrp17a14852010-05-10 03:01:59 +000010334
cristy3ed852e2009-09-05 21:47:34 +000010335 if (logging != MagickFalse)
10336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +000010337 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10338 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010339 /*
10340 Initialize compression level and filtering.
10341 */
10342 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010343 {
10344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10345 " Setting up deflate compression");
10346
10347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10348 " Compression buffer size: 32768");
10349 }
10350
cristy3ed852e2009-09-05 21:47:34 +000010351 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +000010352
cristy3ed852e2009-09-05 21:47:34 +000010353 if (logging != MagickFalse)
10354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10355 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +000010356
cristy4054bfb2011-08-29 23:41:39 +000010357 png_set_compression_mem_level(ping, 9);
10358
glennrp10d739e2011-06-29 18:00:52 +000010359 /* Untangle the "-quality" setting:
10360
10361 Undefined is 0; the default is used.
10362 Default is 75
10363
10364 10's digit:
10365
glennrpef804f52013-09-24 00:38:27 +000010366 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
glennrp10d739e2011-06-29 18:00:52 +000010367 zlib default compression level
10368
10369 1-9: the zlib compression level
10370
10371 1's digit:
10372
10373 0-4: the PNG filter method
10374
10375 5: libpng adaptive filtering if compression level > 5
10376 libpng filter type "none" if compression level <= 5
10377 or if image is grayscale or palette
glennrp750105b2012-04-25 16:20:45 +000010378
glennrp10d739e2011-06-29 18:00:52 +000010379 6: libpng adaptive filtering
10380
10381 7: "LOCO" filtering (intrapixel differing) if writing
glennrp85dfe1a2013-09-24 17:30:39 +000010382 a MNG, otherwise "none". Did not work in IM-6.7.0-9
glennrp10d739e2011-06-29 18:00:52 +000010383 and earlier because of a missing "else".
10384
glennrp85dfe1a2013-09-24 17:30:39 +000010385 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10386 filtering. Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +000010387
glennrpef804f52013-09-24 00:38:27 +000010388 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
glennrp18682582011-06-30 18:11:47 +000010389 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +000010390
10391 Note that using the -quality option, not all combinations of
10392 PNG filter type, zlib compression level, and zlib compression
cristy16ea1392012-03-21 20:38:41 +000010393 strategy are possible. This will be addressed soon in a
10394 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +000010395
10396 */
10397
dirk29dd80e2013-10-31 23:11:11 +000010398 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10399 image_info->quality;
glennrp0fe50b42010-11-16 03:52:51 +000010400
glennrp18682582011-06-30 18:11:47 +000010401 if (quality <= 9)
10402 {
10403 if (mng_info->write_png_compression_strategy == 0)
10404 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10405 }
glennrp750105b2012-04-25 16:20:45 +000010406
glennrp18682582011-06-30 18:11:47 +000010407 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +000010408 {
10409 int
10410 level;
10411
cristybb503372010-05-27 20:51:26 +000010412 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +000010413
glennrp18682582011-06-30 18:11:47 +000010414 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +000010415 }
glennrp0fe50b42010-11-16 03:52:51 +000010416
glennrp18682582011-06-30 18:11:47 +000010417 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +000010418 {
glennrp18682582011-06-30 18:11:47 +000010419 if ((quality %10) == 8 || (quality %10) == 9)
glennrpa24b2452012-06-27 11:38:38 +000010420#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10421 mng_info->write_png_compression_strategy=Z_RLE+1;
10422#else
10423 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10424#endif
cristy3ed852e2009-09-05 21:47:34 +000010425 }
glennrp0fe50b42010-11-16 03:52:51 +000010426
glennrp18682582011-06-30 18:11:47 +000010427 if (mng_info->write_png_compression_filter == 0)
10428 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10429
cristy3ed852e2009-09-05 21:47:34 +000010430 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010431 {
glennrp18682582011-06-30 18:11:47 +000010432 if (mng_info->write_png_compression_level)
10433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10434 " Compression level: %d",
10435 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +000010436
glennrp18682582011-06-30 18:11:47 +000010437 if (mng_info->write_png_compression_strategy)
10438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10439 " Compression strategy: %d",
10440 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +000010441
glennrp18682582011-06-30 18:11:47 +000010442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10443 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +000010444
cristy4054bfb2011-08-29 23:41:39 +000010445 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +000010446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10447 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +000010448 else if (mng_info->write_png_compression_filter == 0 ||
10449 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +000010450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10451 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +000010452 else
10453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10454 " Base filter method: %d",
10455 (int) mng_info->write_png_compression_filter-1);
10456 }
glennrp2b013e42010-11-24 16:55:50 +000010457
glennrp18682582011-06-30 18:11:47 +000010458 if (mng_info->write_png_compression_level != 0)
10459 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10460
10461 if (mng_info->write_png_compression_filter == 6)
10462 {
10463 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10464 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10465 (quality < 50))
10466 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10467 else
10468 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10469 }
cristy4054bfb2011-08-29 23:41:39 +000010470 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +000010471 mng_info->write_png_compression_filter == 10)
10472 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10473
10474 else if (mng_info->write_png_compression_filter == 8)
10475 {
10476#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10477 if (mng_info->write_mng)
10478 {
10479 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10480 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10481 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10482 }
10483#endif
cristy4054bfb2011-08-29 23:41:39 +000010484 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +000010485 }
10486
10487 else if (mng_info->write_png_compression_filter == 9)
10488 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10489
10490 else if (mng_info->write_png_compression_filter != 0)
10491 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10492 mng_info->write_png_compression_filter-1);
10493
10494 if (mng_info->write_png_compression_strategy != 0)
10495 png_set_compression_strategy(ping,
10496 mng_info->write_png_compression_strategy-1);
10497
glennrpdec72c92013-02-26 17:42:47 +000010498 ping_interlace_method=image_info->interlace != NoInterlace;
10499
10500 if (mng_info->write_mng)
10501 png_set_sig_bytes(ping,8);
10502
10503 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10504
10505 if (mng_info->write_png_colortype != 0)
10506 {
10507 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10508 if (ping_have_color != MagickFalse)
10509 {
10510 ping_color_type = PNG_COLOR_TYPE_RGB;
10511
10512 if (ping_bit_depth < 8)
10513 ping_bit_depth=8;
10514 }
10515
10516 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10517 if (ping_have_color != MagickFalse)
10518 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10519 }
10520
10521 if (ping_need_colortype_warning != MagickFalse ||
10522 ((mng_info->write_png_depth &&
10523 (int) mng_info->write_png_depth != ping_bit_depth) ||
10524 (mng_info->write_png_colortype &&
10525 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10526 mng_info->write_png_colortype != 7 &&
10527 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10528 {
10529 if (logging != MagickFalse)
10530 {
10531 if (ping_need_colortype_warning != MagickFalse)
10532 {
10533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10534 " Image has transparency but tRNS chunk was excluded");
10535 }
10536
10537 if (mng_info->write_png_depth)
10538 {
10539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10540 " Defined png:bit-depth=%u, Computed depth=%u",
10541 mng_info->write_png_depth,
10542 ping_bit_depth);
10543 }
10544
10545 if (mng_info->write_png_colortype)
10546 {
10547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10548 " Defined png:color-type=%u, Computed color type=%u",
10549 mng_info->write_png_colortype-1,
10550 ping_color_type);
10551 }
10552 }
10553
10554 png_warning(ping,
10555 "Cannot write image with defined png:bit-depth or png:color-type.");
10556 }
10557
10558 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10559 {
10560 /* Add an opaque matte channel */
10561 image->alpha_trait = BlendPixelTrait;
10562 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10563
10564 if (logging != MagickFalse)
10565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10566 " Added an opaque matte channel");
10567 }
10568
10569 if (number_transparent != 0 || number_semitransparent != 0)
10570 {
10571 if (ping_color_type < 4)
10572 {
10573 ping_have_tRNS=MagickTrue;
10574 if (logging != MagickFalse)
10575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10576 " Setting ping_have_tRNS=MagickTrue.");
10577 }
10578 }
10579
10580 if (logging != MagickFalse)
10581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10582 " Writing PNG header chunks");
10583
10584 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10585 ping_bit_depth,ping_color_type,
10586 ping_interlace_method,ping_compression_method,
10587 ping_filter_method);
10588
10589 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10590 {
10591 png_set_PLTE(ping,ping_info,palette,number_colors);
10592
10593 if (logging != MagickFalse)
10594 {
10595 for (i=0; i< (ssize_t) number_colors; i++)
10596 {
10597 if (i < ping_num_trans)
10598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10599 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10600 (int) i,
10601 (int) palette[i].red,
10602 (int) palette[i].green,
10603 (int) palette[i].blue,
10604 (int) i,
10605 (int) ping_trans_alpha[i]);
10606 else
10607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10608 " PLTE[%d] = (%d,%d,%d)",
10609 (int) i,
10610 (int) palette[i].red,
10611 (int) palette[i].green,
10612 (int) palette[i].blue);
10613 }
10614 }
10615 }
10616
cristy0d57eec2011-09-04 22:13:56 +000010617 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10618 if (ping_exclude_sRGB != MagickFalse ||
glennrp3d627862013-02-26 00:19:34 +000010619 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy0d57eec2011-09-04 22:13:56 +000010620 {
10621 if ((ping_exclude_tEXt == MagickFalse ||
10622 ping_exclude_zTXt == MagickFalse) &&
10623 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +000010624 {
10625 ResetImageProfileIterator(image);
10626 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +000010627 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010628 profile=GetImageProfile(image,name);
10629
10630 if (profile != (StringInfo *) NULL)
10631 {
glennrp5af765f2010-03-30 11:12:18 +000010632#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +000010633 if ((LocaleCompare(name,"ICC") == 0) ||
10634 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +000010635 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010636
10637 if (ping_exclude_iCCP == MagickFalse)
10638 {
glennrpecab7d72013-05-14 22:50:32 +000010639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10640 " Setting up iCCP chunk");
10641
cristy16ea1392012-03-21 20:38:41 +000010642 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +000010643#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +000010644 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +000010645#else
10646 (png_const_bytep) GetStringInfoDatum(profile),
10647#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010648 (png_uint_32) GetStringInfoLength(profile));
glennrp918b9dc2013-04-03 13:41:41 +000010649 ping_have_iCCP = MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010650 }
glennrp26f37912010-12-23 16:22:42 +000010651 }
glennrp0fe50b42010-11-16 03:52:51 +000010652
glennrpc8cbc5d2011-01-01 00:12:34 +000010653 else
cristy3ed852e2009-09-05 21:47:34 +000010654#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010655 if (ping_exclude_zCCP == MagickFalse)
10656 {
glennrpecab7d72013-05-14 22:50:32 +000010657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10658 " Setting up zTXT chunk with uuencoded ICC");
glennrpcf002022011-01-30 02:38:15 +000010659 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +000010660 (unsigned char *) name,(unsigned char *) name,
10661 GetStringInfoDatum(profile),
10662 (png_uint_32) GetStringInfoLength(profile));
glennrpecab7d72013-05-14 22:50:32 +000010663 ping_have_iCCP = MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010664 }
10665 }
glennrp0b206f52011-01-07 04:55:32 +000010666
glennrpc8cbc5d2011-01-01 00:12:34 +000010667 if (logging != MagickFalse)
10668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10669 " Setting up text chunk with %s profile",name);
10670
10671 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +000010672 }
cristy0d57eec2011-09-04 22:13:56 +000010673 }
cristy3ed852e2009-09-05 21:47:34 +000010674 }
10675
10676#if defined(PNG_WRITE_sRGB_SUPPORTED)
10677 if ((mng_info->have_write_global_srgb == 0) &&
glennrpecab7d72013-05-14 22:50:32 +000010678 ping_have_iCCP != MagickTrue &&
10679 (ping_have_sRGB != MagickFalse ||
10680 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +000010681 {
glennrp26f37912010-12-23 16:22:42 +000010682 if (ping_exclude_sRGB == MagickFalse)
10683 {
10684 /*
10685 Note image rendering intent.
10686 */
10687 if (logging != MagickFalse)
10688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10689 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +000010690
glennrp26f37912010-12-23 16:22:42 +000010691 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +000010692 Magick_RenderingIntent_to_PNG_RenderingIntent(
10693 image->rendering_intent)));
glennrp918b9dc2013-04-03 13:41:41 +000010694
10695 ping_have_sRGB = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010696 }
cristy3ed852e2009-09-05 21:47:34 +000010697 }
glennrp26f37912010-12-23 16:22:42 +000010698
glennrp5af765f2010-03-30 11:12:18 +000010699 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +000010700#endif
10701 {
glennrp2cc891a2010-12-24 13:44:32 +000010702 if (ping_exclude_gAMA == MagickFalse &&
glennrp918b9dc2013-04-03 13:41:41 +000010703 ping_have_iCCP == MagickFalse &&
10704 ping_have_sRGB == MagickFalse &&
glennrp2cc891a2010-12-24 13:44:32 +000010705 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +000010706 (image->gamma < .45 || image->gamma > .46)))
10707 {
cristy3ed852e2009-09-05 21:47:34 +000010708 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10709 {
10710 /*
10711 Note image gamma.
10712 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10713 */
10714 if (logging != MagickFalse)
10715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10716 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010717
cristy3ed852e2009-09-05 21:47:34 +000010718 png_set_gAMA(ping,ping_info,image->gamma);
10719 }
glennrp26f37912010-12-23 16:22:42 +000010720 }
glennrp2b013e42010-11-24 16:55:50 +000010721
glennrp918b9dc2013-04-03 13:41:41 +000010722 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010723 {
glennrp26f37912010-12-23 16:22:42 +000010724 if ((mng_info->have_write_global_chrm == 0) &&
10725 (image->chromaticity.red_primary.x != 0.0))
10726 {
10727 /*
10728 Note image chromaticity.
glennrp918b9dc2013-04-03 13:41:41 +000010729 Note: if cHRM+gAMA == sRGB write sRGB instead.
glennrp26f37912010-12-23 16:22:42 +000010730 */
10731 PrimaryInfo
10732 bp,
10733 gp,
10734 rp,
10735 wp;
cristy3ed852e2009-09-05 21:47:34 +000010736
glennrp26f37912010-12-23 16:22:42 +000010737 wp=image->chromaticity.white_point;
10738 rp=image->chromaticity.red_primary;
10739 gp=image->chromaticity.green_primary;
10740 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010741
glennrp26f37912010-12-23 16:22:42 +000010742 if (logging != MagickFalse)
10743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10744 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010745
glennrp26f37912010-12-23 16:22:42 +000010746 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10747 bp.x,bp.y);
10748 }
10749 }
cristy3ed852e2009-09-05 21:47:34 +000010750 }
glennrpdfd70802010-11-14 01:23:35 +000010751
glennrp26f37912010-12-23 16:22:42 +000010752 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010753 {
glennrp26f37912010-12-23 16:22:42 +000010754 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010755 {
glennrp26f37912010-12-23 16:22:42 +000010756 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010757 if (logging)
10758 {
10759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10760 " Setting up bKGD chunk");
10761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10762 " background color = (%d,%d,%d)",
10763 (int) ping_background.red,
10764 (int) ping_background.green,
10765 (int) ping_background.blue);
10766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10767 " index = %d, gray=%d",
10768 (int) ping_background.index,
10769 (int) ping_background.gray);
10770 }
10771 }
glennrp26f37912010-12-23 16:22:42 +000010772 }
10773
10774 if (ping_exclude_pHYs == MagickFalse)
10775 {
10776 if (ping_have_pHYs != MagickFalse)
10777 {
10778 png_set_pHYs(ping,ping_info,
10779 ping_pHYs_x_resolution,
10780 ping_pHYs_y_resolution,
10781 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010782
10783 if (logging)
10784 {
10785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10786 " Setting up pHYs chunk");
10787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10788 " x_resolution=%lu",
10789 (unsigned long) ping_pHYs_x_resolution);
10790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10791 " y_resolution=%lu",
10792 (unsigned long) ping_pHYs_y_resolution);
10793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10794 " unit_type=%lu",
10795 (unsigned long) ping_pHYs_unit_type);
10796 }
glennrp26f37912010-12-23 16:22:42 +000010797 }
glennrpdfd70802010-11-14 01:23:35 +000010798 }
10799
10800#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010801 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010802 {
glennrp26f37912010-12-23 16:22:42 +000010803 if (image->page.x || image->page.y)
10804 {
10805 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10806 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010807
glennrp26f37912010-12-23 16:22:42 +000010808 if (logging != MagickFalse)
10809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10810 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10811 (int) image->page.x, (int) image->page.y);
10812 }
glennrpdfd70802010-11-14 01:23:35 +000010813 }
10814#endif
10815
glennrpda8f3a72011-02-27 23:54:12 +000010816 if (mng_info->need_blob != MagickFalse)
10817 {
cristy16ea1392012-03-21 20:38:41 +000010818 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010819 MagickFalse)
10820 png_error(ping,"WriteBlob Failed");
10821
10822 ping_have_blob=MagickTrue;
10823 }
10824
cristy3ed852e2009-09-05 21:47:34 +000010825 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010826
glennrp39992b42010-11-14 00:03:43 +000010827 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010828 {
glennrp3b51f0e2010-11-27 18:14:08 +000010829 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010830 {
10831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10832 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10833 }
10834
10835 if (ping_color_type == 3)
10836 (void) png_set_tRNS(ping, ping_info,
10837 ping_trans_alpha,
10838 ping_num_trans,
10839 NULL);
10840
10841 else
10842 {
10843 (void) png_set_tRNS(ping, ping_info,
10844 NULL,
10845 0,
10846 &ping_trans_color);
10847
glennrp3b51f0e2010-11-27 18:14:08 +000010848 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010849 {
10850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010851 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010852 (int) ping_trans_color.red,
10853 (int) ping_trans_color.green,
10854 (int) ping_trans_color.blue);
10855 }
10856 }
glennrp991d11d2010-11-12 21:55:28 +000010857 }
10858
cristy3ed852e2009-09-05 21:47:34 +000010859 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010860 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010861
cristy3ed852e2009-09-05 21:47:34 +000010862 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010863
cristy3ed852e2009-09-05 21:47:34 +000010864 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010865 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010866
glennrp26f37912010-12-23 16:22:42 +000010867 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010868 {
glennrp4f25bd02011-01-01 18:51:28 +000010869 if ((image->page.width != 0 && image->page.width != image->columns) ||
10870 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010871 {
10872 unsigned char
10873 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010874
glennrp26f37912010-12-23 16:22:42 +000010875 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10876 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010877 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010878 PNGLong(chunk+4,(png_uint_32) image->page.width);
10879 PNGLong(chunk+8,(png_uint_32) image->page.height);
10880 chunk[12]=0; /* unit = pixels */
10881 (void) WriteBlob(image,13,chunk);
10882 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10883 }
cristy3ed852e2009-09-05 21:47:34 +000010884 }
10885
10886#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010887 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010888#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010889 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010890#undef PNG_HAVE_IDAT
10891#endif
10892
10893 png_set_packing(ping);
10894 /*
10895 Allocate memory.
10896 */
10897 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010898 if (image_depth > 8)
10899 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010900 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010901 {
glennrpb4a13412010-05-05 12:47:19 +000010902 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010903 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010904 break;
glennrp0fe50b42010-11-16 03:52:51 +000010905
glennrpb4a13412010-05-05 12:47:19 +000010906 case PNG_COLOR_TYPE_GRAY_ALPHA:
10907 rowbytes*=2;
10908 break;
glennrp0fe50b42010-11-16 03:52:51 +000010909
glennrpb4a13412010-05-05 12:47:19 +000010910 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010911 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010912 break;
glennrp0fe50b42010-11-16 03:52:51 +000010913
glennrpb4a13412010-05-05 12:47:19 +000010914 default:
10915 break;
cristy3ed852e2009-09-05 21:47:34 +000010916 }
glennrp3b51f0e2010-11-27 18:14:08 +000010917
10918 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010919 {
10920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10921 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010922
glennrpb4a13412010-05-05 12:47:19 +000010923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010924 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010925 }
cristy09973322013-06-30 21:06:30 +000010926 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10927 if (pixel_info == (MemoryInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010928 png_error(ping,"Allocation of memory for pixels failed");
cristy09973322013-06-30 21:06:30 +000010929 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
glennrp0fe50b42010-11-16 03:52:51 +000010930
cristy3ed852e2009-09-05 21:47:34 +000010931 /*
10932 Initialize image scanlines.
10933 */
cristyed552522009-10-16 14:04:35 +000010934 quantum_info=AcquireQuantumInfo(image_info,image);
10935 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010936 png_error(ping,"Memory allocation for quantum_info failed");
cristy3ed852e2009-09-05 21:47:34 +000010937 quantum_info->format=UndefinedQuantumFormat;
10938 quantum_info->depth=image_depth;
glennrp4b840d72012-11-22 16:01:16 +000010939 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
cristy3ed852e2009-09-05 21:47:34 +000010940 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010941
cristy3ed852e2009-09-05 21:47:34 +000010942 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrpfd164d22013-01-26 21:10:22 +000010943 !mng_info->write_png48 && !mng_info->write_png64 &&
glennrp8bb3a022010-12-13 20:40:04 +000010944 !mng_info->write_png32) &&
10945 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010946 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010947 image_matte == MagickFalse &&
10948 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010949 {
glennrp8bb3a022010-12-13 20:40:04 +000010950 /* Palette, Bilevel, or Opaque Monochrome */
cristy16ea1392012-03-21 20:38:41 +000010951 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010952 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010953
cristy3ed852e2009-09-05 21:47:34 +000010954 quantum_info->depth=8;
10955 for (pass=0; pass < num_passes; pass++)
10956 {
10957 /*
10958 Convert PseudoClass image to a PNG monochrome image.
10959 */
cristybb503372010-05-27 20:51:26 +000010960 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010961 {
glennrpd71e86a2011-02-24 01:28:37 +000010962 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10964 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010965
cristy16ea1392012-03-21 20:38:41 +000010966 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010967
cristy16ea1392012-03-21 20:38:41 +000010968 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010969 break;
glennrp0fe50b42010-11-16 03:52:51 +000010970
cristy3ed852e2009-09-05 21:47:34 +000010971 if (mng_info->IsPalette)
10972 {
cristy16ea1392012-03-21 20:38:41 +000010973 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10974 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010975 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10976 mng_info->write_png_depth &&
10977 mng_info->write_png_depth != old_bit_depth)
10978 {
10979 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010980 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010981 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010982 >> (8-old_bit_depth));
10983 }
10984 }
glennrp0fe50b42010-11-16 03:52:51 +000010985
cristy3ed852e2009-09-05 21:47:34 +000010986 else
10987 {
cristy16ea1392012-03-21 20:38:41 +000010988 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10989 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010990 }
glennrp0fe50b42010-11-16 03:52:51 +000010991
cristy3ed852e2009-09-05 21:47:34 +000010992 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010993 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010994 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010995 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010996
glennrp3b51f0e2010-11-27 18:14:08 +000010997 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10999 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000011000
glennrpcf002022011-01-30 02:38:15 +000011001 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000011002 }
11003 if (image->previous == (Image *) NULL)
11004 {
11005 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11006 if (status == MagickFalse)
11007 break;
11008 }
11009 }
11010 }
glennrp0fe50b42010-11-16 03:52:51 +000011011
glennrp8bb3a022010-12-13 20:40:04 +000011012 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000011013 {
glennrp0fe50b42010-11-16 03:52:51 +000011014 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrpfd164d22013-01-26 21:10:22 +000011015 !mng_info->write_png48 && !mng_info->write_png64 &&
11016 !mng_info->write_png32) && (image_matte != MagickFalse ||
11017 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11018 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000011019 {
cristy16ea1392012-03-21 20:38:41 +000011020 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000011021 *p;
glennrp0fe50b42010-11-16 03:52:51 +000011022
glennrp8bb3a022010-12-13 20:40:04 +000011023 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000011024 {
glennrp8bb3a022010-12-13 20:40:04 +000011025
cristybb503372010-05-27 20:51:26 +000011026 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000011027 {
cristy16ea1392012-03-21 20:38:41 +000011028 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011029
cristy16ea1392012-03-21 20:38:41 +000011030 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000011031 break;
glennrp2cc891a2010-12-24 13:44:32 +000011032
glennrp5af765f2010-03-30 11:12:18 +000011033 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000011034 {
glennrp8bb3a022010-12-13 20:40:04 +000011035 if (mng_info->IsPalette)
cristy16ea1392012-03-21 20:38:41 +000011036 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11037 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011038
glennrp8bb3a022010-12-13 20:40:04 +000011039 else
cristy16ea1392012-03-21 20:38:41 +000011040 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11041 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011042
glennrp3b51f0e2010-11-27 18:14:08 +000011043 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000011044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000011045 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000011046 }
glennrp2cc891a2010-12-24 13:44:32 +000011047
glennrp8bb3a022010-12-13 20:40:04 +000011048 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11049 {
11050 if (logging != MagickFalse && y == 0)
11051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11052 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000011053
cristy16ea1392012-03-21 20:38:41 +000011054 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11055 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000011056 }
glennrp2cc891a2010-12-24 13:44:32 +000011057
glennrp3b51f0e2010-11-27 18:14:08 +000011058 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000011059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000011060 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000011061
glennrpcf002022011-01-30 02:38:15 +000011062 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000011063 }
glennrp2cc891a2010-12-24 13:44:32 +000011064
glennrp8bb3a022010-12-13 20:40:04 +000011065 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000011066 {
glennrp8bb3a022010-12-13 20:40:04 +000011067 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11068 if (status == MagickFalse)
11069 break;
cristy3ed852e2009-09-05 21:47:34 +000011070 }
cristy3ed852e2009-09-05 21:47:34 +000011071 }
11072 }
glennrp8bb3a022010-12-13 20:40:04 +000011073
11074 else
11075 {
cristy16ea1392012-03-21 20:38:41 +000011076 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000011077 *p;
11078
11079 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000011080 {
glennrpfd164d22013-01-26 21:10:22 +000011081 if ((image_depth > 8) ||
11082 mng_info->write_png24 ||
glennrp8bb3a022010-12-13 20:40:04 +000011083 mng_info->write_png32 ||
glennrpfd164d22013-01-26 21:10:22 +000011084 mng_info->write_png48 ||
11085 mng_info->write_png64 ||
11086 (!mng_info->write_png8 && !mng_info->IsPalette))
glennrp8bb3a022010-12-13 20:40:04 +000011087 {
11088 for (y=0; y < (ssize_t) image->rows; y++)
11089 {
cristy862a33c2012-05-17 22:49:37 +000011090 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000011091
cristy16ea1392012-03-21 20:38:41 +000011092 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000011093 break;
glennrp2cc891a2010-12-24 13:44:32 +000011094
glennrp8bb3a022010-12-13 20:40:04 +000011095 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11096 {
11097 if (image->storage_class == DirectClass)
cristy16ea1392012-03-21 20:38:41 +000011098 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11099 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011100
glennrp8bb3a022010-12-13 20:40:04 +000011101 else
cristy16ea1392012-03-21 20:38:41 +000011102 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11103 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000011104 }
glennrp2cc891a2010-12-24 13:44:32 +000011105
glennrp8bb3a022010-12-13 20:40:04 +000011106 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11107 {
cristy16ea1392012-03-21 20:38:41 +000011108 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000011109 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000011110 exception);
glennrp2cc891a2010-12-24 13:44:32 +000011111
glennrp8bb3a022010-12-13 20:40:04 +000011112 if (logging != MagickFalse && y == 0)
11113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11114 " Writing GRAY_ALPHA PNG pixels (3)");
11115 }
glennrp2cc891a2010-12-24 13:44:32 +000011116
glennrp8bb3a022010-12-13 20:40:04 +000011117 else if (image_matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +000011118 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11119 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011120
glennrp8bb3a022010-12-13 20:40:04 +000011121 else
cristy16ea1392012-03-21 20:38:41 +000011122 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11123 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011124
glennrp8bb3a022010-12-13 20:40:04 +000011125 if (logging != MagickFalse && y == 0)
11126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11127 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000011128
glennrpcf002022011-01-30 02:38:15 +000011129 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000011130 }
11131 }
glennrp2cc891a2010-12-24 13:44:32 +000011132
glennrp8bb3a022010-12-13 20:40:04 +000011133 else
glennrpfd164d22013-01-26 21:10:22 +000011134 /* not ((image_depth > 8) ||
11135 mng_info->write_png24 || mng_info->write_png32 ||
11136 mng_info->write_png48 || mng_info->write_png64 ||
11137 (!mng_info->write_png8 && !mng_info->IsPalette))
11138 */
glennrp8bb3a022010-12-13 20:40:04 +000011139 {
11140 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11141 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11142 {
11143 if (logging != MagickFalse)
11144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11145 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000011146
glennrp8bb3a022010-12-13 20:40:04 +000011147 quantum_info->depth=8;
11148 image_depth=8;
11149 }
glennrp2cc891a2010-12-24 13:44:32 +000011150
glennrp8bb3a022010-12-13 20:40:04 +000011151 for (y=0; y < (ssize_t) image->rows; y++)
11152 {
11153 if (logging != MagickFalse && y == 0)
11154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11155 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000011156
cristy16ea1392012-03-21 20:38:41 +000011157 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000011158
cristy16ea1392012-03-21 20:38:41 +000011159 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000011160 break;
glennrp2cc891a2010-12-24 13:44:32 +000011161
glennrp8bb3a022010-12-13 20:40:04 +000011162 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000011163 {
glennrp4bf89732011-03-21 13:48:28 +000011164 quantum_info->depth=image->depth;
11165
cristy16ea1392012-03-21 20:38:41 +000011166 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11167 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000011168 }
glennrp2cc891a2010-12-24 13:44:32 +000011169
glennrp8bb3a022010-12-13 20:40:04 +000011170 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11171 {
11172 if (logging != MagickFalse && y == 0)
11173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11174 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000011175
cristy16ea1392012-03-21 20:38:41 +000011176 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000011177 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000011178 exception);
glennrp8bb3a022010-12-13 20:40:04 +000011179 }
glennrp2cc891a2010-12-24 13:44:32 +000011180
glennrp8bb3a022010-12-13 20:40:04 +000011181 else
glennrp8bb3a022010-12-13 20:40:04 +000011182 {
cristy16ea1392012-03-21 20:38:41 +000011183 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11184 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000011185
11186 if (logging != MagickFalse && y <= 2)
11187 {
11188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000011189 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000011190
11191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11192 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11193 (int)ping_pixels[0],(int)ping_pixels[1]);
11194 }
glennrp8bb3a022010-12-13 20:40:04 +000011195 }
glennrpcf002022011-01-30 02:38:15 +000011196 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000011197 }
11198 }
glennrp2cc891a2010-12-24 13:44:32 +000011199
glennrp8bb3a022010-12-13 20:40:04 +000011200 if (image->previous == (Image *) NULL)
11201 {
11202 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11203 if (status == MagickFalse)
11204 break;
11205 }
cristy3ed852e2009-09-05 21:47:34 +000011206 }
glennrp8bb3a022010-12-13 20:40:04 +000011207 }
11208 }
11209
cristyb32b90a2009-09-07 21:45:48 +000011210 if (quantum_info != (QuantumInfo *) NULL)
11211 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000011212
11213 if (logging != MagickFalse)
11214 {
11215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000011216 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000011217
cristy3ed852e2009-09-05 21:47:34 +000011218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011219 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000011220
cristy3ed852e2009-09-05 21:47:34 +000011221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011222 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000011223
cristy3ed852e2009-09-05 21:47:34 +000011224 if (mng_info->write_png_depth)
11225 {
11226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011227 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011228 }
glennrp0fe50b42010-11-16 03:52:51 +000011229
cristy3ed852e2009-09-05 21:47:34 +000011230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000011231 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011232
cristy3ed852e2009-09-05 21:47:34 +000011233 if (mng_info->write_png_colortype)
11234 {
11235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011236 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011237 }
glennrp0fe50b42010-11-16 03:52:51 +000011238
cristy3ed852e2009-09-05 21:47:34 +000011239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000011240 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011241
cristy3ed852e2009-09-05 21:47:34 +000011242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000011243 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000011244 }
11245 /*
glennrpa0ed0092011-04-18 16:36:29 +000011246 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000011247 */
glennrp823b55c2011-03-14 18:46:46 +000011248 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000011249 {
glennrp26f37912010-12-23 16:22:42 +000011250 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000011251 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000011252 while (property != (const char *) NULL)
11253 {
11254 png_textp
11255 text;
glennrp2cc891a2010-12-24 13:44:32 +000011256
cristy16ea1392012-03-21 20:38:41 +000011257 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000011258
glennrpe4d5faf2013-07-22 00:11:04 +000011259 /* Don't write any "png:" or "jpeg:" properties; those are just for
11260 * "identify" or for passing through to another JPEG
11261 */
11262 if ((LocaleNCompare(property,"png:",4) != 0 &&
glennrpa3d5f0e2014-01-30 18:58:51 +000011263 LocaleNCompare(property,"jpeg:",5) != 0) &&
glennrpe4d5faf2013-07-22 00:11:04 +000011264
glennrpa0ed0092011-04-18 16:36:29 +000011265
11266 /* Suppress density and units if we wrote a pHYs chunk */
11267 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000011268 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000011269 LocaleCompare(property,"units") != 0) &&
11270
11271 /* Suppress the IM-generated Date:create and Date:modify */
11272 (ping_exclude_date == MagickFalse ||
11273 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000011274 {
glennrpc70af4a2011-03-07 00:08:23 +000011275 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000011276 {
cristya865ccd2012-07-28 00:33:10 +000011277
glennrpecab7d72013-05-14 22:50:32 +000011278#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +000011279 text=(png_textp) png_malloc(ping,
11280 (png_alloc_size_t) sizeof(png_text));
11281#else
11282 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11283#endif
glennrpc70af4a2011-03-07 00:08:23 +000011284 text[0].key=(char *) property;
11285 text[0].text=(char *) value;
11286 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000011287
glennrpc70af4a2011-03-07 00:08:23 +000011288 if (ping_exclude_tEXt != MagickFalse)
11289 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11290
11291 else if (ping_exclude_zTXt != MagickFalse)
11292 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11293
11294 else
glennrp26f37912010-12-23 16:22:42 +000011295 {
glennrpc70af4a2011-03-07 00:08:23 +000011296 text[0].compression=image_info->compression == NoCompression ||
11297 (image_info->compression == UndefinedCompression &&
11298 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11299 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000011300 }
glennrp2cc891a2010-12-24 13:44:32 +000011301
glennrpc70af4a2011-03-07 00:08:23 +000011302 if (logging != MagickFalse)
11303 {
11304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11305 " Setting up text chunk");
11306
11307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpcbc92152013-02-04 15:46:22 +000011308 " keyword: '%s'",text[0].key);
glennrpc70af4a2011-03-07 00:08:23 +000011309 }
11310
11311 png_set_text(ping,ping_info,text,1);
11312 png_free(ping,text);
11313 }
glennrp26f37912010-12-23 16:22:42 +000011314 }
11315 property=GetNextImageProperty(image);
11316 }
cristy3ed852e2009-09-05 21:47:34 +000011317 }
11318
11319 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011320 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011321
11322 if (logging != MagickFalse)
11323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11324 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000011325
cristy3ed852e2009-09-05 21:47:34 +000011326 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000011327
cristy3ed852e2009-09-05 21:47:34 +000011328 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11329 {
11330 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000011331 (ping_width != mng_info->page.width) ||
11332 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000011333 {
11334 unsigned char
11335 chunk[32];
11336
11337 /*
11338 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11339 */
11340 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11341 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000011342 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000011343 chunk[4]=4;
11344 chunk[5]=0; /* frame name separator (no name) */
11345 chunk[6]=1; /* flag for changing delay, for next frame only */
11346 chunk[7]=0; /* flag for changing frame timeout */
11347 chunk[8]=1; /* flag for changing frame clipping for next frame */
11348 chunk[9]=0; /* flag for changing frame sync_id */
11349 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11350 chunk[14]=0; /* clipping boundaries delta type */
11351 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11352 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000011353 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000011354 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11355 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000011356 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000011357 (void) WriteBlob(image,31,chunk);
11358 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11359 mng_info->old_framing_mode=4;
11360 mng_info->framing_mode=1;
11361 }
glennrp0fe50b42010-11-16 03:52:51 +000011362
cristy3ed852e2009-09-05 21:47:34 +000011363 else
11364 mng_info->framing_mode=3;
11365 }
11366 if (mng_info->write_mng && !mng_info->need_fram &&
11367 ((int) image->dispose == 3))
glennrpedaa0382012-04-12 14:16:21 +000011368 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
glennrp0fe50b42010-11-16 03:52:51 +000011369
cristy3ed852e2009-09-05 21:47:34 +000011370 /*
11371 Free PNG resources.
11372 */
glennrp5af765f2010-03-30 11:12:18 +000011373
cristy3ed852e2009-09-05 21:47:34 +000011374 png_destroy_write_struct(&ping,&ping_info);
11375
cristy09973322013-06-30 21:06:30 +000011376 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +000011377
cristy16ea1392012-03-21 20:38:41 +000011378 if (ping_have_blob != MagickFalse)
11379 (void) CloseBlob(image);
11380
11381 image_info=DestroyImageInfo(image_info);
11382 image=DestroyImage(image);
11383
glennrpb9cfe272010-12-21 15:08:06 +000011384 /* Store bit depth actually written */
11385 s[0]=(char) ping_bit_depth;
11386 s[1]='\0';
11387
cristy16ea1392012-03-21 20:38:41 +000011388 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000011389
cristy3ed852e2009-09-05 21:47:34 +000011390 if (logging != MagickFalse)
11391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11392 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011393
glennrp868fff32014-03-16 22:09:06 +000011394#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpedaa0382012-04-12 14:16:21 +000011395 UnlockSemaphoreInfo(ping_semaphore);
11396#endif
11397
11398 /* } for navigation to beginning of SETJMP-protected block. Revert to
11399 * Throwing an Exception when an error occurs.
11400 */
11401
cristy3ed852e2009-09-05 21:47:34 +000011402 return(MagickTrue);
11403/* End write one PNG image */
glennrpedaa0382012-04-12 14:16:21 +000011404
cristy3ed852e2009-09-05 21:47:34 +000011405}
11406
11407/*
11408%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11409% %
11410% %
11411% %
11412% W r i t e P N G I m a g e %
11413% %
11414% %
11415% %
11416%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11417%
11418% WritePNGImage() writes a Portable Network Graphics (PNG) or
11419% Multiple-image Network Graphics (MNG) image file.
11420%
11421% MNG support written by Glenn Randers-Pehrson, glennrp@image...
11422%
11423% The format of the WritePNGImage method is:
11424%
cristy16ea1392012-03-21 20:38:41 +000011425% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11426% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011427%
11428% A description of each parameter follows:
11429%
11430% o image_info: the image info.
11431%
11432% o image: The image.
11433%
cristy16ea1392012-03-21 20:38:41 +000011434% o exception: return any errors or warnings in this structure.
11435%
cristy3ed852e2009-09-05 21:47:34 +000011436% Returns MagickTrue on success, MagickFalse on failure.
11437%
11438% Communicating with the PNG encoder:
11439%
11440% While the datastream written is always in PNG format and normally would
11441% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000011442% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000011443%
glennrp5a39f372011-02-25 04:52:16 +000011444% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11445% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000011446% is present, the tRNS chunk must only have values 0 and 255
11447% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000011448% transparent). If other values are present they will be
11449% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000011450% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000011451% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11452% of any resulting fully-transparent pixels is changed to
11453% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000011454%
11455% If you want better quantization or dithering of the colors
11456% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000011457% PNG encoder. The pixels contain 8-bit indices even if
11458% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000011459% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000011460% PNG grayscale type might be slightly more efficient. Please
11461% note that writing to the PNG8 format may result in loss
11462% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000011463%
11464% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11465% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000011466% one of the colors as transparent. The only loss incurred
11467% is reduction of sample depth to 8. If the image has more
11468% than one transparent color, has semitransparent pixels, or
11469% has an opaque pixel with the same RGB components as the
11470% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000011471%
11472% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11473% transparency is permitted, i.e., the alpha sample for
11474% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000011475% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000011476% The only loss in data is the reduction of the sample depth
11477% to 8.
cristy3ed852e2009-09-05 21:47:34 +000011478%
glennrpfd164d22013-01-26 21:10:22 +000011479% o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11480% chunk can be present to convey binary transparency by naming
11481% one of the colors as transparent. If the image has more
11482% than one transparent color, has semitransparent pixels, or
11483% has an opaque pixel with the same RGB components as the
11484% transparent color, an image is not written.
11485%
11486% o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11487% transparency is permitted, i.e., the alpha sample for
11488% each pixel can have any value from 0 to 65535. The alpha
11489% channel is present even if the image is fully opaque.
11490%
glennrp5830fbc2013-01-27 06:11:45 +000011491% o PNG00: A PNG that inherits its colortype and bit-depth from the input
11492% image, if the input was a PNG, is written. If these values
11493% cannot be found, then "PNG00" falls back to the regular "PNG"
11494% format.
11495%
cristy3ed852e2009-09-05 21:47:34 +000011496% o -define: For more precise control of the PNG output, you can use the
11497% Image options "png:bit-depth" and "png:color-type". These
11498% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000011499% from the application programming interfaces. The options
11500% are case-independent and are converted to lowercase before
11501% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000011502%
11503% png:color-type can be 0, 2, 3, 4, or 6.
11504%
11505% When png:color-type is 0 (Grayscale), png:bit-depth can
11506% be 1, 2, 4, 8, or 16.
11507%
11508% When png:color-type is 2 (RGB), png:bit-depth can
11509% be 8 or 16.
11510%
11511% When png:color-type is 3 (Indexed), png:bit-depth can
11512% be 1, 2, 4, or 8. This refers to the number of bits
11513% used to store the index. The color samples always have
11514% bit-depth 8 in indexed PNG files.
11515%
11516% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11517% png:bit-depth can be 8 or 16.
11518%
glennrpfd164d22013-01-26 21:10:22 +000011519% If the image cannot be written without loss with the
11520% requested bit-depth and color-type, a PNG file will not
11521% be written, a warning will be issued, and the encoder will
11522% return MagickFalse.
glennrp5a39f372011-02-25 04:52:16 +000011523%
cristy3ed852e2009-09-05 21:47:34 +000011524% Since image encoders should not be responsible for the "heavy lifting",
11525% the user should make sure that ImageMagick has already reduced the
11526% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000011527% transparency prior to attempting to write the image with depth, color,
cristy16ea1392012-03-21 20:38:41 +000011528% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000011529%
cristy3ed852e2009-09-05 21:47:34 +000011530% Note that another definition, "png:bit-depth-written" exists, but it
11531% is not intended for external use. It is only used internally by the
11532% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11533%
11534% It is possible to request that the PNG encoder write previously-formatted
11535% ancillary chunks in the output PNG file, using the "-profile" commandline
11536% option as shown below or by setting the profile via a programming
11537% interface:
11538%
11539% -profile PNG-chunk-x:<file>
11540%
11541% where x is a location flag and <file> is a file containing the chunk
11542% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000011543% This encoder will compute the chunk length and CRC, so those must not
11544% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000011545%
11546% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11547% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11548% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000011549% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000011550%
glennrpbb8a7332010-11-13 15:17:35 +000011551% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000011552%
glennrp3241bd02010-12-12 04:36:28 +000011553% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000011554%
glennrpd6afd542010-11-19 01:53:05 +000011555% o 32-bit depth is reduced to 16.
11556% o 16-bit depth is reduced to 8 if all pixels contain samples whose
11557% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000011558% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000011559% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000011560% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000011561% o Grayscale images are reduced to 1, 2, or 4 bit depth if
11562% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000011563% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000011564% o If matte channel is present but only one transparent color is
11565% present, RGB+tRNS is written instead of RGBA
11566% o Opaque matte channel is removed (or added, if color-type 4 or 6
11567% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000011568%
cristy3ed852e2009-09-05 21:47:34 +000011569%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11570*/
11571static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy16ea1392012-03-21 20:38:41 +000011572 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011573{
11574 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011575 excluding,
11576 logging,
11577 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011578 status;
11579
11580 MngInfo
11581 *mng_info;
11582
11583 const char
11584 *value;
11585
11586 int
glennrp5c7cf4e2010-12-24 00:30:00 +000011587 source;
11588
cristy3ed852e2009-09-05 21:47:34 +000011589 /*
11590 Open image file.
11591 */
11592 assert(image_info != (const ImageInfo *) NULL);
11593 assert(image_info->signature == MagickSignature);
11594 assert(image != (Image *) NULL);
11595 assert(image->signature == MagickSignature);
11596 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011597 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011598 /*
11599 Allocate a MngInfo structure.
11600 */
11601 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011602 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000011603
cristy3ed852e2009-09-05 21:47:34 +000011604 if (mng_info == (MngInfo *) NULL)
11605 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011606
cristy3ed852e2009-09-05 21:47:34 +000011607 /*
11608 Initialize members of the MngInfo structure.
11609 */
11610 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11611 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000011612 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011613 have_mng_structure=MagickTrue;
11614
11615 /* See if user has requested a specific PNG subformat */
11616
11617 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11618 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11619 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
glennrpfd164d22013-01-26 21:10:22 +000011620 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11621 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
cristy3ed852e2009-09-05 21:47:34 +000011622
cristy092ec8d2013-04-26 13:46:22 +000011623 value=GetImageOption(image_info,"png:format");
glennrp5a4989d2014-04-23 00:34:09 +000011624 if (value == (char *) NULL)
11625 if (LocaleCompare(image_info->magick,"PNG00") == 0)
glennrpb381a262012-02-11 17:49:49 +000011626
glennrp5a4989d2014-04-23 00:34:09 +000011627 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
glennrpb381a262012-02-11 17:49:49 +000011628 {
glennrpf70c4d22013-03-19 15:26:48 +000011629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11630 " Format=%s",value);
11631
glennrpfd164d22013-01-26 21:10:22 +000011632 mng_info->write_png8 = MagickFalse;
11633 mng_info->write_png24 = MagickFalse;
11634 mng_info->write_png32 = MagickFalse;
11635 mng_info->write_png48 = MagickFalse;
11636 mng_info->write_png64 = MagickFalse;
11637
glennrpb381a262012-02-11 17:49:49 +000011638 if (LocaleCompare(value,"png8") == 0)
glennrpb381a262012-02-11 17:49:49 +000011639 mng_info->write_png8 = MagickTrue;
glennrpb381a262012-02-11 17:49:49 +000011640
11641 else if (LocaleCompare(value,"png24") == 0)
glennrpb381a262012-02-11 17:49:49 +000011642 mng_info->write_png24 = MagickTrue;
glennrpb381a262012-02-11 17:49:49 +000011643
11644 else if (LocaleCompare(value,"png32") == 0)
glennrpb381a262012-02-11 17:49:49 +000011645 mng_info->write_png32 = MagickTrue;
glennrpfd164d22013-01-26 21:10:22 +000011646
11647 else if (LocaleCompare(value,"png48") == 0)
11648 mng_info->write_png48 = MagickTrue;
11649
11650 else if (LocaleCompare(value,"png64") == 0)
11651 mng_info->write_png64 = MagickTrue;
glennrp5830fbc2013-01-27 06:11:45 +000011652
glennrp5a4989d2014-04-23 00:34:09 +000011653 else if ((LocaleCompare(value,"png00") == 0) ||
11654 LocaleCompare(image_info->magick,"PNG00") == 0)
glennrp5830fbc2013-01-27 06:11:45 +000011655 {
glennrp3398b5b2013-05-03 23:10:31 +000011656 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11657 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
glennrpf70c4d22013-03-19 15:26:48 +000011658
11659 if (value != (char *) NULL)
11660 {
11661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11662 " png00 inherited bit depth=%s",value);
11663
11664 if (LocaleCompare(value,"1") == 0)
11665 mng_info->write_png_depth = 1;
11666
glennrp5a4989d2014-04-23 00:34:09 +000011667 else if (LocaleCompare(value,"2") == 0)
glennrpf70c4d22013-03-19 15:26:48 +000011668 mng_info->write_png_depth = 2;
11669
glennrp5a4989d2014-04-23 00:34:09 +000011670 else if (LocaleCompare(value,"4") == 0)
glennrpf70c4d22013-03-19 15:26:48 +000011671 mng_info->write_png_depth = 4;
11672
11673 else if (LocaleCompare(value,"8") == 0)
11674 mng_info->write_png_depth = 8;
11675
11676 else if (LocaleCompare(value,"16") == 0)
11677 mng_info->write_png_depth = 16;
11678 }
11679
glennrp3398b5b2013-05-03 23:10:31 +000011680 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
glennrpf70c4d22013-03-19 15:26:48 +000011681
11682 if (value != (char *) NULL)
11683 {
11684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11685 " png00 inherited color type=%s",value);
11686
11687 if (LocaleCompare(value,"0") == 0)
11688 mng_info->write_png_colortype = 1;
11689
11690 else if (LocaleCompare(value,"2") == 0)
11691 mng_info->write_png_colortype = 3;
11692
11693 else if (LocaleCompare(value,"3") == 0)
11694 mng_info->write_png_colortype = 4;
11695
11696 else if (LocaleCompare(value,"4") == 0)
11697 mng_info->write_png_colortype = 5;
11698
11699 else if (LocaleCompare(value,"6") == 0)
11700 mng_info->write_png_colortype = 7;
11701 }
glennrp5830fbc2013-01-27 06:11:45 +000011702 }
11703 }
11704
cristy3ed852e2009-09-05 21:47:34 +000011705 if (mng_info->write_png8)
11706 {
glennrp9c1eb072010-06-06 22:19:15 +000011707 mng_info->write_png_colortype = /* 3 */ 4;
11708 mng_info->write_png_depth = 8;
11709 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011710 }
11711
11712 if (mng_info->write_png24)
11713 {
glennrp9c1eb072010-06-06 22:19:15 +000011714 mng_info->write_png_colortype = /* 2 */ 3;
11715 mng_info->write_png_depth = 8;
11716 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011717
cristy8a46d822012-08-28 23:32:39 +000011718 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +000011719 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011720
glennrp9c1eb072010-06-06 22:19:15 +000011721 else
cristy16ea1392012-03-21 20:38:41 +000011722 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011723
cristy16ea1392012-03-21 20:38:41 +000011724 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011725 }
11726
11727 if (mng_info->write_png32)
11728 {
glennrp9c1eb072010-06-06 22:19:15 +000011729 mng_info->write_png_colortype = /* 6 */ 7;
11730 mng_info->write_png_depth = 8;
11731 image->depth = 8;
dirk47da46d2014-04-24 18:48:54 +000011732 image->alpha_trait = BlendPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +000011733
glennrp197c8e62014-04-22 23:45:20 +000011734 (void) SetImageType(image,TrueColorMatteType,exception);
cristy16ea1392012-03-21 20:38:41 +000011735 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011736 }
11737
glennrpfd164d22013-01-26 21:10:22 +000011738 if (mng_info->write_png48)
11739 {
11740 mng_info->write_png_colortype = /* 2 */ 3;
11741 mng_info->write_png_depth = 16;
11742 image->depth = 16;
11743
glennrp4dda64f2013-01-26 21:20:24 +000011744 if (image->alpha_trait == BlendPixelTrait)
11745 (void) SetImageType(image,TrueColorMatteType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011746
11747 else
glennrp4dda64f2013-01-26 21:20:24 +000011748 (void) SetImageType(image,TrueColorType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011749
glennrp4dda64f2013-01-26 21:20:24 +000011750 (void) SyncImage(image,exception);
glennrpfd164d22013-01-26 21:10:22 +000011751 }
11752
11753 if (mng_info->write_png64)
11754 {
11755 mng_info->write_png_colortype = /* 6 */ 7;
11756 mng_info->write_png_depth = 16;
11757 image->depth = 16;
dirk47da46d2014-04-24 18:48:54 +000011758 image->alpha_trait = BlendPixelTrait;
glennrpfd164d22013-01-26 21:10:22 +000011759
glennrp197c8e62014-04-22 23:45:20 +000011760 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp4dda64f2013-01-26 21:20:24 +000011761 (void) SyncImage(image,exception);
glennrpfd164d22013-01-26 21:10:22 +000011762 }
11763
cristy092ec8d2013-04-26 13:46:22 +000011764 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011765
cristy3ed852e2009-09-05 21:47:34 +000011766 if (value != (char *) NULL)
11767 {
11768 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011769 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011770
cristy3ed852e2009-09-05 21:47:34 +000011771 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011772 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011773
cristy3ed852e2009-09-05 21:47:34 +000011774 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011775 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011776
cristy3ed852e2009-09-05 21:47:34 +000011777 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011778 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011779
cristy3ed852e2009-09-05 21:47:34 +000011780 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011781 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011782
glennrpbb8a7332010-11-13 15:17:35 +000011783 else
cristy16ea1392012-03-21 20:38:41 +000011784 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011785 GetMagickModule(),CoderWarning,
11786 "ignoring invalid defined png:bit-depth",
11787 "=%s",value);
11788
cristy3ed852e2009-09-05 21:47:34 +000011789 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011791 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011792 }
glennrp0fe50b42010-11-16 03:52:51 +000011793
cristy092ec8d2013-04-26 13:46:22 +000011794 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011795
cristy3ed852e2009-09-05 21:47:34 +000011796 if (value != (char *) NULL)
11797 {
11798 /* We must store colortype+1 because 0 is a valid colortype */
11799 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011800 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011801
cristy16ea1392012-03-21 20:38:41 +000011802 else if (LocaleCompare(value,"1") == 0)
11803 mng_info->write_png_colortype = 2;
11804
cristy3ed852e2009-09-05 21:47:34 +000011805 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011806 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011807
cristy3ed852e2009-09-05 21:47:34 +000011808 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011809 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011810
cristy3ed852e2009-09-05 21:47:34 +000011811 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011812 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011813
cristy3ed852e2009-09-05 21:47:34 +000011814 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011815 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011816
glennrpbb8a7332010-11-13 15:17:35 +000011817 else
cristy16ea1392012-03-21 20:38:41 +000011818 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011819 GetMagickModule(),CoderWarning,
11820 "ignoring invalid defined png:color-type",
11821 "=%s",value);
11822
cristy3ed852e2009-09-05 21:47:34 +000011823 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011825 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011826 }
11827
glennrp0e8ea192010-12-24 18:00:33 +000011828 /* Check for chunks to be excluded:
11829 *
glennrp0dff56c2011-01-29 19:10:02 +000011830 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011831 * listed in the "unused_chunks" array, above.
11832 *
cristy5d6fc9c2011-12-27 03:10:42 +000011833 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011834 * define (in the image properties or in the image artifacts)
11835 * or via a mng_info member. For convenience, in addition
11836 * to or instead of a comma-separated list of chunks, the
11837 * "exclude-chunk" string can be simply "all" or "none".
11838 *
11839 * The exclude-chunk define takes priority over the mng_info.
11840 *
cristy5d6fc9c2011-12-27 03:10:42 +000011841 * A "png:include-chunk" define takes priority over both the
11842 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011843 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011844 * well as a comma-separated list. Chunks that are unknown to
11845 * ImageMagick are always excluded, regardless of their "copy-safe"
11846 * status according to the PNG specification, and even if they
glennrpaa192b12012-01-17 21:35:21 +000011847 * appear in the "include-chunk" list. Such defines appearing among
11848 * the image options take priority over those found among the image
11849 * artifacts.
glennrp0e8ea192010-12-24 18:00:33 +000011850 *
11851 * Finally, all chunks listed in the "unused_chunks" array are
11852 * automatically excluded, regardless of the other instructions
11853 * or lack thereof.
11854 *
11855 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11856 * will not be written and the gAMA chunk will only be written if it
11857 * is not between .45 and .46, or approximately (1.0/2.2).
11858 *
11859 * If you exclude tRNS and the image has transparency, the colortype
11860 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11861 *
11862 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011863 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011864 */
11865
glennrp26f37912010-12-23 16:22:42 +000011866 mng_info->ping_exclude_bKGD=MagickFalse;
11867 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011868 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011869 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11870 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011871 mng_info->ping_exclude_iCCP=MagickFalse;
11872 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11873 mng_info->ping_exclude_oFFs=MagickFalse;
11874 mng_info->ping_exclude_pHYs=MagickFalse;
11875 mng_info->ping_exclude_sRGB=MagickFalse;
11876 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011877 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011878 mng_info->ping_exclude_vpAg=MagickFalse;
11879 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11880 mng_info->ping_exclude_zTXt=MagickFalse;
11881
glennrp8d3d6e52011-04-19 04:39:51 +000011882 mng_info->ping_preserve_colormap=MagickFalse;
11883
cristy092ec8d2013-04-26 13:46:22 +000011884 value=GetImageOption(image_info,"png:preserve-colormap");
glennrp8d3d6e52011-04-19 04:39:51 +000011885 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011886 value=GetImageArtifact(image,"png:preserve-colormap");
glennrp8d3d6e52011-04-19 04:39:51 +000011887 if (value != NULL)
11888 mng_info->ping_preserve_colormap=MagickTrue;
11889
glennrpecab7d72013-05-14 22:50:32 +000011890 mng_info->ping_preserve_iCCP=MagickFalse;
11891
11892 value=GetImageOption(image_info,"png:preserve-iCCP");
11893 if (value == NULL)
11894 value=GetImageArtifact(image,"png:preserve-iCCP");
11895 if (value != NULL)
11896 mng_info->ping_preserve_iCCP=MagickTrue;
11897
11898 /* These compression-level, compression-strategy, and compression-filter
glennrp18682582011-06-30 18:11:47 +000011899 * defines take precedence over values from the -quality option.
11900 */
cristy092ec8d2013-04-26 13:46:22 +000011901 value=GetImageOption(image_info,"png:compression-level");
glennrp18682582011-06-30 18:11:47 +000011902 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011903 value=GetImageArtifact(image,"png:compression-level");
glennrp18682582011-06-30 18:11:47 +000011904 if (value != NULL)
11905 {
glennrp18682582011-06-30 18:11:47 +000011906 /* We have to add 1 to everything because 0 is a valid input,
11907 * and we want to use 0 (the default) to mean undefined.
11908 */
11909 if (LocaleCompare(value,"0") == 0)
11910 mng_info->write_png_compression_level = 1;
11911
glennrp0ffb95c2012-01-30 21:16:22 +000011912 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011913 mng_info->write_png_compression_level = 2;
11914
11915 else if (LocaleCompare(value,"2") == 0)
11916 mng_info->write_png_compression_level = 3;
11917
11918 else if (LocaleCompare(value,"3") == 0)
11919 mng_info->write_png_compression_level = 4;
11920
11921 else if (LocaleCompare(value,"4") == 0)
11922 mng_info->write_png_compression_level = 5;
11923
11924 else if (LocaleCompare(value,"5") == 0)
11925 mng_info->write_png_compression_level = 6;
11926
11927 else if (LocaleCompare(value,"6") == 0)
11928 mng_info->write_png_compression_level = 7;
11929
11930 else if (LocaleCompare(value,"7") == 0)
11931 mng_info->write_png_compression_level = 8;
11932
11933 else if (LocaleCompare(value,"8") == 0)
11934 mng_info->write_png_compression_level = 9;
11935
11936 else if (LocaleCompare(value,"9") == 0)
11937 mng_info->write_png_compression_level = 10;
11938
11939 else
cristy16ea1392012-03-21 20:38:41 +000011940 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011941 GetMagickModule(),CoderWarning,
11942 "ignoring invalid defined png:compression-level",
11943 "=%s",value);
11944 }
11945
cristy092ec8d2013-04-26 13:46:22 +000011946 value=GetImageOption(image_info,"png:compression-strategy");
glennrp18682582011-06-30 18:11:47 +000011947 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011948 value=GetImageArtifact(image,"png:compression-strategy");
glennrp18682582011-06-30 18:11:47 +000011949 if (value != NULL)
11950 {
glennrp18682582011-06-30 18:11:47 +000011951 if (LocaleCompare(value,"0") == 0)
11952 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11953
11954 else if (LocaleCompare(value,"1") == 0)
11955 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11956
11957 else if (LocaleCompare(value,"2") == 0)
11958 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11959
11960 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011961#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011962 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011963#else
11964 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11965#endif
glennrp18682582011-06-30 18:11:47 +000011966
11967 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011968#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011969 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011970#else
11971 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11972#endif
glennrp18682582011-06-30 18:11:47 +000011973
11974 else
cristy16ea1392012-03-21 20:38:41 +000011975 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011976 GetMagickModule(),CoderWarning,
11977 "ignoring invalid defined png:compression-strategy",
11978 "=%s",value);
11979 }
11980
cristy092ec8d2013-04-26 13:46:22 +000011981 value=GetImageOption(image_info,"png:compression-filter");
glennrp18682582011-06-30 18:11:47 +000011982 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011983 value=GetImageArtifact(image,"png:compression-filter");
glennrp18682582011-06-30 18:11:47 +000011984 if (value != NULL)
11985 {
glennrp18682582011-06-30 18:11:47 +000011986 /* To do: combinations of filters allowed by libpng
11987 * masks 0x08 through 0xf8
11988 *
11989 * Implement this as a comma-separated list of 0,1,2,3,4,5
11990 * where 5 is a special case meaning PNG_ALL_FILTERS.
11991 */
11992
11993 if (LocaleCompare(value,"0") == 0)
11994 mng_info->write_png_compression_filter = 1;
11995
cristyb19b8122012-10-22 11:03:30 +000011996 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011997 mng_info->write_png_compression_filter = 2;
11998
11999 else if (LocaleCompare(value,"2") == 0)
12000 mng_info->write_png_compression_filter = 3;
12001
12002 else if (LocaleCompare(value,"3") == 0)
12003 mng_info->write_png_compression_filter = 4;
12004
12005 else if (LocaleCompare(value,"4") == 0)
12006 mng_info->write_png_compression_filter = 5;
12007
12008 else if (LocaleCompare(value,"5") == 0)
12009 mng_info->write_png_compression_filter = 6;
12010
glennrp18682582011-06-30 18:11:47 +000012011 else
cristy16ea1392012-03-21 20:38:41 +000012012 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000012013 GetMagickModule(),CoderWarning,
12014 "ignoring invalid defined png:compression-filter",
12015 "=%s",value);
glennrp1a506ac2014-07-25 19:49:10 +000012016 }
12017
12018 for (source=0; source<8; source++)
12019 {
12020 value = NULL;
12021
glennrp2dd19062014-07-28 00:22:24 +000012022 if (source == 0)
12023 value=GetImageOption(image_info,"png:exclude-chunks");
12024
12025 if (source == 1)
12026 value=GetImageArtifact(image,"png:exclude-chunks");
12027
12028 if (source == 2)
12029 value=GetImageOption(image_info,"png:exclude-chunk");
12030
12031 if (source == 3)
12032 value=GetImageArtifact(image,"png:exclude-chunk");
12033
12034 if (source == 4)
12035 value=GetImageOption(image_info,"png:include-chunks");
12036
12037 if (source == 5)
12038 value=GetImageArtifact(image,"png:include-chunks");
12039
12040 if (source == 6)
12041 value=GetImageOption(image_info,"png:include-chunk");
12042
12043 if (source == 7)
12044 value=GetImageArtifact(image,"png:include-chunk");
glennrp18682582011-06-30 18:11:47 +000012045
glennrp1a506ac2014-07-25 19:49:10 +000012046 if (value == NULL)
12047 continue;
glennrp03812ae2010-12-24 01:31:34 +000012048
glennrp1a506ac2014-07-25 19:49:10 +000012049 if (source < 4)
12050 excluding = MagickTrue;
glennrp5c7cf4e2010-12-24 00:30:00 +000012051 else
glennrp1a506ac2014-07-25 19:49:10 +000012052 excluding = MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000012053
12054 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000012055 {
glennrp1a506ac2014-07-25 19:49:10 +000012056 if (source == 0 || source == 2)
12057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12058 " png:exclude-chunk=%s found in image options.\n", value);
12059 else if (source == 1 || source == 3)
glennrp2cc891a2010-12-24 13:44:32 +000012060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12061 " png:exclude-chunk=%s found in image artifacts.\n", value);
glennrp1a506ac2014-07-25 19:49:10 +000012062 else if (source == 4 || source == 6)
glennrp2cc891a2010-12-24 13:44:32 +000012063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a506ac2014-07-25 19:49:10 +000012064 " png:include-chunk=%s found in image options.\n", value);
12065 else /* if (source == 5 || source == 7) */
12066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12067 " png:include-chunk=%s found in image artifacts.\n", value);
glennrp2cc891a2010-12-24 13:44:32 +000012068 }
glennrp03812ae2010-12-24 01:31:34 +000012069
glennrp689efa22014-07-25 00:00:15 +000012070 if (IsOptionMember("all",value) != MagickFalse)
glennrp97d1b572013-05-24 01:41:32 +000012071 {
glennrp1a506ac2014-07-25 19:49:10 +000012072 mng_info->ping_exclude_bKGD=excluding;
12073 mng_info->ping_exclude_cHRM=excluding;
12074 mng_info->ping_exclude_date=excluding;
12075 mng_info->ping_exclude_EXIF=excluding;
12076 mng_info->ping_exclude_gAMA=excluding;
12077 mng_info->ping_exclude_iCCP=excluding;
12078 /* mng_info->ping_exclude_iTXt=excluding; */
12079 mng_info->ping_exclude_oFFs=excluding;
12080 mng_info->ping_exclude_pHYs=excluding;
12081 mng_info->ping_exclude_sRGB=excluding;
12082 mng_info->ping_exclude_tEXt=excluding;
12083 mng_info->ping_exclude_tRNS=excluding;
12084 mng_info->ping_exclude_vpAg=excluding;
12085 mng_info->ping_exclude_zCCP=excluding;
12086 mng_info->ping_exclude_zTXt=excluding;
glennrp03812ae2010-12-24 01:31:34 +000012087 }
glennrp280283d2014-08-01 15:02:04 +000012088
glennrp1a506ac2014-07-25 19:49:10 +000012089 if (IsOptionMember("none",value) != MagickFalse)
glennrpacba0042010-12-24 14:27:26 +000012090 {
cristya20c8fe2014-08-14 11:55:11 +000012091 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12092 MagickTrue;
12093 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12094 MagickTrue;
12095 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12096 MagickTrue;
12097 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12098 MagickTrue;
12099 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12100 MagickTrue;
12101 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12102 MagickTrue;
glennrp1a506ac2014-07-25 19:49:10 +000012103 /* mng_info->ping_exclude_iTXt=!excluding; */
cristya20c8fe2014-08-14 11:55:11 +000012104 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12105 MagickTrue;
12106 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12107 MagickTrue;
12108 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12109 MagickTrue;
12110 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12111 MagickTrue;
12112 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12113 MagickTrue;
12114 mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12115 MagickTrue;
12116 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12117 MagickTrue;
12118 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12119 MagickTrue;
glennrpacba0042010-12-24 14:27:26 +000012120 }
12121
glennrp1a506ac2014-07-25 19:49:10 +000012122 if (IsOptionMember("bkgd",value) != MagickFalse)
12123 mng_info->ping_exclude_bKGD=excluding;
glennrp26f37912010-12-23 16:22:42 +000012124
glennrp1a506ac2014-07-25 19:49:10 +000012125 if (IsOptionMember("chrm",value) != MagickFalse)
12126 mng_info->ping_exclude_cHRM=excluding;
glennrp03812ae2010-12-24 01:31:34 +000012127
glennrp1a506ac2014-07-25 19:49:10 +000012128 if (IsOptionMember("date",value) != MagickFalse)
12129 mng_info->ping_exclude_date=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012130
glennrp1a506ac2014-07-25 19:49:10 +000012131 if (IsOptionMember("exif",value) != MagickFalse)
12132 mng_info->ping_exclude_EXIF=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012133
glennrp1a506ac2014-07-25 19:49:10 +000012134 if (IsOptionMember("gama",value) != MagickFalse)
12135 mng_info->ping_exclude_gAMA=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012136
glennrp1a506ac2014-07-25 19:49:10 +000012137 if (IsOptionMember("iccp",value) != MagickFalse)
12138 mng_info->ping_exclude_iCCP=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012139
glennrp689efa22014-07-25 00:00:15 +000012140#if 0
glennrp1a506ac2014-07-25 19:49:10 +000012141 if (IsOptionMember("itxt",value) != MagickFalse)
12142 mng_info->ping_exclude_iTXt=excluding;
glennrp689efa22014-07-25 00:00:15 +000012143#endif
glennrp2cc891a2010-12-24 13:44:32 +000012144
glennrp1a506ac2014-07-25 19:49:10 +000012145 if (IsOptionMember("offs",value) != MagickFalse)
12146 mng_info->ping_exclude_oFFs=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012147
glennrp1a506ac2014-07-25 19:49:10 +000012148 if (IsOptionMember("phys",value) != MagickFalse)
12149 mng_info->ping_exclude_pHYs=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012150
glennrp1a506ac2014-07-25 19:49:10 +000012151 if (IsOptionMember("srgb",value) != MagickFalse)
12152 mng_info->ping_exclude_sRGB=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012153
glennrp1a506ac2014-07-25 19:49:10 +000012154 if (IsOptionMember("text",value) != MagickFalse)
12155 mng_info->ping_exclude_tEXt=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012156
glennrp1a506ac2014-07-25 19:49:10 +000012157 if (IsOptionMember("trns",value) != MagickFalse)
12158 mng_info->ping_exclude_tRNS=excluding;
glennrpa1e3b7b2010-12-24 16:37:33 +000012159
glennrp1a506ac2014-07-25 19:49:10 +000012160 if (IsOptionMember("vpag",value) != MagickFalse)
12161 mng_info->ping_exclude_vpAg=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012162
glennrp1a506ac2014-07-25 19:49:10 +000012163 if (IsOptionMember("zccp",value) != MagickFalse)
12164 mng_info->ping_exclude_zCCP=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012165
glennrp1a506ac2014-07-25 19:49:10 +000012166 if (IsOptionMember("ztxt",value) != MagickFalse)
12167 mng_info->ping_exclude_zTXt=excluding;
glennrp26f37912010-12-23 16:22:42 +000012168 }
12169
glennrp1a506ac2014-07-25 19:49:10 +000012170 if (logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000012171 {
12172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000012173 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000012174 if (mng_info->ping_exclude_bKGD != MagickFalse)
12175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12176 " bKGD");
12177 if (mng_info->ping_exclude_cHRM != MagickFalse)
12178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12179 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000012180 if (mng_info->ping_exclude_date != MagickFalse)
12181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12182 " date");
glennrp26f37912010-12-23 16:22:42 +000012183 if (mng_info->ping_exclude_EXIF != MagickFalse)
12184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12185 " EXIF");
12186 if (mng_info->ping_exclude_gAMA != MagickFalse)
12187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12188 " gAMA");
12189 if (mng_info->ping_exclude_iCCP != MagickFalse)
12190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12191 " iCCP");
glennrp689efa22014-07-25 00:00:15 +000012192#if 0
glennrp26f37912010-12-23 16:22:42 +000012193 if (mng_info->ping_exclude_iTXt != MagickFalse)
12194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12195 " iTXt");
glennrp689efa22014-07-25 00:00:15 +000012196#endif
12197
glennrp26f37912010-12-23 16:22:42 +000012198 if (mng_info->ping_exclude_oFFs != MagickFalse)
12199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12200 " oFFs");
12201 if (mng_info->ping_exclude_pHYs != MagickFalse)
12202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12203 " pHYs");
12204 if (mng_info->ping_exclude_sRGB != MagickFalse)
12205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12206 " sRGB");
12207 if (mng_info->ping_exclude_tEXt != MagickFalse)
12208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12209 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000012210 if (mng_info->ping_exclude_tRNS != MagickFalse)
12211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12212 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000012213 if (mng_info->ping_exclude_vpAg != MagickFalse)
12214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12215 " vpAg");
12216 if (mng_info->ping_exclude_zCCP != MagickFalse)
12217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12218 " zCCP");
12219 if (mng_info->ping_exclude_zTXt != MagickFalse)
12220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12221 " zTXt");
12222 }
12223
glennrpb9cfe272010-12-21 15:08:06 +000012224 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000012225
cristy16ea1392012-03-21 20:38:41 +000012226 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012227
12228 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012229
cristy3ed852e2009-09-05 21:47:34 +000012230 if (logging != MagickFalse)
12231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012232
cristy3ed852e2009-09-05 21:47:34 +000012233 return(status);
12234}
12235
12236#if defined(JNG_SUPPORTED)
12237
12238/* Write one JNG image */
12239static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +000012240 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012241{
12242 Image
12243 *jpeg_image;
12244
12245 ImageInfo
12246 *jpeg_image_info;
12247
12248 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000012249 logging,
cristy3ed852e2009-09-05 21:47:34 +000012250 status;
12251
12252 size_t
12253 length;
12254
12255 unsigned char
12256 *blob,
12257 chunk[80],
12258 *p;
12259
12260 unsigned int
12261 jng_alpha_compression_method,
12262 jng_alpha_sample_depth,
12263 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000012264 transparent;
12265
cristybb503372010-05-27 20:51:26 +000012266 size_t
glennrp59575fa2011-12-31 21:31:39 +000012267 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000012268 jng_quality;
12269
12270 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000012271 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012272
12273 blob=(unsigned char *) NULL;
12274 jpeg_image=(Image *) NULL;
12275 jpeg_image_info=(ImageInfo *) NULL;
12276
12277 status=MagickTrue;
12278 transparent=image_info->type==GrayscaleMatteType ||
cristy8a46d822012-08-28 23:32:39 +000012279 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +000012280
glennrp4b917592013-06-20 19:53:59 +000012281 jng_alpha_sample_depth = 0;
12282
glennrp59575fa2011-12-31 21:31:39 +000012283 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12284
12285 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12286
glennrp750105b2012-04-25 16:20:45 +000012287 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
glennrp59575fa2011-12-31 21:31:39 +000012288 image_info->quality;
12289
12290 if (jng_alpha_quality >= 1000)
12291 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000012292
glennrpd0ee5a22014-03-28 14:28:47 +000012293 length=0;
12294
cristy3ed852e2009-09-05 21:47:34 +000012295 if (transparent)
12296 {
12297 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000012298
cristy3ed852e2009-09-05 21:47:34 +000012299 /* Create JPEG blob, image, and image_info */
12300 if (logging != MagickFalse)
12301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +000012302 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000012303
cristy3ed852e2009-09-05 21:47:34 +000012304 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000012305
cristy3ed852e2009-09-05 21:47:34 +000012306 if (jpeg_image_info == (ImageInfo *) NULL)
12307 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000012308
cristy3ed852e2009-09-05 21:47:34 +000012309 if (logging != MagickFalse)
12310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12311 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000012312
cristy16ea1392012-03-21 20:38:41 +000012313 jpeg_image=SeparateImage(image,AlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +000012314 if (jpeg_image == (Image *) NULL)
12315 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12316 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy8a46d822012-08-28 23:32:39 +000012317 jpeg_image->alpha_trait=UndefinedPixelTrait;
glennrp8f77fdc2012-03-21 15:16:37 +000012318 jpeg_image->quality=jng_alpha_quality;
cristy16ea1392012-03-21 20:38:41 +000012319 jpeg_image_info->type=GrayscaleType;
12320 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000012321 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012322 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000012323 "%s",jpeg_image->filename);
12324 }
glennrp59575fa2011-12-31 21:31:39 +000012325 else
12326 {
12327 jng_alpha_compression_method=0;
12328 jng_color_type=10;
12329 jng_alpha_sample_depth=0;
12330 }
cristy3ed852e2009-09-05 21:47:34 +000012331
12332 /* To do: check bit depth of PNG alpha channel */
12333
12334 /* Check if image is grayscale. */
12335 if (image_info->type != TrueColorMatteType && image_info->type !=
cristy7fb26522012-06-21 13:02:48 +000012336 TrueColorType && IsImageGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000012337 jng_color_type-=2;
12338
glennrp59575fa2011-12-31 21:31:39 +000012339 if (logging != MagickFalse)
12340 {
12341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12342 " JNG Quality = %d",(int) jng_quality);
12343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12344 " JNG Color Type = %d",jng_color_type);
12345 if (transparent)
12346 {
12347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12348 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12350 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12352 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12353 }
12354 }
12355
cristy3ed852e2009-09-05 21:47:34 +000012356 if (transparent)
12357 {
12358 if (jng_alpha_compression_method==0)
12359 {
12360 const char
12361 *value;
12362
cristy16ea1392012-03-21 20:38:41 +000012363 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000012364 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012365 exception);
cristy3ed852e2009-09-05 21:47:34 +000012366 if (logging != MagickFalse)
12367 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12368 " Creating PNG blob.");
cristy3ed852e2009-09-05 21:47:34 +000012369
12370 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12371 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12372 jpeg_image_info->interlace=NoInterlace;
12373
glennrpcc5d45b2012-01-06 04:06:10 +000012374 /* Exclude all ancillary chunks */
12375 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12376
cristy3ed852e2009-09-05 21:47:34 +000012377 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000012378 exception);
cristy3ed852e2009-09-05 21:47:34 +000012379
12380 /* Retrieve sample depth used */
cristy16ea1392012-03-21 20:38:41 +000012381 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000012382 if (value != (char *) NULL)
12383 jng_alpha_sample_depth= (unsigned int) value[0];
12384 }
12385 else
12386 {
cristy16ea1392012-03-21 20:38:41 +000012387 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000012388
12389 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012390 exception);
cristy3ed852e2009-09-05 21:47:34 +000012391
12392 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12393 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12394 jpeg_image_info->interlace=NoInterlace;
12395 if (logging != MagickFalse)
12396 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12397 " Creating blob.");
12398 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000012399 exception);
cristy3ed852e2009-09-05 21:47:34 +000012400 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000012401
cristy3ed852e2009-09-05 21:47:34 +000012402 if (logging != MagickFalse)
12403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012404 " Successfully read jpeg_image into a blob, length=%.20g.",
12405 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012406
12407 }
12408 /* Destroy JPEG image and image_info */
12409 jpeg_image=DestroyImage(jpeg_image);
12410 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12411 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12412 }
12413
12414 /* Write JHDR chunk */
12415 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12416 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000012417 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000012418 PNGLong(chunk+4,(png_uint_32) image->columns);
12419 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012420 chunk[12]=jng_color_type;
12421 chunk[13]=8; /* sample depth */
12422 chunk[14]=8; /*jng_image_compression_method */
12423 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12424 chunk[16]=jng_alpha_sample_depth;
12425 chunk[17]=jng_alpha_compression_method;
12426 chunk[18]=0; /*jng_alpha_filter_method */
12427 chunk[19]=0; /*jng_alpha_interlace_method */
12428 (void) WriteBlob(image,20,chunk);
12429 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12430 if (logging != MagickFalse)
12431 {
12432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000012433 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000012434
cristy3ed852e2009-09-05 21:47:34 +000012435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000012436 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000012437
cristy3ed852e2009-09-05 21:47:34 +000012438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12439 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000012440
cristy3ed852e2009-09-05 21:47:34 +000012441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12442 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000012443
cristy3ed852e2009-09-05 21:47:34 +000012444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12445 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000012446
cristy3ed852e2009-09-05 21:47:34 +000012447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12448 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000012449
cristy3ed852e2009-09-05 21:47:34 +000012450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12451 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000012452
cristy3ed852e2009-09-05 21:47:34 +000012453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12454 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000012455
cristy3ed852e2009-09-05 21:47:34 +000012456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12457 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000012458
cristy3ed852e2009-09-05 21:47:34 +000012459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12460 " JNG alpha interlace:%5d",0);
12461 }
12462
glennrp0fe50b42010-11-16 03:52:51 +000012463 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000012464 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000012465
12466 /*
12467 Write leading ancillary chunks
12468 */
12469
12470 if (transparent)
12471 {
12472 /*
12473 Write JNG bKGD chunk
12474 */
12475
12476 unsigned char
12477 blue,
12478 green,
12479 red;
12480
cristybb503372010-05-27 20:51:26 +000012481 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012482 num_bytes;
12483
12484 if (jng_color_type == 8 || jng_color_type == 12)
12485 num_bytes=6L;
12486 else
12487 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000012488 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012489 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012490 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012491 red=ScaleQuantumToChar(image->background_color.red);
12492 green=ScaleQuantumToChar(image->background_color.green);
12493 blue=ScaleQuantumToChar(image->background_color.blue);
12494 *(chunk+4)=0;
12495 *(chunk+5)=red;
12496 *(chunk+6)=0;
12497 *(chunk+7)=green;
12498 *(chunk+8)=0;
12499 *(chunk+9)=blue;
12500 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12501 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12502 }
12503
12504 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12505 {
12506 /*
12507 Write JNG sRGB chunk
12508 */
12509 (void) WriteBlobMSBULong(image,1L);
12510 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012511 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012512
cristy3ed852e2009-09-05 21:47:34 +000012513 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012514 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012515 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012516 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012517
cristy3ed852e2009-09-05 21:47:34 +000012518 else
glennrpe610a072010-08-05 17:08:46 +000012519 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012520 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012521 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012522
cristy3ed852e2009-09-05 21:47:34 +000012523 (void) WriteBlob(image,5,chunk);
12524 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12525 }
12526 else
12527 {
12528 if (image->gamma != 0.0)
12529 {
12530 /*
12531 Write JNG gAMA chunk
12532 */
12533 (void) WriteBlobMSBULong(image,4L);
12534 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012535 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012536 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012537 (void) WriteBlob(image,8,chunk);
12538 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12539 }
glennrp0fe50b42010-11-16 03:52:51 +000012540
cristy3ed852e2009-09-05 21:47:34 +000012541 if ((mng_info->equal_chrms == MagickFalse) &&
12542 (image->chromaticity.red_primary.x != 0.0))
12543 {
12544 PrimaryInfo
12545 primary;
12546
12547 /*
12548 Write JNG cHRM chunk
12549 */
12550 (void) WriteBlobMSBULong(image,32L);
12551 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012552 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012553 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012554 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12555 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012556 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012557 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12558 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012559 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012560 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12561 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012562 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012563 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12564 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012565 (void) WriteBlob(image,36,chunk);
12566 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12567 }
12568 }
glennrp0fe50b42010-11-16 03:52:51 +000012569
cristy16ea1392012-03-21 20:38:41 +000012570 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012571 {
12572 /*
12573 Write JNG pHYs chunk
12574 */
12575 (void) WriteBlobMSBULong(image,9L);
12576 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012577 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000012578 if (image->units == PixelsPerInchResolution)
12579 {
cristy35ef8242010-06-03 16:24:13 +000012580 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012581 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012582
cristy35ef8242010-06-03 16:24:13 +000012583 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012584 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012585
cristy3ed852e2009-09-05 21:47:34 +000012586 chunk[12]=1;
12587 }
glennrp0fe50b42010-11-16 03:52:51 +000012588
cristy3ed852e2009-09-05 21:47:34 +000012589 else
12590 {
12591 if (image->units == PixelsPerCentimeterResolution)
12592 {
cristy35ef8242010-06-03 16:24:13 +000012593 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012594 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012595
cristy35ef8242010-06-03 16:24:13 +000012596 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012597 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012598
cristy3ed852e2009-09-05 21:47:34 +000012599 chunk[12]=1;
12600 }
glennrp0fe50b42010-11-16 03:52:51 +000012601
cristy3ed852e2009-09-05 21:47:34 +000012602 else
12603 {
cristy16ea1392012-03-21 20:38:41 +000012604 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12605 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012606 chunk[12]=0;
12607 }
12608 }
12609 (void) WriteBlob(image,13,chunk);
12610 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12611 }
12612
12613 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12614 {
12615 /*
12616 Write JNG oFFs chunk
12617 */
12618 (void) WriteBlobMSBULong(image,9L);
12619 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000012620 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000012621 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12622 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000012623 chunk[12]=0;
12624 (void) WriteBlob(image,13,chunk);
12625 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12626 }
12627 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12628 {
12629 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12630 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000012631 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012632 PNGLong(chunk+4,(png_uint_32) image->page.width);
12633 PNGLong(chunk+8,(png_uint_32) image->page.height);
12634 chunk[12]=0; /* unit = pixels */
12635 (void) WriteBlob(image,13,chunk);
12636 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12637 }
12638
12639
12640 if (transparent)
12641 {
12642 if (jng_alpha_compression_method==0)
12643 {
cristybb503372010-05-27 20:51:26 +000012644 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012645 i;
12646
cristyfa6de8c2014-05-18 16:34:44 +000012647 size_t
cristy3ed852e2009-09-05 21:47:34 +000012648 len;
12649
12650 /* Write IDAT chunk header */
12651 if (logging != MagickFalse)
12652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012653 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012654 length);
cristy3ed852e2009-09-05 21:47:34 +000012655
12656 /* Copy IDAT chunks */
12657 len=0;
12658 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012659 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012660 {
cristy5389e5a2014-05-28 14:22:43 +000012661 len=(size_t) (*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
cristy3ed852e2009-09-05 21:47:34 +000012662 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012663
cristy3ed852e2009-09-05 21:47:34 +000012664 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12665 {
12666 /* Found an IDAT chunk. */
cristyfa6de8c2014-05-18 16:34:44 +000012667 (void) WriteBlobMSBULong(image,len);
12668 LogPNGChunk(logging,mng_IDAT,len);
12669 (void) WriteBlob(image,len+4,p);
12670 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
cristy3ed852e2009-09-05 21:47:34 +000012671 }
glennrp0fe50b42010-11-16 03:52:51 +000012672
cristy3ed852e2009-09-05 21:47:34 +000012673 else
12674 {
12675 if (logging != MagickFalse)
12676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012677 " Skipping %c%c%c%c chunk, length=%.20g.",
12678 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012679 }
12680 p+=(8+len);
12681 }
12682 }
12683 else
12684 {
12685 /* Write JDAA chunk header */
12686 if (logging != MagickFalse)
12687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012688 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012689 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012690 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012691 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012692 /* Write JDAT chunk(s) data */
12693 (void) WriteBlob(image,4,chunk);
12694 (void) WriteBlob(image,length,blob);
12695 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12696 (uInt) length));
12697 }
12698 blob=(unsigned char *) RelinquishMagickMemory(blob);
12699 }
12700
12701 /* Encode image as a JPEG blob */
12702 if (logging != MagickFalse)
12703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12704 " Creating jpeg_image_info.");
12705 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12706 if (jpeg_image_info == (ImageInfo *) NULL)
12707 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12708
12709 if (logging != MagickFalse)
12710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12711 " Creating jpeg_image.");
12712
cristy16ea1392012-03-21 20:38:41 +000012713 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012714 if (jpeg_image == (Image *) NULL)
12715 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12716 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12717
12718 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012719 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012720 jpeg_image->filename);
12721
12722 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012723 exception);
cristy3ed852e2009-09-05 21:47:34 +000012724
12725 if (logging != MagickFalse)
12726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012727 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12728 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012729
12730 if (jng_color_type == 8 || jng_color_type == 12)
12731 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012732
glennrp59575fa2011-12-31 21:31:39 +000012733 jpeg_image_info->quality=jng_quality;
12734 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012735 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12736 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012737
cristy3ed852e2009-09-05 21:47:34 +000012738 if (logging != MagickFalse)
12739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12740 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012741
cristy16ea1392012-03-21 20:38:41 +000012742 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012743
cristy3ed852e2009-09-05 21:47:34 +000012744 if (logging != MagickFalse)
12745 {
12746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012747 " Successfully read jpeg_image into a blob, length=%.20g.",
12748 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012749
12750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012751 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012752 }
glennrp0fe50b42010-11-16 03:52:51 +000012753
cristy3ed852e2009-09-05 21:47:34 +000012754 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012755 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012756 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012757 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012758 (void) WriteBlob(image,4,chunk);
12759 (void) WriteBlob(image,length,blob);
12760 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12761
12762 jpeg_image=DestroyImage(jpeg_image);
12763 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12764 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12765 blob=(unsigned char *) RelinquishMagickMemory(blob);
12766
12767 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012768 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012769
12770 /* Write IEND chunk */
12771 (void) WriteBlobMSBULong(image,0L);
12772 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012773 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012774 (void) WriteBlob(image,4,chunk);
12775 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12776
12777 if (logging != MagickFalse)
12778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12779 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012780
cristy3ed852e2009-09-05 21:47:34 +000012781 return(status);
12782}
12783
12784
12785/*
12786%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12787% %
12788% %
12789% %
12790% W r i t e J N G I m a g e %
12791% %
12792% %
12793% %
12794%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12795%
12796% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12797%
12798% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12799%
12800% The format of the WriteJNGImage method is:
12801%
cristy16ea1392012-03-21 20:38:41 +000012802% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12803% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012804%
12805% A description of each parameter follows:
12806%
12807% o image_info: the image info.
12808%
12809% o image: The image.
12810%
cristy16ea1392012-03-21 20:38:41 +000012811% o exception: return any errors or warnings in this structure.
12812%
cristy3ed852e2009-09-05 21:47:34 +000012813%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12814*/
cristy16ea1392012-03-21 20:38:41 +000012815static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12816 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012817{
12818 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012819 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012820 logging,
cristy3ed852e2009-09-05 21:47:34 +000012821 status;
12822
12823 MngInfo
12824 *mng_info;
12825
cristy3ed852e2009-09-05 21:47:34 +000012826 /*
12827 Open image file.
12828 */
12829 assert(image_info != (const ImageInfo *) NULL);
12830 assert(image_info->signature == MagickSignature);
12831 assert(image != (Image *) NULL);
12832 assert(image->signature == MagickSignature);
12833 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012834 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012835 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012836 if (status == MagickFalse)
12837 return(status);
12838
12839 /*
12840 Allocate a MngInfo structure.
12841 */
12842 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012843 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012844 if (mng_info == (MngInfo *) NULL)
12845 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12846 /*
12847 Initialize members of the MngInfo structure.
12848 */
12849 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12850 mng_info->image=image;
12851 have_mng_structure=MagickTrue;
12852
12853 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12854
cristy16ea1392012-03-21 20:38:41 +000012855 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012856 (void) CloseBlob(image);
12857
12858 (void) CatchImageException(image);
12859 MngInfoFreeStruct(mng_info,&have_mng_structure);
12860 if (logging != MagickFalse)
12861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12862 return(status);
12863}
12864#endif
12865
cristy16ea1392012-03-21 20:38:41 +000012866static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12867 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012868{
12869 const char
12870 *option;
12871
12872 Image
12873 *next_image;
12874
12875 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012876 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012877 status;
12878
glennrp03812ae2010-12-24 01:31:34 +000012879 volatile MagickBooleanType
12880 logging;
12881
cristy3ed852e2009-09-05 21:47:34 +000012882 MngInfo
12883 *mng_info;
12884
12885 int
cristy3ed852e2009-09-05 21:47:34 +000012886 image_count,
12887 need_iterations,
12888 need_matte;
12889
12890 volatile int
12891#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12892 defined(PNG_MNG_FEATURES_SUPPORTED)
12893 need_local_plte,
12894#endif
12895 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012896 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012897 use_global_plte;
12898
cristybb503372010-05-27 20:51:26 +000012899 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012900 i;
12901
12902 unsigned char
12903 chunk[800];
12904
12905 volatile unsigned int
12906 write_jng,
12907 write_mng;
12908
cristybb503372010-05-27 20:51:26 +000012909 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012910 scene;
12911
cristybb503372010-05-27 20:51:26 +000012912 size_t
cristy3ed852e2009-09-05 21:47:34 +000012913 final_delay=0,
12914 initial_delay;
12915
glennrpd5045b42010-03-24 12:40:35 +000012916#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012917 if (image_info->verbose)
12918 printf("Your PNG library (libpng-%s) is rather old.\n",
12919 PNG_LIBPNG_VER_STRING);
12920#endif
12921
12922 /*
12923 Open image file.
12924 */
12925 assert(image_info != (const ImageInfo *) NULL);
12926 assert(image_info->signature == MagickSignature);
12927 assert(image != (Image *) NULL);
12928 assert(image->signature == MagickSignature);
12929 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012930 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012931 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012932 if (status == MagickFalse)
12933 return(status);
12934
12935 /*
12936 Allocate a MngInfo structure.
12937 */
12938 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012939 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012940 if (mng_info == (MngInfo *) NULL)
12941 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12942 /*
12943 Initialize members of the MngInfo structure.
12944 */
12945 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12946 mng_info->image=image;
12947 have_mng_structure=MagickTrue;
12948 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12949
12950 /*
12951 * See if user has requested a specific PNG subformat to be used
12952 * for all of the PNGs in the MNG being written, e.g.,
12953 *
12954 * convert *.png png8:animation.mng
12955 *
12956 * To do: check -define png:bit_depth and png:color_type as well,
12957 * or perhaps use mng:bit_depth and mng:color_type instead for
12958 * global settings.
12959 */
12960
12961 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12962 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12963 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12964
12965 write_jng=MagickFalse;
12966 if (image_info->compression == JPEGCompression)
12967 write_jng=MagickTrue;
12968
12969 mng_info->adjoin=image_info->adjoin &&
12970 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12971
cristy3ed852e2009-09-05 21:47:34 +000012972 if (logging != MagickFalse)
12973 {
12974 /* Log some info about the input */
12975 Image
12976 *p;
12977
12978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +000012979 " Checking input image(s)\n"
12980 " Image_info depth: %.20g, Type: %d",
12981 (double) image_info->depth, image_info->type);
cristy3ed852e2009-09-05 21:47:34 +000012982
12983 scene=0;
12984 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12985 {
glennrp0fe50b42010-11-16 03:52:51 +000012986
cristy3ed852e2009-09-05 21:47:34 +000012987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp280283d2014-08-01 15:02:04 +000012988 " Scene: %.20g\n, Image depth: %.20g",
12989 (double) scene++, (double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012990
cristydc2d3272013-02-12 14:00:44 +000012991 if (p->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000012992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12993 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012994
cristy3ed852e2009-09-05 21:47:34 +000012995 else
12996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12997 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012998
cristy3ed852e2009-09-05 21:47:34 +000012999 if (p->storage_class == PseudoClass)
13000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13001 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000013002
cristy3ed852e2009-09-05 21:47:34 +000013003 else
13004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13005 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000013006
cristy3ed852e2009-09-05 21:47:34 +000013007 if (p->colors)
13008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013009 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000013010
cristy3ed852e2009-09-05 21:47:34 +000013011 else
13012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13013 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000013014
cristy3ed852e2009-09-05 21:47:34 +000013015 if (mng_info->adjoin == MagickFalse)
13016 break;
13017 }
13018 }
13019
cristy3ed852e2009-09-05 21:47:34 +000013020 use_global_plte=MagickFalse;
13021 all_images_are_gray=MagickFalse;
13022#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13023 need_local_plte=MagickTrue;
13024#endif
13025 need_defi=MagickFalse;
13026 need_matte=MagickFalse;
13027 mng_info->framing_mode=1;
13028 mng_info->old_framing_mode=1;
13029
13030 if (write_mng)
13031 if (image_info->page != (char *) NULL)
13032 {
13033 /*
13034 Determine image bounding box.
13035 */
13036 SetGeometry(image,&mng_info->page);
13037 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13038 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13039 }
13040 if (write_mng)
13041 {
13042 unsigned int
13043 need_geom;
13044
13045 unsigned short
13046 red,
13047 green,
13048 blue;
13049
13050 mng_info->page=image->page;
13051 need_geom=MagickTrue;
13052 if (mng_info->page.width || mng_info->page.height)
13053 need_geom=MagickFalse;
13054 /*
13055 Check all the scenes.
13056 */
13057 initial_delay=image->delay;
13058 need_iterations=MagickFalse;
13059 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13060 mng_info->equal_physs=MagickTrue,
13061 mng_info->equal_gammas=MagickTrue;
13062 mng_info->equal_srgbs=MagickTrue;
13063 mng_info->equal_backgrounds=MagickTrue;
13064 image_count=0;
13065#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13066 defined(PNG_MNG_FEATURES_SUPPORTED)
13067 all_images_are_gray=MagickTrue;
13068 mng_info->equal_palettes=MagickFalse;
13069 need_local_plte=MagickFalse;
13070#endif
13071 for (next_image=image; next_image != (Image *) NULL; )
13072 {
13073 if (need_geom)
13074 {
13075 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13076 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000013077
cristy3ed852e2009-09-05 21:47:34 +000013078 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13079 mng_info->page.height=next_image->rows+next_image->page.y;
13080 }
glennrp0fe50b42010-11-16 03:52:51 +000013081
cristy3ed852e2009-09-05 21:47:34 +000013082 if (next_image->page.x || next_image->page.y)
13083 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013084
cristydc2d3272013-02-12 14:00:44 +000013085 if (next_image->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000013086 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013087
cristy3ed852e2009-09-05 21:47:34 +000013088 if ((int) next_image->dispose >= BackgroundDispose)
cristydc2d3272013-02-12 14:00:44 +000013089 if ((next_image->alpha_trait == BlendPixelTrait) ||
13090 next_image->page.x || next_image->page.y ||
cristy3ed852e2009-09-05 21:47:34 +000013091 ((next_image->columns < mng_info->page.width) &&
13092 (next_image->rows < mng_info->page.height)))
13093 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013094
cristy3ed852e2009-09-05 21:47:34 +000013095 if (next_image->iterations)
13096 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013097
cristy3ed852e2009-09-05 21:47:34 +000013098 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000013099
cristy3ed852e2009-09-05 21:47:34 +000013100 if (final_delay != initial_delay || final_delay > 1UL*
13101 next_image->ticks_per_second)
13102 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000013103
cristy3ed852e2009-09-05 21:47:34 +000013104#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13105 defined(PNG_MNG_FEATURES_SUPPORTED)
13106 /*
13107 check for global palette possibility.
13108 */
cristy8a46d822012-08-28 23:32:39 +000013109 if (image->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000013110 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013111
cristy3ed852e2009-09-05 21:47:34 +000013112 if (need_local_plte == 0)
13113 {
cristy7fb26522012-06-21 13:02:48 +000013114 if (IsImageGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013115 all_images_are_gray=MagickFalse;
13116 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13117 if (use_global_plte == 0)
13118 use_global_plte=mng_info->equal_palettes;
13119 need_local_plte=!mng_info->equal_palettes;
13120 }
13121#endif
13122 if (GetNextImageInList(next_image) != (Image *) NULL)
13123 {
13124 if (next_image->background_color.red !=
13125 next_image->next->background_color.red ||
13126 next_image->background_color.green !=
13127 next_image->next->background_color.green ||
13128 next_image->background_color.blue !=
13129 next_image->next->background_color.blue)
13130 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013131
cristy3ed852e2009-09-05 21:47:34 +000013132 if (next_image->gamma != next_image->next->gamma)
13133 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013134
cristy3ed852e2009-09-05 21:47:34 +000013135 if (next_image->rendering_intent !=
13136 next_image->next->rendering_intent)
13137 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013138
cristy3ed852e2009-09-05 21:47:34 +000013139 if ((next_image->units != next_image->next->units) ||
cristy16ea1392012-03-21 20:38:41 +000013140 (next_image->resolution.x != next_image->next->resolution.x) ||
13141 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000013142 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013143
cristy3ed852e2009-09-05 21:47:34 +000013144 if (mng_info->equal_chrms)
13145 {
13146 if (next_image->chromaticity.red_primary.x !=
13147 next_image->next->chromaticity.red_primary.x ||
13148 next_image->chromaticity.red_primary.y !=
13149 next_image->next->chromaticity.red_primary.y ||
13150 next_image->chromaticity.green_primary.x !=
13151 next_image->next->chromaticity.green_primary.x ||
13152 next_image->chromaticity.green_primary.y !=
13153 next_image->next->chromaticity.green_primary.y ||
13154 next_image->chromaticity.blue_primary.x !=
13155 next_image->next->chromaticity.blue_primary.x ||
13156 next_image->chromaticity.blue_primary.y !=
13157 next_image->next->chromaticity.blue_primary.y ||
13158 next_image->chromaticity.white_point.x !=
13159 next_image->next->chromaticity.white_point.x ||
13160 next_image->chromaticity.white_point.y !=
13161 next_image->next->chromaticity.white_point.y)
13162 mng_info->equal_chrms=MagickFalse;
13163 }
13164 }
13165 image_count++;
13166 next_image=GetNextImageInList(next_image);
13167 }
13168 if (image_count < 2)
13169 {
13170 mng_info->equal_backgrounds=MagickFalse;
13171 mng_info->equal_chrms=MagickFalse;
13172 mng_info->equal_gammas=MagickFalse;
13173 mng_info->equal_srgbs=MagickFalse;
13174 mng_info->equal_physs=MagickFalse;
13175 use_global_plte=MagickFalse;
13176#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13177 need_local_plte=MagickTrue;
13178#endif
13179 need_iterations=MagickFalse;
13180 }
glennrp0fe50b42010-11-16 03:52:51 +000013181
cristy3ed852e2009-09-05 21:47:34 +000013182 if (mng_info->need_fram == MagickFalse)
13183 {
13184 /*
13185 Only certain framing rates 100/n are exactly representable without
13186 the FRAM chunk but we'll allow some slop in VLC files
13187 */
13188 if (final_delay == 0)
13189 {
13190 if (need_iterations != MagickFalse)
13191 {
13192 /*
13193 It's probably a GIF with loop; don't run it *too* fast.
13194 */
glennrp02617122010-07-28 13:07:35 +000013195 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000013196 {
13197 final_delay=10;
cristy16ea1392012-03-21 20:38:41 +000013198 (void) ThrowMagickException(exception,GetMagickModule(),
13199 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000013200 "input has zero delay between all frames; assuming",
13201 " 10 cs `%s'","");
13202 }
cristy3ed852e2009-09-05 21:47:34 +000013203 }
13204 else
13205 mng_info->ticks_per_second=0;
13206 }
13207 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000013208 mng_info->ticks_per_second=(png_uint_32)
13209 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000013210 if (final_delay > 50)
13211 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000013212
cristy3ed852e2009-09-05 21:47:34 +000013213 if (final_delay > 75)
13214 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000013215
cristy3ed852e2009-09-05 21:47:34 +000013216 if (final_delay > 125)
13217 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013218
cristy3ed852e2009-09-05 21:47:34 +000013219 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13220 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
glennrpd0ee5a22014-03-28 14:28:47 +000013221 (final_delay != 25) && (final_delay != 50) && (1UL*final_delay !=
cristy3ed852e2009-09-05 21:47:34 +000013222 1UL*image->ticks_per_second))
13223 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13224 }
glennrp0fe50b42010-11-16 03:52:51 +000013225
cristy3ed852e2009-09-05 21:47:34 +000013226 if (mng_info->need_fram != MagickFalse)
13227 mng_info->ticks_per_second=1UL*image->ticks_per_second;
13228 /*
13229 If pseudocolor, we should also check to see if all the
13230 palettes are identical and write a global PLTE if they are.
13231 ../glennrp Feb 99.
13232 */
13233 /*
13234 Write the MNG version 1.0 signature and MHDR chunk.
13235 */
13236 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13237 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13238 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000013239 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000013240 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13241 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000013242 PNGLong(chunk+12,mng_info->ticks_per_second);
13243 PNGLong(chunk+16,0L); /* layer count=unknown */
13244 PNGLong(chunk+20,0L); /* frame count=unknown */
13245 PNGLong(chunk+24,0L); /* play time=unknown */
13246 if (write_jng)
13247 {
13248 if (need_matte)
13249 {
13250 if (need_defi || mng_info->need_fram || use_global_plte)
13251 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000013252
cristy3ed852e2009-09-05 21:47:34 +000013253 else
13254 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13255 }
glennrp0fe50b42010-11-16 03:52:51 +000013256
cristy3ed852e2009-09-05 21:47:34 +000013257 else
13258 {
13259 if (need_defi || mng_info->need_fram || use_global_plte)
13260 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000013261
cristy3ed852e2009-09-05 21:47:34 +000013262 else
13263 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13264 }
13265 }
glennrp0fe50b42010-11-16 03:52:51 +000013266
cristy3ed852e2009-09-05 21:47:34 +000013267 else
13268 {
13269 if (need_matte)
13270 {
13271 if (need_defi || mng_info->need_fram || use_global_plte)
13272 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000013273
cristy3ed852e2009-09-05 21:47:34 +000013274 else
13275 PNGLong(chunk+28,9L); /* simplicity=VLC */
13276 }
glennrp0fe50b42010-11-16 03:52:51 +000013277
cristy3ed852e2009-09-05 21:47:34 +000013278 else
13279 {
13280 if (need_defi || mng_info->need_fram || use_global_plte)
13281 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000013282
cristy3ed852e2009-09-05 21:47:34 +000013283 else
13284 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13285 }
13286 }
13287 (void) WriteBlob(image,32,chunk);
13288 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
cristy092ec8d2013-04-26 13:46:22 +000013289 option=GetImageOption(image_info,"mng:need-cacheoff");
cristy3ed852e2009-09-05 21:47:34 +000013290 if (option != (const char *) NULL)
13291 {
13292 size_t
13293 length;
13294
13295 /*
13296 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13297 */
13298 PNGType(chunk,mng_nEED);
13299 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000013300 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000013301 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000013302 length+=4;
13303 (void) WriteBlob(image,length,chunk);
13304 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13305 }
13306 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13307 (GetNextImageInList(image) != (Image *) NULL) &&
13308 (image->iterations != 1))
13309 {
13310 /*
13311 Write MNG TERM chunk
13312 */
13313 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13314 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000013315 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013316 chunk[4]=3; /* repeat animation */
13317 chunk[5]=0; /* show last frame when done */
13318 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13319 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000013320
cristy3ed852e2009-09-05 21:47:34 +000013321 if (image->iterations == 0)
13322 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000013323
cristy3ed852e2009-09-05 21:47:34 +000013324 else
13325 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000013326
cristy3ed852e2009-09-05 21:47:34 +000013327 if (logging != MagickFalse)
13328 {
13329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013330 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13331 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000013332
cristy3ed852e2009-09-05 21:47:34 +000013333 if (image->iterations == 0)
13334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013335 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000013336
cristy3ed852e2009-09-05 21:47:34 +000013337 else
13338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013339 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000013340 }
13341 (void) WriteBlob(image,14,chunk);
13342 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13343 }
13344 /*
13345 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13346 */
13347 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13348 mng_info->equal_srgbs)
13349 {
13350 /*
13351 Write MNG sRGB chunk
13352 */
13353 (void) WriteBlobMSBULong(image,1L);
13354 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000013355 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000013356
cristy3ed852e2009-09-05 21:47:34 +000013357 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000013358 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000013359 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000013360 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000013361
cristy3ed852e2009-09-05 21:47:34 +000013362 else
glennrpe610a072010-08-05 17:08:46 +000013363 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000013364 Magick_RenderingIntent_to_PNG_RenderingIntent(
13365 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000013366
cristy3ed852e2009-09-05 21:47:34 +000013367 (void) WriteBlob(image,5,chunk);
13368 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13369 mng_info->have_write_global_srgb=MagickTrue;
13370 }
glennrp0fe50b42010-11-16 03:52:51 +000013371
cristy3ed852e2009-09-05 21:47:34 +000013372 else
13373 {
13374 if (image->gamma && mng_info->equal_gammas)
13375 {
13376 /*
13377 Write MNG gAMA chunk
13378 */
13379 (void) WriteBlobMSBULong(image,4L);
13380 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000013381 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000013382 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013383 (void) WriteBlob(image,8,chunk);
13384 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13385 mng_info->have_write_global_gama=MagickTrue;
13386 }
13387 if (mng_info->equal_chrms)
13388 {
13389 PrimaryInfo
13390 primary;
13391
13392 /*
13393 Write MNG cHRM chunk
13394 */
13395 (void) WriteBlobMSBULong(image,32L);
13396 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000013397 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000013398 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000013399 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13400 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013401 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000013402 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13403 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013404 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000013405 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13406 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013407 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000013408 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13409 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013410 (void) WriteBlob(image,36,chunk);
13411 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13412 mng_info->have_write_global_chrm=MagickTrue;
13413 }
13414 }
cristy16ea1392012-03-21 20:38:41 +000013415 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000013416 {
13417 /*
13418 Write MNG pHYs chunk
13419 */
13420 (void) WriteBlobMSBULong(image,9L);
13421 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000013422 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000013423
cristy3ed852e2009-09-05 21:47:34 +000013424 if (image->units == PixelsPerInchResolution)
13425 {
cristy35ef8242010-06-03 16:24:13 +000013426 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013427 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013428
cristy35ef8242010-06-03 16:24:13 +000013429 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013430 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013431
cristy3ed852e2009-09-05 21:47:34 +000013432 chunk[12]=1;
13433 }
glennrp0fe50b42010-11-16 03:52:51 +000013434
cristy3ed852e2009-09-05 21:47:34 +000013435 else
13436 {
13437 if (image->units == PixelsPerCentimeterResolution)
13438 {
cristy35ef8242010-06-03 16:24:13 +000013439 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013440 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013441
cristy35ef8242010-06-03 16:24:13 +000013442 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013443 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013444
cristy3ed852e2009-09-05 21:47:34 +000013445 chunk[12]=1;
13446 }
glennrp0fe50b42010-11-16 03:52:51 +000013447
cristy3ed852e2009-09-05 21:47:34 +000013448 else
13449 {
cristy16ea1392012-03-21 20:38:41 +000013450 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13451 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013452 chunk[12]=0;
13453 }
13454 }
13455 (void) WriteBlob(image,13,chunk);
13456 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13457 }
13458 /*
13459 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13460 or does not cover the entire frame.
13461 */
cristydc2d3272013-02-12 14:00:44 +000013462 if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13463 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
cristy3ed852e2009-09-05 21:47:34 +000013464 (image->page.width+image->page.x < mng_info->page.width))
13465 || (image->page.height && (image->page.height+image->page.y
13466 < mng_info->page.height))))
13467 {
13468 (void) WriteBlobMSBULong(image,6L);
13469 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000013470 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000013471 red=ScaleQuantumToShort(image->background_color.red);
13472 green=ScaleQuantumToShort(image->background_color.green);
13473 blue=ScaleQuantumToShort(image->background_color.blue);
13474 PNGShort(chunk+4,red);
13475 PNGShort(chunk+6,green);
13476 PNGShort(chunk+8,blue);
13477 (void) WriteBlob(image,10,chunk);
13478 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13479 if (mng_info->equal_backgrounds)
13480 {
13481 (void) WriteBlobMSBULong(image,6L);
13482 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000013483 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000013484 (void) WriteBlob(image,10,chunk);
13485 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13486 }
13487 }
13488
13489#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13490 if ((need_local_plte == MagickFalse) &&
13491 (image->storage_class == PseudoClass) &&
13492 (all_images_are_gray == MagickFalse))
13493 {
cristybb503372010-05-27 20:51:26 +000013494 size_t
cristy3ed852e2009-09-05 21:47:34 +000013495 data_length;
13496
13497 /*
13498 Write MNG PLTE chunk
13499 */
13500 data_length=3*image->colors;
13501 (void) WriteBlobMSBULong(image,data_length);
13502 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013503 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013504
cristybb503372010-05-27 20:51:26 +000013505 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013506 {
cristy16ea1392012-03-21 20:38:41 +000013507 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13508 image->colormap[i].red) & 0xff);
13509 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13510 image->colormap[i].green) & 0xff);
13511 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13512 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000013513 }
glennrp0fe50b42010-11-16 03:52:51 +000013514
cristy3ed852e2009-09-05 21:47:34 +000013515 (void) WriteBlob(image,data_length+4,chunk);
13516 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13517 mng_info->have_write_global_plte=MagickTrue;
13518 }
13519#endif
13520 }
13521 scene=0;
13522 mng_info->delay=0;
13523#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13524 defined(PNG_MNG_FEATURES_SUPPORTED)
13525 mng_info->equal_palettes=MagickFalse;
13526#endif
13527 do
13528 {
13529 if (mng_info->adjoin)
13530 {
13531#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13532 defined(PNG_MNG_FEATURES_SUPPORTED)
13533 /*
13534 If we aren't using a global palette for the entire MNG, check to
13535 see if we can use one for two or more consecutive images.
13536 */
13537 if (need_local_plte && use_global_plte && !all_images_are_gray)
13538 {
13539 if (mng_info->IsPalette)
13540 {
13541 /*
13542 When equal_palettes is true, this image has the same palette
13543 as the previous PseudoClass image
13544 */
13545 mng_info->have_write_global_plte=mng_info->equal_palettes;
13546 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13547 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13548 {
13549 /*
13550 Write MNG PLTE chunk
13551 */
cristybb503372010-05-27 20:51:26 +000013552 size_t
cristy3ed852e2009-09-05 21:47:34 +000013553 data_length;
13554
13555 data_length=3*image->colors;
13556 (void) WriteBlobMSBULong(image,data_length);
13557 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013558 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013559
cristybb503372010-05-27 20:51:26 +000013560 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013561 {
13562 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13563 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13564 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13565 }
glennrp0fe50b42010-11-16 03:52:51 +000013566
cristy3ed852e2009-09-05 21:47:34 +000013567 (void) WriteBlob(image,data_length+4,chunk);
13568 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13569 (uInt) (data_length+4)));
13570 mng_info->have_write_global_plte=MagickTrue;
13571 }
13572 }
13573 else
13574 mng_info->have_write_global_plte=MagickFalse;
13575 }
13576#endif
13577 if (need_defi)
13578 {
cristybb503372010-05-27 20:51:26 +000013579 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000013580 previous_x,
13581 previous_y;
13582
13583 if (scene)
13584 {
13585 previous_x=mng_info->page.x;
13586 previous_y=mng_info->page.y;
13587 }
13588 else
13589 {
13590 previous_x=0;
13591 previous_y=0;
13592 }
13593 mng_info->page=image->page;
13594 if ((mng_info->page.x != previous_x) ||
13595 (mng_info->page.y != previous_y))
13596 {
13597 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13598 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000013599 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000013600 chunk[4]=0; /* object 0 MSB */
13601 chunk[5]=0; /* object 0 LSB */
13602 chunk[6]=0; /* visible */
13603 chunk[7]=0; /* abstract */
13604 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13605 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13606 (void) WriteBlob(image,16,chunk);
13607 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13608 }
13609 }
13610 }
13611
13612 mng_info->write_mng=write_mng;
13613
13614 if ((int) image->dispose >= 3)
13615 mng_info->framing_mode=3;
13616
13617 if (mng_info->need_fram && mng_info->adjoin &&
13618 ((image->delay != mng_info->delay) ||
13619 (mng_info->framing_mode != mng_info->old_framing_mode)))
13620 {
13621 if (image->delay == mng_info->delay)
13622 {
13623 /*
13624 Write a MNG FRAM chunk with the new framing mode.
13625 */
13626 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13627 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013628 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013629 chunk[4]=(unsigned char) mng_info->framing_mode;
13630 (void) WriteBlob(image,5,chunk);
13631 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13632 }
13633 else
13634 {
13635 /*
13636 Write a MNG FRAM chunk with the delay.
13637 */
13638 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13639 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013640 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013641 chunk[4]=(unsigned char) mng_info->framing_mode;
13642 chunk[5]=0; /* frame name separator (no name) */
13643 chunk[6]=2; /* flag for changing default delay */
13644 chunk[7]=0; /* flag for changing frame timeout */
13645 chunk[8]=0; /* flag for changing frame clipping */
13646 chunk[9]=0; /* flag for changing frame sync_id */
13647 PNGLong(chunk+10,(png_uint_32)
13648 ((mng_info->ticks_per_second*
13649 image->delay)/MagickMax(image->ticks_per_second,1)));
13650 (void) WriteBlob(image,14,chunk);
13651 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013652 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013653 }
13654 mng_info->old_framing_mode=mng_info->framing_mode;
13655 }
13656
13657#if defined(JNG_SUPPORTED)
13658 if (image_info->compression == JPEGCompression)
13659 {
13660 ImageInfo
13661 *write_info;
13662
13663 if (logging != MagickFalse)
13664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13665 " Writing JNG object.");
13666 /* To do: specify the desired alpha compression method. */
13667 write_info=CloneImageInfo(image_info);
13668 write_info->compression=UndefinedCompression;
cristy16ea1392012-03-21 20:38:41 +000013669 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013670 write_info=DestroyImageInfo(write_info);
13671 }
13672 else
13673#endif
13674 {
13675 if (logging != MagickFalse)
13676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13677 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013678
glennrpb9cfe272010-12-21 15:08:06 +000013679 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013680 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013681
13682 /* We don't want any ancillary chunks written */
13683 mng_info->ping_exclude_bKGD=MagickTrue;
13684 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013685 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013686 mng_info->ping_exclude_EXIF=MagickTrue;
13687 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013688 mng_info->ping_exclude_iCCP=MagickTrue;
13689 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13690 mng_info->ping_exclude_oFFs=MagickTrue;
13691 mng_info->ping_exclude_pHYs=MagickTrue;
13692 mng_info->ping_exclude_sRGB=MagickTrue;
13693 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013694 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013695 mng_info->ping_exclude_vpAg=MagickTrue;
13696 mng_info->ping_exclude_zCCP=MagickTrue;
13697 mng_info->ping_exclude_zTXt=MagickTrue;
13698
cristy16ea1392012-03-21 20:38:41 +000013699 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013700 }
13701
13702 if (status == MagickFalse)
13703 {
13704 MngInfoFreeStruct(mng_info,&have_mng_structure);
13705 (void) CloseBlob(image);
13706 return(MagickFalse);
13707 }
13708 (void) CatchImageException(image);
13709 if (GetNextImageInList(image) == (Image *) NULL)
13710 break;
13711 image=SyncNextImageInList(image);
13712 status=SetImageProgress(image,SaveImagesTag,scene++,
13713 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013714
cristy3ed852e2009-09-05 21:47:34 +000013715 if (status == MagickFalse)
13716 break;
glennrp0fe50b42010-11-16 03:52:51 +000013717
cristy3ed852e2009-09-05 21:47:34 +000013718 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013719
cristy3ed852e2009-09-05 21:47:34 +000013720 if (write_mng)
13721 {
13722 while (GetPreviousImageInList(image) != (Image *) NULL)
13723 image=GetPreviousImageInList(image);
13724 /*
13725 Write the MEND chunk.
13726 */
13727 (void) WriteBlobMSBULong(image,0x00000000L);
13728 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013729 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013730 (void) WriteBlob(image,4,chunk);
13731 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13732 }
13733 /*
13734 Relinquish resources.
13735 */
13736 (void) CloseBlob(image);
13737 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013738
cristy3ed852e2009-09-05 21:47:34 +000013739 if (logging != MagickFalse)
13740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013741
cristy3ed852e2009-09-05 21:47:34 +000013742 return(MagickTrue);
13743}
glennrpd5045b42010-03-24 12:40:35 +000013744#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013745
cristy3ed852e2009-09-05 21:47:34 +000013746static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13747{
glennrp3bd393f2011-12-21 18:54:53 +000013748 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013749 printf("Your PNG library is too old: You have libpng-%s\n",
13750 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013751
cristy3ed852e2009-09-05 21:47:34 +000013752 ThrowBinaryException(CoderError,"PNG library is too old",
13753 image_info->filename);
13754}
glennrp39992b42010-11-14 00:03:43 +000013755
cristy3ed852e2009-09-05 21:47:34 +000013756static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13757{
13758 return(WritePNGImage(image_info,image));
13759}
glennrpd5045b42010-03-24 12:40:35 +000013760#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013761#endif