blob: 951ad80dd43a0685245f542c784222ffae67404f [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% %
Cristy7ce65e72015-12-12 18:03:16 -050021% Copyright 1999-2016 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[] =
glennrp6647b972015-01-12 04:39:14 +0000141{
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
cristy3ed852e2009-09-05 21:47:34 +0000464/*
465 Establish thread safety.
466 setjmp/longjmp is claimed to be safe on these platforms:
467 setjmp/longjmp is alleged to be unsafe on these platforms:
468*/
glennrp868fff32014-03-16 22:09:06 +0000469#ifdef PNG_SETJMP_SUPPORTED
470# ifndef IMPNG_SETJMP_IS_THREAD_SAFE
471# define IMPNG_SETJMP_NOT_THREAD_SAFE
472# endif
cristy3ed852e2009-09-05 21:47:34 +0000473
glennrp868fff32014-03-16 22:09:06 +0000474# ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
cristy3ed852e2009-09-05 21:47:34 +0000475static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000476 *ping_semaphore = (SemaphoreInfo *) NULL;
glennrp868fff32014-03-16 22:09:06 +0000477# endif
cristy3ed852e2009-09-05 21:47:34 +0000478#endif
479
480/*
481 This temporary until I set up malloc'ed object attributes array.
482 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
483 waste more memory.
484*/
485#define MNG_MAX_OBJECTS 256
486
487/*
488 If this not defined, spec is interpreted strictly. If it is
489 defined, an attempt will be made to recover from some errors,
490 including
491 o global PLTE too short
492*/
493#undef MNG_LOOSE
494
495/*
496 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
497 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
498 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
499 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
500 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
501 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
502 will be enabled by default in libpng-1.2.0.
503*/
cristy3ed852e2009-09-05 21:47:34 +0000504#ifdef PNG_MNG_FEATURES_SUPPORTED
505# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
506# define PNG_READ_EMPTY_PLTE_SUPPORTED
507# endif
508# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
509# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
510# endif
511#endif
512
513/*
cristybb503372010-05-27 20:51:26 +0000514 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000515 This macro is only defined in libpng-1.0.3 and later.
516 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
517*/
518#ifndef PNG_UINT_31_MAX
519#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
520#endif
521
522/*
523 Constant strings for known chunk types. If you need to add a chunk,
524 add a string holding the name here. To make the code more
525 portable, we use ASCII numbers like this, not characters.
526*/
527
Cristy629f8052015-09-14 07:48:08 -0400528static const png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
529static const png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
530static const png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
531static const png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
532static const png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
533static const png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
534static const png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
535static const png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
536static const png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
537static const png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
538static const png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
539static const png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
540static const png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
541static const png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
542static const png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
543static const png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
544static const png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
545static const png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
546static const png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
547static const png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
548static const png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
549static const png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
550static const png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
551static const png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
552static const png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
553static const png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
554static const png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
555static const png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
556static const png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
557static const png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
558static const png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
559static const png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
560static const png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
561static const png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000562
563#if defined(JNG_SUPPORTED)
Cristy629f8052015-09-14 07:48:08 -0400564static const png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
565static const png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
566static const png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
567static const png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
568static const png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
569static const png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000570#endif
571
glennrp689efa22014-07-25 00:00:15 +0000572#if 0
573/* Other known chunks that are not yet supported by ImageMagick: */
Cristy629f8052015-09-14 07:48:08 -0400574static const png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
575static const png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
576static const png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
577static const png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
578static const png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
579static const png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
580static const png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
glennrp689efa22014-07-25 00:00:15 +0000581#endif
cristy3ed852e2009-09-05 21:47:34 +0000582
583typedef struct _MngBox
584{
cristy8182b072010-05-30 20:10:53 +0000585 long
cristy3ed852e2009-09-05 21:47:34 +0000586 left,
587 right,
588 top,
589 bottom;
590} MngBox;
591
592typedef struct _MngPair
593{
cristy8182b072010-05-30 20:10:53 +0000594 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000595 a,
596 b;
597} MngPair;
598
599#ifdef MNG_OBJECT_BUFFERS
600typedef struct _MngBuffer
601{
602
cristybb503372010-05-27 20:51:26 +0000603 size_t
cristy3ed852e2009-09-05 21:47:34 +0000604 height,
605 width;
606
607 Image
608 *image;
609
610 png_color
611 plte[256];
612
613 int
614 reference_count;
615
616 unsigned char
617 alpha_sample_depth,
618 compression_method,
619 color_type,
620 concrete,
621 filter_method,
622 frozen,
623 image_type,
624 interlace_method,
625 pixel_sample_depth,
626 plte_length,
627 sample_depth,
628 viewable;
629} MngBuffer;
630#endif
631
632typedef struct _MngInfo
633{
634
635#ifdef MNG_OBJECT_BUFFERS
636 MngBuffer
637 *ob[MNG_MAX_OBJECTS];
638#endif
639
640 Image *
641 image;
642
643 RectangleInfo
644 page;
645
646 int
647 adjoin,
648#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
649 bytes_in_read_buffer,
650 found_empty_plte,
651#endif
652 equal_backgrounds,
653 equal_chrms,
654 equal_gammas,
655#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
656 defined(PNG_MNG_FEATURES_SUPPORTED)
657 equal_palettes,
658#endif
659 equal_physs,
660 equal_srgbs,
661 framing_mode,
662 have_global_bkgd,
663 have_global_chrm,
664 have_global_gama,
665 have_global_phys,
666 have_global_sbit,
667 have_global_srgb,
668 have_saved_bkgd_index,
669 have_write_global_chrm,
670 have_write_global_gama,
671 have_write_global_plte,
672 have_write_global_srgb,
673 need_fram,
674 object_id,
675 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000676 saved_bkgd_index;
677
678 int
679 new_number_colors;
680
cristybb503372010-05-27 20:51:26 +0000681 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000682 image_found,
683 loop_count[256],
684 loop_iteration[256],
685 scenes_found,
686 x_off[MNG_MAX_OBJECTS],
687 y_off[MNG_MAX_OBJECTS];
688
689 MngBox
690 clip,
691 frame,
692 image_box,
693 object_clip[MNG_MAX_OBJECTS];
694
695 unsigned char
696 /* These flags could be combined into one byte */
697 exists[MNG_MAX_OBJECTS],
698 frozen[MNG_MAX_OBJECTS],
699 loop_active[256],
700 invisible[MNG_MAX_OBJECTS],
701 viewable[MNG_MAX_OBJECTS];
702
703 MagickOffsetType
704 loop_jump[256];
705
706 png_colorp
707 global_plte;
708
709 png_color_8
710 global_sbit;
711
712 png_byte
713#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
714 read_buffer[8],
715#endif
716 global_trns[256];
717
718 float
719 global_gamma;
720
721 ChromaticityInfo
722 global_chrm;
723
724 RenderingIntent
725 global_srgb_intent;
726
cristy35ef8242010-06-03 16:24:13 +0000727 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000728 delay,
729 global_plte_length,
730 global_trns_length,
731 global_x_pixels_per_unit,
732 global_y_pixels_per_unit,
733 mng_width,
734 mng_height,
735 ticks_per_second;
736
glennrpb9cfe272010-12-21 15:08:06 +0000737 MagickBooleanType
738 need_blob;
739
cristy3ed852e2009-09-05 21:47:34 +0000740 unsigned int
741 IsPalette,
742 global_phys_unit_type,
743 basi_warning,
744 clon_warning,
745 dhdr_warning,
746 jhdr_warning,
747 magn_warning,
748 past_warning,
749 phyg_warning,
750 phys_warning,
751 sbit_warning,
752 show_warning,
753 mng_type,
754 write_mng,
755 write_png_colortype,
756 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000757 write_png_compression_level,
758 write_png_compression_strategy,
759 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000760 write_png8,
761 write_png24,
glennrpfd164d22013-01-26 21:10:22 +0000762 write_png32,
763 write_png48,
764 write_png64;
cristy3ed852e2009-09-05 21:47:34 +0000765
766#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000767 size_t
cristy3ed852e2009-09-05 21:47:34 +0000768 basi_width,
769 basi_height;
770
771 unsigned int
772 basi_depth,
773 basi_color_type,
774 basi_compression_method,
775 basi_filter_type,
776 basi_interlace_method,
777 basi_red,
778 basi_green,
779 basi_blue,
780 basi_alpha,
781 basi_viewable;
782#endif
783
784 png_uint_16
785 magn_first,
786 magn_last,
787 magn_mb,
788 magn_ml,
789 magn_mr,
790 magn_mt,
791 magn_mx,
792 magn_my,
793 magn_methx,
794 magn_methy;
795
cristy16ea1392012-03-21 20:38:41 +0000796 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000797 mng_global_bkgd;
798
glennrp26f37912010-12-23 16:22:42 +0000799 /* Added at version 6.6.6-7 */
800 MagickBooleanType
801 ping_exclude_bKGD,
802 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000803 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000804 ping_exclude_EXIF,
805 ping_exclude_gAMA,
806 ping_exclude_iCCP,
807 /* ping_exclude_iTXt, */
808 ping_exclude_oFFs,
809 ping_exclude_pHYs,
810 ping_exclude_sRGB,
811 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000812 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000813 ping_exclude_vpAg,
814 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000815 ping_exclude_zTXt,
glennrpecab7d72013-05-14 22:50:32 +0000816 ping_preserve_colormap,
817 /* Added at version 6.8.5-7 */
dirkfd6fd072014-10-24 21:10:08 +0000818 ping_preserve_iCCP,
819 /* Added at version 6.8.9-9 */
820 ping_exclude_tIME;
glennrp26f37912010-12-23 16:22:42 +0000821
cristy3ed852e2009-09-05 21:47:34 +0000822} MngInfo;
823#endif /* VER */
824
825/*
826 Forward declarations.
827*/
828static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000829 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000830
cristy3ed852e2009-09-05 21:47:34 +0000831static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000832 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000833
cristy3ed852e2009-09-05 21:47:34 +0000834#if defined(JNG_SUPPORTED)
835static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000836 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000837#endif
838
glennrp0c3e06b2010-11-19 13:45:02 +0000839#if PNG_LIBPNG_VER > 10011
840
glennrpfd05d622011-02-25 04:10:33 +0000841
glennrp0c3e06b2010-11-19 13:45:02 +0000842#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
843static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000844LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
glennrp0c3e06b2010-11-19 13:45:02 +0000845{
glennrp67b9c1a2011-04-22 18:47:36 +0000846 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
847 *
848 * This is true if the high byte and the next highest byte of
849 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000850 * are equal to each other. We check this by seeing if the samples
851 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000852 *
853 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000854 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000855 */
856
glennrp3faa9a32011-04-23 14:00:25 +0000857#define QuantumToCharToQuantumEqQuantum(quantum) \
858 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
859
glennrp0c3e06b2010-11-19 13:45:02 +0000860 MagickBooleanType
861 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000862
glennrp03e11f62011-04-22 13:30:16 +0000863 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000864 {
865
cristy16ea1392012-03-21 20:38:41 +0000866 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000867 *p;
868
869 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000870 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
871 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
872 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
873 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000874
875 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
876 {
877 int indx;
878
879 for (indx=0; indx < (ssize_t) image->colors; indx++)
880 {
glennrp3faa9a32011-04-23 14:00:25 +0000881 ok_to_reduce=(
882 QuantumToCharToQuantumEqQuantum(
883 image->colormap[indx].red) &&
884 QuantumToCharToQuantumEqQuantum(
885 image->colormap[indx].green) &&
886 QuantumToCharToQuantumEqQuantum(
887 image->colormap[indx].blue)) ?
888 MagickTrue : MagickFalse;
889
glennrp0c3e06b2010-11-19 13:45:02 +0000890 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000891 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000892 }
893 }
894
895 if ((ok_to_reduce != MagickFalse) &&
896 (image->storage_class != PseudoClass))
897 {
898 ssize_t
899 y;
900
901 register ssize_t
902 x;
903
904 for (y=0; y < (ssize_t) image->rows; y++)
905 {
cristy16ea1392012-03-21 20:38:41 +0000906 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0c3e06b2010-11-19 13:45:02 +0000907
cristy16ea1392012-03-21 20:38:41 +0000908 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +0000909 {
910 ok_to_reduce = MagickFalse;
911 break;
912 }
913
914 for (x=(ssize_t) image->columns-1; x >= 0; x--)
915 {
glennrp3faa9a32011-04-23 14:00:25 +0000916 ok_to_reduce=
cristy16ea1392012-03-21 20:38:41 +0000917 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
918 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
919 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +0000920 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000921
922 if (ok_to_reduce == MagickFalse)
923 break;
924
cristy16ea1392012-03-21 20:38:41 +0000925 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +0000926 }
glennrp8640fb52010-11-23 15:48:26 +0000927 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +0000928 break;
929 }
930 }
931
932 if (ok_to_reduce != MagickFalse)
933 {
glennrp0c3e06b2010-11-19 13:45:02 +0000934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000935 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +0000936 }
glennrpa6a06632011-01-19 15:15:34 +0000937 else
938 {
939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +0000940 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +0000941 }
glennrp0c3e06b2010-11-19 13:45:02 +0000942 }
943
944 return ok_to_reduce;
945}
946#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
947
glennrp1a56e9c2012-04-25 17:06:57 +0000948static const char* PngColorTypeToString(const unsigned int color_type)
949{
950 const char
951 *result = "Unknown";
952
953 switch (color_type)
954 {
955 case PNG_COLOR_TYPE_GRAY:
956 result = "Gray";
957 break;
958 case PNG_COLOR_TYPE_GRAY_ALPHA:
959 result = "Gray+Alpha";
960 break;
961 case PNG_COLOR_TYPE_PALETTE:
962 result = "Palette";
963 break;
964 case PNG_COLOR_TYPE_RGB:
965 result = "RGB";
966 break;
967 case PNG_COLOR_TYPE_RGB_ALPHA:
968 result = "RGB+Alpha";
969 break;
970 }
971
972 return result;
973}
974
glennrpe610a072010-08-05 17:08:46 +0000975static int
glennrpcf002022011-01-30 02:38:15 +0000976Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +0000977{
glennrpe610a072010-08-05 17:08:46 +0000978 switch (intent)
979 {
980 case PerceptualIntent:
981 return 0;
glennrp0fe50b42010-11-16 03:52:51 +0000982
glennrpe610a072010-08-05 17:08:46 +0000983 case RelativeIntent:
984 return 1;
glennrp0fe50b42010-11-16 03:52:51 +0000985
glennrpe610a072010-08-05 17:08:46 +0000986 case SaturationIntent:
987 return 2;
glennrp0fe50b42010-11-16 03:52:51 +0000988
glennrpe610a072010-08-05 17:08:46 +0000989 case AbsoluteIntent:
990 return 3;
glennrp0fe50b42010-11-16 03:52:51 +0000991
glennrpe610a072010-08-05 17:08:46 +0000992 default:
993 return -1;
994 }
995}
996
997static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +0000998Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +0000999{
glennrpcf002022011-01-30 02:38:15 +00001000 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001001 {
1002 case 0:
1003 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001004
glennrpe610a072010-08-05 17:08:46 +00001005 case 1:
1006 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001007
glennrpe610a072010-08-05 17:08:46 +00001008 case 2:
1009 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001010
glennrpe610a072010-08-05 17:08:46 +00001011 case 3:
1012 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001013
glennrpe610a072010-08-05 17:08:46 +00001014 default:
1015 return UndefinedIntent;
1016 }
1017}
1018
cristy9d8c1222012-08-10 12:34:19 +00001019static const char *
glennrp98b83d42012-07-23 02:50:31 +00001020Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1021{
1022 switch (ping_intent)
1023 {
1024 case 0:
1025 return "Perceptual Intent";
1026
1027 case 1:
1028 return "Relative Intent";
1029
1030 case 2:
1031 return "Saturation Intent";
1032
1033 case 3:
1034 return "Absolute Intent";
1035
1036 default:
1037 return "Undefined Intent";
1038 }
1039}
1040
cristyd9ecd042012-06-17 18:26:12 +00001041static const char *
glennrp5dff4352012-06-06 22:12:04 +00001042Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1043{
1044 switch (ping_colortype)
1045 {
1046 case 0:
1047 return "Grayscale";
1048
1049 case 2:
1050 return "Truecolor";
1051
1052 case 3:
1053 return "Indexed";
1054
1055 case 4:
1056 return "GrayAlpha";
1057
1058 case 6:
1059 return "RGBA";
1060
1061 default:
1062 return "UndefinedColorType";
1063 }
1064}
1065
glennrpd5045b42010-03-24 12:40:35 +00001066#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001067#endif /* MAGICKCORE_PNG_DELEGATE */
1068
1069/*
1070%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1071% %
1072% %
1073% %
1074% I s M N G %
1075% %
1076% %
1077% %
1078%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1079%
1080% IsMNG() returns MagickTrue if the image format type, identified by the
1081% magick string, is MNG.
1082%
1083% The format of the IsMNG method is:
1084%
1085% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1086%
1087% A description of each parameter follows:
1088%
1089% o magick: compare image format pattern against these bytes.
1090%
1091% o length: Specifies the length of the magick string.
1092%
1093%
1094*/
1095static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1096{
1097 if (length < 8)
1098 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001099
cristy3ed852e2009-09-05 21:47:34 +00001100 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1101 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001102
cristy3ed852e2009-09-05 21:47:34 +00001103 return(MagickFalse);
1104}
1105
1106/*
1107%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1108% %
1109% %
1110% %
1111% I s J N G %
1112% %
1113% %
1114% %
1115%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1116%
1117% IsJNG() returns MagickTrue if the image format type, identified by the
1118% magick string, is JNG.
1119%
1120% The format of the IsJNG method is:
1121%
1122% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1123%
1124% A description of each parameter follows:
1125%
1126% o magick: compare image format pattern against these bytes.
1127%
1128% o length: Specifies the length of the magick string.
1129%
1130%
1131*/
1132static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1133{
1134 if (length < 8)
1135 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001136
cristy3ed852e2009-09-05 21:47:34 +00001137 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1138 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001139
cristy3ed852e2009-09-05 21:47:34 +00001140 return(MagickFalse);
1141}
1142
1143/*
1144%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1145% %
1146% %
1147% %
1148% I s P N G %
1149% %
1150% %
1151% %
1152%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1153%
1154% IsPNG() returns MagickTrue if the image format type, identified by the
1155% magick string, is PNG.
1156%
1157% The format of the IsPNG method is:
1158%
1159% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1160%
1161% A description of each parameter follows:
1162%
1163% o magick: compare image format pattern against these bytes.
1164%
1165% o length: Specifies the length of the magick string.
1166%
1167*/
1168static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1169{
1170 if (length < 8)
1171 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001172
cristy3ed852e2009-09-05 21:47:34 +00001173 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1174 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001175
cristy3ed852e2009-09-05 21:47:34 +00001176 return(MagickFalse);
1177}
1178
1179#if defined(MAGICKCORE_PNG_DELEGATE)
1180#if defined(__cplusplus) || defined(c_plusplus)
1181extern "C" {
1182#endif
1183
glennrpd5045b42010-03-24 12:40:35 +00001184#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001185static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001186{
1187 unsigned char
1188 buffer[4];
1189
1190 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001191 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00001192 buffer[0]=(unsigned char) (value >> 24);
1193 buffer[1]=(unsigned char) (value >> 16);
1194 buffer[2]=(unsigned char) (value >> 8);
1195 buffer[3]=(unsigned char) value;
1196 return((size_t) WriteBlob(image,4,buffer));
1197}
1198
1199static void PNGLong(png_bytep p,png_uint_32 value)
1200{
1201 *p++=(png_byte) ((value >> 24) & 0xff);
1202 *p++=(png_byte) ((value >> 16) & 0xff);
1203 *p++=(png_byte) ((value >> 8) & 0xff);
1204 *p++=(png_byte) (value & 0xff);
1205}
1206
glennrpa521b2f2010-10-29 04:11:03 +00001207#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001208static void PNGsLong(png_bytep p,png_int_32 value)
1209{
1210 *p++=(png_byte) ((value >> 24) & 0xff);
1211 *p++=(png_byte) ((value >> 16) & 0xff);
1212 *p++=(png_byte) ((value >> 8) & 0xff);
1213 *p++=(png_byte) (value & 0xff);
1214}
glennrpa521b2f2010-10-29 04:11:03 +00001215#endif
cristy3ed852e2009-09-05 21:47:34 +00001216
1217static void PNGShort(png_bytep p,png_uint_16 value)
1218{
1219 *p++=(png_byte) ((value >> 8) & 0xff);
1220 *p++=(png_byte) (value & 0xff);
1221}
1222
Cristy120b5812015-09-14 17:33:11 -04001223static void PNGType(png_bytep p,const png_byte *type)
cristy3ed852e2009-09-05 21:47:34 +00001224{
1225 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1226}
1227
Cristy120b5812015-09-14 17:33:11 -04001228static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
glennrp03812ae2010-12-24 01:31:34 +00001229 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001230{
1231 if (logging != MagickFalse)
1232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001233 " Writing %c%c%c%c chunk, length: %.20g",
1234 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001235}
glennrpd5045b42010-03-24 12:40:35 +00001236#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001237
1238#if defined(__cplusplus) || defined(c_plusplus)
1239}
1240#endif
1241
glennrpd5045b42010-03-24 12:40:35 +00001242#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001243/*
1244%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245% %
1246% %
1247% %
1248% R e a d P N G I m a g e %
1249% %
1250% %
1251% %
1252%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253%
1254% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1255% Multiple-image Network Graphics (MNG) image file and returns it. It
1256% allocates the memory necessary for the new Image structure and returns a
1257% pointer to the new image or set of images.
1258%
1259% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1260%
1261% The format of the ReadPNGImage method is:
1262%
1263% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1264%
1265% A description of each parameter follows:
1266%
1267% o image_info: the image info.
1268%
1269% o exception: return any errors or warnings in this structure.
1270%
1271% To do, more or less in chronological order (as of version 5.5.2,
1272% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1273%
1274% Get 16-bit cheap transparency working.
1275%
1276% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1277%
1278% Preserve all unknown and not-yet-handled known chunks found in input
1279% PNG file and copy them into output PNG files according to the PNG
1280% copying rules.
1281%
1282% (At this point, PNG encoding should be in full MNG compliance)
1283%
1284% Provide options for choice of background to use when the MNG BACK
1285% chunk is not present or is not mandatory (i.e., leave transparent,
1286% user specified, MNG BACK, PNG bKGD)
1287%
1288% Implement LOOP/ENDL [done, but could do discretionary loops more
1289% efficiently by linking in the duplicate frames.].
1290%
1291% Decode and act on the MHDR simplicity profile (offer option to reject
1292% files or attempt to process them anyway when the profile isn't LC or VLC).
1293%
1294% Upgrade to full MNG without Delta-PNG.
1295%
1296% o BACK [done a while ago except for background image ID]
1297% o MOVE [done 15 May 1999]
1298% o CLIP [done 15 May 1999]
1299% o DISC [done 19 May 1999]
1300% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1301% o SEEK [partially done 19 May 1999 (discard function only)]
1302% o SHOW
1303% o PAST
1304% o BASI
1305% o MNG-level tEXt/iTXt/zTXt
1306% o pHYg
1307% o pHYs
1308% o sBIT
1309% o bKGD
1310% o iTXt (wait for libpng implementation).
1311%
1312% Use the scene signature to discover when an identical scene is
1313% being reused, and just point to the original image->exception instead
1314% of storing another set of pixels. This not specific to MNG
1315% but could be applied generally.
1316%
1317% Upgrade to full MNG with Delta-PNG.
1318%
1319% JNG tEXt/iTXt/zTXt
1320%
1321% We will not attempt to read files containing the CgBI chunk.
1322% They are really Xcode files meant for display on the iPhone.
1323% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001324% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001325% since irretrievable loss of color data has occurred due to the
1326% use of premultiplied alpha.
1327*/
1328
1329#if defined(__cplusplus) || defined(c_plusplus)
1330extern "C" {
1331#endif
1332
1333/*
1334 This the function that does the actual reading of data. It is
1335 the same as the one supplied in libpng, except that it receives the
1336 datastream from the ReadBlob() function instead of standard input.
1337*/
1338static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1339{
1340 Image
1341 *image;
1342
1343 image=(Image *) png_get_io_ptr(png_ptr);
glennrp8fe91592014-10-25 13:57:25 +00001344 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00001345 {
1346 png_size_t
1347 check;
1348
1349 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1350 if (check != length)
1351 {
1352 char
cristy151b66d2015-04-15 10:50:31 +00001353 msg[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001354
cristy151b66d2015-04-15 10:50:31 +00001355 (void) FormatLocaleString(msg,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001356 "Expected %.20g bytes; found %.20g bytes",(double) length,
1357 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001358 png_warning(png_ptr,msg);
1359 png_error(png_ptr,"Read Exception");
1360 }
1361 }
1362}
1363
1364#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1365 !defined(PNG_MNG_FEATURES_SUPPORTED)
1366/* We use mng_get_data() instead of png_get_data() if we have a libpng
1367 * older than libpng-1.0.3a, which was the first to allow the empty
1368 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1369 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1370 * encountered after an empty PLTE, so we have to look ahead for bKGD
1371 * chunks and remove them from the datastream that is passed to libpng,
1372 * and store their contents for later use.
1373 */
1374static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1375{
1376 MngInfo
1377 *mng_info;
1378
1379 Image
1380 *image;
1381
1382 png_size_t
1383 check;
1384
cristybb503372010-05-27 20:51:26 +00001385 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001386 i;
1387
1388 i=0;
1389 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1390 image=(Image *) mng_info->image;
1391 while (mng_info->bytes_in_read_buffer && length)
1392 {
1393 data[i]=mng_info->read_buffer[i];
1394 mng_info->bytes_in_read_buffer--;
1395 length--;
1396 i++;
1397 }
glennrp8fe91592014-10-25 13:57:25 +00001398 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00001399 {
1400 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001401
cristy3ed852e2009-09-05 21:47:34 +00001402 if (check != length)
1403 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001404
cristy3ed852e2009-09-05 21:47:34 +00001405 if (length == 4)
1406 {
1407 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1408 (data[3] == 0))
1409 {
1410 check=(png_size_t) ReadBlob(image,(size_t) length,
1411 (char *) mng_info->read_buffer);
1412 mng_info->read_buffer[4]=0;
1413 mng_info->bytes_in_read_buffer=4;
1414 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1415 mng_info->found_empty_plte=MagickTrue;
1416 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1417 {
1418 mng_info->found_empty_plte=MagickFalse;
1419 mng_info->have_saved_bkgd_index=MagickFalse;
1420 }
1421 }
glennrp0fe50b42010-11-16 03:52:51 +00001422
cristy3ed852e2009-09-05 21:47:34 +00001423 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1424 (data[3] == 1))
1425 {
1426 check=(png_size_t) ReadBlob(image,(size_t) length,
1427 (char *) mng_info->read_buffer);
1428 mng_info->read_buffer[4]=0;
1429 mng_info->bytes_in_read_buffer=4;
1430 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1431 if (mng_info->found_empty_plte)
1432 {
1433 /*
1434 Skip the bKGD data byte and CRC.
1435 */
1436 check=(png_size_t)
1437 ReadBlob(image,5,(char *) mng_info->read_buffer);
1438 check=(png_size_t) ReadBlob(image,(size_t) length,
1439 (char *) mng_info->read_buffer);
1440 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1441 mng_info->have_saved_bkgd_index=MagickTrue;
1442 mng_info->bytes_in_read_buffer=0;
1443 }
1444 }
1445 }
1446 }
1447}
1448#endif
1449
1450static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1451{
1452 Image
1453 *image;
1454
1455 image=(Image *) png_get_io_ptr(png_ptr);
glennrp8fe91592014-10-25 13:57:25 +00001456 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00001457 {
1458 png_size_t
1459 check;
1460
cristybb503372010-05-27 20:51:26 +00001461 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001462
cristy3ed852e2009-09-05 21:47:34 +00001463 if (check != length)
1464 png_error(png_ptr,"WriteBlob Failed");
1465 }
1466}
1467
1468static void png_flush_data(png_structp png_ptr)
1469{
1470 (void) png_ptr;
1471}
1472
1473#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1474static int PalettesAreEqual(Image *a,Image *b)
1475{
cristybb503372010-05-27 20:51:26 +00001476 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001477 i;
1478
1479 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1480 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001481
cristy3ed852e2009-09-05 21:47:34 +00001482 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1483 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001484
cristy3ed852e2009-09-05 21:47:34 +00001485 if (a->colors != b->colors)
1486 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001487
cristybb503372010-05-27 20:51:26 +00001488 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001489 {
1490 if ((a->colormap[i].red != b->colormap[i].red) ||
1491 (a->colormap[i].green != b->colormap[i].green) ||
1492 (a->colormap[i].blue != b->colormap[i].blue))
1493 return((int) MagickFalse);
1494 }
glennrp0fe50b42010-11-16 03:52:51 +00001495
cristy3ed852e2009-09-05 21:47:34 +00001496 return((int) MagickTrue);
1497}
1498#endif
1499
1500static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1501{
1502 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1503 mng_info->exists[i] && !mng_info->frozen[i])
1504 {
1505#ifdef MNG_OBJECT_BUFFERS
1506 if (mng_info->ob[i] != (MngBuffer *) NULL)
1507 {
1508 if (mng_info->ob[i]->reference_count > 0)
1509 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001510
cristy3ed852e2009-09-05 21:47:34 +00001511 if (mng_info->ob[i]->reference_count == 0)
1512 {
1513 if (mng_info->ob[i]->image != (Image *) NULL)
1514 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001515
cristy3ed852e2009-09-05 21:47:34 +00001516 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1517 }
1518 }
1519 mng_info->ob[i]=(MngBuffer *) NULL;
1520#endif
1521 mng_info->exists[i]=MagickFalse;
1522 mng_info->invisible[i]=MagickFalse;
1523 mng_info->viewable[i]=MagickFalse;
1524 mng_info->frozen[i]=MagickFalse;
1525 mng_info->x_off[i]=0;
1526 mng_info->y_off[i]=0;
1527 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001528 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001529 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001530 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001531 }
1532}
1533
glennrp21f0e622011-01-07 16:20:57 +00001534static void MngInfoFreeStruct(MngInfo *mng_info,
1535 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001536{
glennrp21f0e622011-01-07 16:20:57 +00001537 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001538 {
cristybb503372010-05-27 20:51:26 +00001539 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001540 i;
1541
1542 for (i=1; i < MNG_MAX_OBJECTS; i++)
1543 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001544
cristy3ed852e2009-09-05 21:47:34 +00001545 if (mng_info->global_plte != (png_colorp) NULL)
1546 mng_info->global_plte=(png_colorp)
1547 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001548
cristy3ed852e2009-09-05 21:47:34 +00001549 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1550 *have_mng_structure=MagickFalse;
1551 }
1552}
1553
1554static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1555{
1556 MngBox
1557 box;
1558
1559 box=box1;
1560 if (box.left < box2.left)
1561 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001562
cristy3ed852e2009-09-05 21:47:34 +00001563 if (box.top < box2.top)
1564 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001565
cristy3ed852e2009-09-05 21:47:34 +00001566 if (box.right > box2.right)
1567 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001568
cristy3ed852e2009-09-05 21:47:34 +00001569 if (box.bottom > box2.bottom)
1570 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001571
cristy3ed852e2009-09-05 21:47:34 +00001572 return box;
1573}
1574
1575static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1576{
1577 MngBox
1578 box;
1579
1580 /*
1581 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1582 */
cristybb503372010-05-27 20:51:26 +00001583 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1584 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1585 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1586 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001587 if (delta_type != 0)
1588 {
1589 box.left+=previous_box.left;
1590 box.right+=previous_box.right;
1591 box.top+=previous_box.top;
1592 box.bottom+=previous_box.bottom;
1593 }
glennrp0fe50b42010-11-16 03:52:51 +00001594
cristy3ed852e2009-09-05 21:47:34 +00001595 return(box);
1596}
1597
1598static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1599 unsigned char *p)
1600{
1601 MngPair
1602 pair;
1603 /*
cristybb503372010-05-27 20:51:26 +00001604 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001605 */
cristy8182b072010-05-30 20:10:53 +00001606 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1607 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001608
cristy3ed852e2009-09-05 21:47:34 +00001609 if (delta_type != 0)
1610 {
1611 pair.a+=previous_pair.a;
1612 pair.b+=previous_pair.b;
1613 }
glennrp0fe50b42010-11-16 03:52:51 +00001614
cristy3ed852e2009-09-05 21:47:34 +00001615 return(pair);
1616}
1617
cristy8182b072010-05-30 20:10:53 +00001618static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001619{
cristy8182b072010-05-30 20:10:53 +00001620 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001621}
1622
cristy16ea1392012-03-21 20:38:41 +00001623typedef struct _PNGErrorInfo
cristyc82a27b2011-10-21 01:07:16 +00001624{
cristyc82a27b2011-10-21 01:07:16 +00001625 Image
1626 *image;
1627
cristy16ea1392012-03-21 20:38:41 +00001628 ExceptionInfo
1629 *exception;
1630} PNGErrorInfo;
cristyc82a27b2011-10-21 01:07:16 +00001631
cristy16ea1392012-03-21 20:38:41 +00001632static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1633{
1634 ExceptionInfo
1635 *exception;
cristyc82a27b2011-10-21 01:07:16 +00001636
cristy16ea1392012-03-21 20:38:41 +00001637 Image
1638 *image;
1639
1640 PNGErrorInfo
1641 *error_info;
1642
1643 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1644 image=error_info->image;
1645 exception=error_info->exception;
1646
1647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb989d322014-11-05 04:25:18 +00001648 " libpng-%s error: %s", png_get_libpng_ver(NULL),message);
cristy16ea1392012-03-21 20:38:41 +00001649
1650 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1651 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001652
glennrpe4017e32011-01-08 17:16:09 +00001653#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001654 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1655 * are building with libpng-1.4.x and can be ignored.
1656 */
cristy3ed852e2009-09-05 21:47:34 +00001657 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001658#else
1659 png_longjmp(ping,1);
1660#endif
cristy3ed852e2009-09-05 21:47:34 +00001661}
1662
glennrpcf002022011-01-30 02:38:15 +00001663static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001664{
cristy16ea1392012-03-21 20:38:41 +00001665 ExceptionInfo
1666 *exception;
1667
cristy3ed852e2009-09-05 21:47:34 +00001668 Image
1669 *image;
1670
cristy16ea1392012-03-21 20:38:41 +00001671 PNGErrorInfo
1672 *error_info;
1673
cristy3ed852e2009-09-05 21:47:34 +00001674 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1675 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001676
cristy16ea1392012-03-21 20:38:41 +00001677 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1678 image=error_info->image;
1679 exception=error_info->exception;
1680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb989d322014-11-05 04:25:18 +00001681 " libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
glennrp0fe50b42010-11-16 03:52:51 +00001682
cristy16ea1392012-03-21 20:38:41 +00001683 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001684 message,"`%s'",image->filename);
1685}
1686
1687#ifdef PNG_USER_MEM_SUPPORTED
glennrp943b7d32013-04-21 00:40:38 +00001688#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +00001689static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1690#else
1691static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1692#endif
cristy3ed852e2009-09-05 21:47:34 +00001693{
cristydf0d90e2011-12-12 01:03:55 +00001694 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001695 return((png_voidp) AcquireMagickMemory((size_t) size));
cristy3ed852e2009-09-05 21:47:34 +00001696}
1697
1698/*
1699 Free a pointer. It is removed from the list at the same time.
1700*/
glennrpcf002022011-01-30 02:38:15 +00001701static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001702{
glennrp3bd393f2011-12-21 18:54:53 +00001703 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001704 ptr=RelinquishMagickMemory(ptr);
1705 return((png_free_ptr) NULL);
1706}
1707#endif
1708
1709#if defined(__cplusplus) || defined(c_plusplus)
1710}
1711#endif
1712
1713static int
glennrpedaa0382012-04-12 14:16:21 +00001714Magick_png_read_raw_profile(png_struct *ping,Image *image,
1715 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001716{
cristybb503372010-05-27 20:51:26 +00001717 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001718 i;
1719
1720 register unsigned char
1721 *dp;
1722
1723 register png_charp
1724 sp;
1725
1726 png_uint_32
1727 length,
1728 nibbles;
1729
1730 StringInfo
1731 *profile;
1732
glennrp0c3e06b2010-11-19 13:45:02 +00001733 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001734 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1735 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1736 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1737 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1738 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1739 13,14,15};
1740
1741 sp=text[ii].text+1;
1742 /* look for newline */
1743 while (*sp != '\n')
1744 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001745
cristy3ed852e2009-09-05 21:47:34 +00001746 /* look for length */
1747 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1748 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001749
cristyf2f27272009-12-17 14:48:46 +00001750 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001751
glennrp97f90e22011-02-22 05:47:58 +00001752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1753 " length: %lu",(unsigned long) length);
1754
cristy3ed852e2009-09-05 21:47:34 +00001755 while (*sp != ' ' && *sp != '\n')
1756 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001757
cristy3ed852e2009-09-05 21:47:34 +00001758 /* allocate space */
1759 if (length == 0)
1760 {
glennrpedaa0382012-04-12 14:16:21 +00001761 png_warning(ping,"invalid profile length");
cristy3ed852e2009-09-05 21:47:34 +00001762 return(MagickFalse);
1763 }
glennrp0fe50b42010-11-16 03:52:51 +00001764
cristy8723e4b2011-09-01 13:11:19 +00001765 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001766
cristy3ed852e2009-09-05 21:47:34 +00001767 if (profile == (StringInfo *) NULL)
1768 {
glennrpedaa0382012-04-12 14:16:21 +00001769 png_warning(ping, "unable to copy profile");
cristy3ed852e2009-09-05 21:47:34 +00001770 return(MagickFalse);
1771 }
glennrp0fe50b42010-11-16 03:52:51 +00001772
cristy3ed852e2009-09-05 21:47:34 +00001773 /* copy profile, skipping white space and column 1 "=" signs */
1774 dp=GetStringInfoDatum(profile);
1775 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001776
cristybb503372010-05-27 20:51:26 +00001777 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001778 {
1779 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1780 {
1781 if (*sp == '\0')
1782 {
glennrpedaa0382012-04-12 14:16:21 +00001783 png_warning(ping, "ran out of profile data");
cristy3ed852e2009-09-05 21:47:34 +00001784 profile=DestroyStringInfo(profile);
1785 return(MagickFalse);
1786 }
1787 sp++;
1788 }
glennrp0fe50b42010-11-16 03:52:51 +00001789
cristy3ed852e2009-09-05 21:47:34 +00001790 if (i%2 == 0)
1791 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001792
cristy3ed852e2009-09-05 21:47:34 +00001793 else
1794 (*dp++)+=unhex[(int) *sp++];
1795 }
1796 /*
1797 We have already read "Raw profile type.
1798 */
cristy16ea1392012-03-21 20:38:41 +00001799 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001800 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001801
cristy3ed852e2009-09-05 21:47:34 +00001802 if (image_info->verbose)
1803 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001804
cristy3ed852e2009-09-05 21:47:34 +00001805 return MagickTrue;
1806}
1807
1808#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1809static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1810{
1811 Image
1812 *image;
1813
1814
1815 /* The unknown chunk structure contains the chunk data:
1816 png_byte name[5];
1817 png_byte *data;
1818 png_size_t size;
1819
1820 Note that libpng has already taken care of the CRC handling.
1821 */
1822
glennrp2ad70152013-03-03 00:44:57 +00001823 LogMagickEvent(CoderEvent,GetMagickModule(),
1824 " read_vpag_chunk: found %c%c%c%c chunk",
1825 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
cristy3ed852e2009-09-05 21:47:34 +00001826
1827 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1828 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1829 return(0); /* Did not recognize */
1830
1831 /* recognized vpAg */
1832
1833 if (chunk->size != 9)
1834 return(-1); /* Error return */
1835
1836 if (chunk->data[8] != 0)
1837 return(0); /* ImageMagick requires pixel units */
1838
1839 image=(Image *) png_get_user_chunk_ptr(ping);
1840
cristybb503372010-05-27 20:51:26 +00001841 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001842 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001843
cristybb503372010-05-27 20:51:26 +00001844 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001845 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1846
1847 /* Return one of the following: */
1848 /* return(-n); chunk had an error */
1849 /* return(0); did not recognize */
1850 /* return(n); success */
1851
1852 return(1);
1853
1854}
1855#endif
1856
dirkfd6fd072014-10-24 21:10:08 +00001857#if defined(PNG_tIME_SUPPORTED)
1858static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
1859 ExceptionInfo *exception)
1860{
1861 png_timep
1862 time;
1863
1864 if (png_get_tIME(ping,info,&time))
1865 {
1866 char
1867 timestamp[21];
1868
1869 FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
1870 time->year,time->month,time->day,time->hour,time->minute,time->second);
1871 SetImageProperty(image,"png:tIME",timestamp,exception);
1872 }
1873}
1874#endif
1875
cristy3ed852e2009-09-05 21:47:34 +00001876/*
1877%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1878% %
1879% %
1880% %
1881% R e a d O n e P N G I m a g e %
1882% %
1883% %
1884% %
1885%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1886%
1887% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1888% (minus the 8-byte signature) and returns it. It allocates the memory
1889% necessary for the new Image structure and returns a pointer to the new
1890% image.
1891%
1892% The format of the ReadOnePNGImage method is:
1893%
1894% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1895% ExceptionInfo *exception)
1896%
1897% A description of each parameter follows:
1898%
1899% o mng_info: Specifies a pointer to a MngInfo structure.
1900%
1901% o image_info: the image info.
1902%
1903% o exception: return any errors or warnings in this structure.
1904%
1905*/
1906static Image *ReadOnePNGImage(MngInfo *mng_info,
1907 const ImageInfo *image_info, ExceptionInfo *exception)
1908{
1909 /* Read one PNG image */
1910
glennrpcc95c3f2011-04-18 16:46:48 +00001911 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1912
cristy3ed852e2009-09-05 21:47:34 +00001913 Image
1914 *image;
1915
glennrpd0cae252013-03-15 22:30:41 +00001916 char
glennrpec0ddbc2013-03-16 12:40:12 +00001917 im_vers[32],
1918 libpng_runv[32],
1919 libpng_vers[32],
1920 zlib_runv[32],
1921 zlib_vers[32];
glennrpd0cae252013-03-15 22:30:41 +00001922
cristy3ed852e2009-09-05 21:47:34 +00001923 int
glennrp98b83d42012-07-23 02:50:31 +00001924 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
glennrpcb395ac2011-03-30 19:50:23 +00001925 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001926 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001927 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001928 num_passes,
glennrp913f9612012-06-27 17:50:00 +00001929 number_colors,
glennrpfaa852b2010-03-30 12:17:00 +00001930 pass,
1931 ping_bit_depth,
1932 ping_color_type,
glennrpfcf06162012-11-05 14:57:08 +00001933 ping_file_depth,
glennrpfaa852b2010-03-30 12:17:00 +00001934 ping_interlace_method,
1935 ping_compression_method,
1936 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001937 ping_num_trans,
1938 unit_type;
1939
1940 double
1941 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00001942
1943 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00001944 logging,
glennrp98b83d42012-07-23 02:50:31 +00001945 ping_found_cHRM,
1946 ping_found_gAMA,
1947 ping_found_iCCP,
1948 ping_found_sRGB,
glennrp3d627862013-02-26 00:19:34 +00001949 ping_found_sRGB_cHRM,
glennrpecab7d72013-05-14 22:50:32 +00001950 ping_preserve_iCCP,
cristy3ed852e2009-09-05 21:47:34 +00001951 status;
1952
cristy09973322013-06-30 21:06:30 +00001953 MemoryInfo
1954 *volatile pixel_info;
1955
cristy16ea1392012-03-21 20:38:41 +00001956 PixelInfo
1957 transparent_color;
1958
1959 PNGErrorInfo
1960 error_info;
1961
glennrpfaa852b2010-03-30 12:17:00 +00001962 png_bytep
1963 ping_trans_alpha;
1964
1965 png_color_16p
1966 ping_background,
1967 ping_trans_color;
1968
cristy3ed852e2009-09-05 21:47:34 +00001969 png_info
1970 *end_info,
1971 *ping_info;
1972
1973 png_struct
1974 *ping;
1975
1976 png_textp
1977 text;
1978
glennrpfaa852b2010-03-30 12:17:00 +00001979 png_uint_32
1980 ping_height,
1981 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00001982 x_resolution,
1983 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00001984
cristy16ea1392012-03-21 20:38:41 +00001985 QuantumInfo
1986 *quantum_info;
1987
cristybb503372010-05-27 20:51:26 +00001988 ssize_t
cristy756ae432011-11-19 02:18:25 +00001989 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00001990 y;
1991
1992 register unsigned char
1993 *p;
1994
cristybb503372010-05-27 20:51:26 +00001995 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001996 i,
1997 x;
1998
cristy16ea1392012-03-21 20:38:41 +00001999 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002000 *q;
2001
2002 size_t
glennrp39992b42010-11-14 00:03:43 +00002003 length,
cristy3ed852e2009-09-05 21:47:34 +00002004 row_offset;
2005
cristyeb3b22a2011-03-31 20:16:11 +00002006 ssize_t
2007 j;
2008
cristy75fc68f2012-10-08 16:26:00 +00002009 unsigned char
cristy09973322013-06-30 21:06:30 +00002010 *ping_pixels;
cristy55b78b52012-10-08 14:01:27 +00002011
glennrp629960f2012-05-29 19:13:52 +00002012#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002013 png_byte unused_chunks[]=
2014 {
2015 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2016 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2017 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2018 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2019 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
dirkfd6fd072014-10-24 21:10:08 +00002020#if !defined(PNG_tIME_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00002021 116, 73, 77, 69, (png_byte) '\0', /* tIME */
dirkfd6fd072014-10-24 21:10:08 +00002022#endif
glennrp629960f2012-05-29 19:13:52 +00002023#ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2024 /* ignore the APNG chunks */
2025 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2026 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2027 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2028#endif
cristy3ed852e2009-09-05 21:47:34 +00002029 };
2030#endif
2031
glennrpd0cae252013-03-15 22:30:41 +00002032 /* Define these outside of the following "if logging()" block so they will
2033 * show in debuggers.
2034 */
2035 *im_vers='\0';
2036 (void) ConcatenateMagickString(im_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002037 MagickLibVersionText,32);
glennrpd0cae252013-03-15 22:30:41 +00002038 (void) ConcatenateMagickString(im_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002039 MagickLibAddendum,32);
2040
glennrpd0cae252013-03-15 22:30:41 +00002041 *libpng_vers='\0';
2042 (void) ConcatenateMagickString(libpng_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002043 PNG_LIBPNG_VER_STRING,32);
2044 *libpng_runv='\0';
2045 (void) ConcatenateMagickString(libpng_runv,
2046 png_get_libpng_ver(NULL),32);
2047
glennrpd0cae252013-03-15 22:30:41 +00002048 *zlib_vers='\0';
2049 (void) ConcatenateMagickString(zlib_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002050 ZLIB_VERSION,32);
2051 *zlib_runv='\0';
2052 (void) ConcatenateMagickString(zlib_runv,
2053 zlib_version,32);
2054
glennrp2dd19062014-07-28 00:22:24 +00002055 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2056 " Enter ReadOnePNGImage()\n"
2057 " IM version = %s\n"
2058 " Libpng version = %s",
2059 im_vers, libpng_vers);
2060
2061 if (logging != MagickFalse)
2062 {
2063 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
glennrpd0cae252013-03-15 22:30:41 +00002064 {
glennrp2dd19062014-07-28 00:22:24 +00002065 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2066 libpng_runv);
glennrpd0cae252013-03-15 22:30:41 +00002067 }
glennrp2dd19062014-07-28 00:22:24 +00002068 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
2069 zlib_vers);
2070 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2071 {
2072 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2073 zlib_runv);
2074 }
2075 }
glennrpd0cae252013-03-15 22:30:41 +00002076
glennrp25c1e2b2010-03-25 01:39:56 +00002077#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002078 if (image_info->verbose)
2079 printf("Your PNG library (libpng-%s) is rather old.\n",
2080 PNG_LIBPNG_VER_STRING);
2081#endif
2082
glennrp61b4c952009-11-10 20:40:41 +00002083#if (PNG_LIBPNG_VER >= 10400)
2084# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2085 if (image_info->verbose)
2086 {
2087 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2088 PNG_LIBPNG_VER_STRING);
2089 printf("Please update it.\n");
2090 }
2091# endif
2092#endif
2093
cristy16ea1392012-03-21 20:38:41 +00002094
2095 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002096 image=mng_info->image;
2097
glennrpa6a06632011-01-19 15:15:34 +00002098 if (logging != MagickFalse)
glennrp98b83d42012-07-23 02:50:31 +00002099 {
glennrpa6a06632011-01-19 15:15:34 +00002100 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00002101 " Before reading:\n"
2102 " image->alpha_trait=%d"
2103 " image->rendering_intent=%d\n"
2104 " image->colorspace=%d\n"
2105 " image->gamma=%f",
2106 (int) image->alpha_trait, (int) image->rendering_intent,
2107 (int) image->colorspace, image->gamma);
glennrp98b83d42012-07-23 02:50:31 +00002108 }
2109 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2110
glennrp0e319732011-01-25 21:53:13 +00002111 /* Set to an out-of-range color unless tRNS chunk is present */
2112 transparent_color.red=65537;
2113 transparent_color.green=65537;
2114 transparent_color.blue=65537;
cristy16ea1392012-03-21 20:38:41 +00002115 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002116
glennrp913f9612012-06-27 17:50:00 +00002117 number_colors=0;
glennrpcb395ac2011-03-30 19:50:23 +00002118 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002119 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002120 num_raw_profiles = 0;
2121
glennrp98b83d42012-07-23 02:50:31 +00002122 ping_found_cHRM = MagickFalse;
2123 ping_found_gAMA = MagickFalse;
2124 ping_found_iCCP = MagickFalse;
2125 ping_found_sRGB = MagickFalse;
glennrp4b917592013-06-20 19:53:59 +00002126 ping_found_sRGB_cHRM = MagickFalse;
glennrpecab7d72013-05-14 22:50:32 +00002127 ping_preserve_iCCP = MagickFalse;
2128
glennrp98b83d42012-07-23 02:50:31 +00002129
cristy3ed852e2009-09-05 21:47:34 +00002130 /*
2131 Allocate the PNG structures
2132 */
2133#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00002134 error_info.image=image;
2135 error_info.exception=exception;
glennrp3e0971d2014-11-06 12:59:43 +00002136 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002137 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2138 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002139#else
glennrp3e0971d2014-11-06 12:59:43 +00002140 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002141 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002142#endif
2143 if (ping == (png_struct *) NULL)
2144 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002145
cristy3ed852e2009-09-05 21:47:34 +00002146 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002147
cristy3ed852e2009-09-05 21:47:34 +00002148 if (ping_info == (png_info *) NULL)
2149 {
2150 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2151 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2152 }
glennrp0fe50b42010-11-16 03:52:51 +00002153
cristy3ed852e2009-09-05 21:47:34 +00002154 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002155
cristy3ed852e2009-09-05 21:47:34 +00002156 if (end_info == (png_info *) NULL)
2157 {
2158 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2159 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2160 }
glennrp0fe50b42010-11-16 03:52:51 +00002161
cristy09973322013-06-30 21:06:30 +00002162 pixel_info=(MemoryInfo *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002163
glennrpfaa852b2010-03-30 12:17:00 +00002164 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002165 {
2166 /*
2167 PNG image is corrupt.
2168 */
2169 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002170
glennrp868fff32014-03-16 22:09:06 +00002171#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002172 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002173#endif
glennrpedaa0382012-04-12 14:16:21 +00002174
cristy09973322013-06-30 21:06:30 +00002175 if (pixel_info != (MemoryInfo *) NULL)
2176 pixel_info=RelinquishVirtualMemory(pixel_info);
glennrpedaa0382012-04-12 14:16:21 +00002177
cristy3ed852e2009-09-05 21:47:34 +00002178 if (logging != MagickFalse)
2179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2180 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002181
cristy3ed852e2009-09-05 21:47:34 +00002182 if (image != (Image *) NULL)
dirk057a5d22015-04-21 21:05:39 +00002183 {
2184 const char
2185 *option;
2186
2187 option=GetImageOption(image_info,"png:preserve-corrupt-image");
2188 if (IsStringTrue(option) == MagickFalse)
2189 image->columns=0;
2190 }
glennrp0fe50b42010-11-16 03:52:51 +00002191
cristy3ed852e2009-09-05 21:47:34 +00002192 return(GetFirstImageInList(image));
2193 }
glennrpedaa0382012-04-12 14:16:21 +00002194
2195 /* { For navigation to end of SETJMP-protected block. Within this
2196 * block, use png_error() instead of Throwing an Exception, to ensure
2197 * that libpng is able to clean up, and that the semaphore is unlocked.
2198 */
2199
glennrp868fff32014-03-16 22:09:06 +00002200#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpedaa0382012-04-12 14:16:21 +00002201 LockSemaphoreInfo(ping_semaphore);
2202#endif
2203
glennrp943b7d32013-04-21 00:40:38 +00002204#ifdef PNG_BENIGN_ERRORS_SUPPORTED
glennrpa3a5f952013-04-17 12:58:15 +00002205 /* Allow benign errors */
2206 png_set_benign_errors(ping, 1);
2207#endif
2208
dirkf9eb2042015-10-22 13:22:42 +02002209#ifdef PNG_SET_USER_LIMITS_SUPPORTED
2210 /* Reject images with too many rows or columns */
2211 png_set_user_limits(ping,
2212 (png_uint_32) MagickMin(0x7fffffffL,
2213 GetMagickResourceLimit(WidthResource)),
2214 (png_uint_32) MagickMin(0x7fffffffL,
2215 GetMagickResourceLimit(HeightResource)));
2216#endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2217
cristy3ed852e2009-09-05 21:47:34 +00002218 /*
2219 Prepare PNG for reading.
2220 */
glennrpfaa852b2010-03-30 12:17:00 +00002221
cristy3ed852e2009-09-05 21:47:34 +00002222 mng_info->image_found++;
2223 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002224
cristy3ed852e2009-09-05 21:47:34 +00002225 if (LocaleCompare(image_info->magick,"MNG") == 0)
2226 {
2227#if defined(PNG_MNG_FEATURES_SUPPORTED)
2228 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2229 png_set_read_fn(ping,image,png_get_data);
2230#else
2231#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2232 png_permit_empty_plte(ping,MagickTrue);
2233 png_set_read_fn(ping,image,png_get_data);
2234#else
2235 mng_info->image=image;
2236 mng_info->bytes_in_read_buffer=0;
2237 mng_info->found_empty_plte=MagickFalse;
2238 mng_info->have_saved_bkgd_index=MagickFalse;
2239 png_set_read_fn(ping,mng_info,mng_get_data);
2240#endif
2241#endif
2242 }
glennrp0fe50b42010-11-16 03:52:51 +00002243
cristy3ed852e2009-09-05 21:47:34 +00002244 else
2245 png_set_read_fn(ping,image,png_get_data);
2246
glennrpe604a752014-06-28 02:24:30 +00002247 {
2248 const char
2249 *value;
2250
2251 value=GetImageOption(image_info,"profile:skip");
2252
2253 if (IsOptionMember("ICC",value) == MagickFalse)
2254 {
2255
2256 value=GetImageOption(image_info,"png:preserve-iCCP");
2257
2258 if (value == NULL)
2259 value=GetImageArtifact(image,"png:preserve-iCCP");
2260
2261 if (value != NULL)
2262 ping_preserve_iCCP=MagickTrue;
glennrp201f0c92014-06-28 16:13:49 +00002263
2264#if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2265 /* Don't let libpng check for ICC/sRGB profile because we're going
2266 * to do that anyway. This feature was added at libpng-1.6.12.
glennrpcf45b202014-06-29 16:46:17 +00002267 * If logging, go ahead and check and issue a warning as appropriate.
glennrp201f0c92014-06-28 16:13:49 +00002268 */
glennrpcf45b202014-06-29 16:46:17 +00002269 if (logging == MagickFalse)
2270 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
glennrp201f0c92014-06-28 16:13:49 +00002271#endif
glennrpe604a752014-06-28 02:24:30 +00002272 }
2273#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2274 else
2275 {
2276 png_set_keep_unknown_chunks(ping, 1, mng_iCCP, 1);
2277 }
2278#endif
2279 }
cristy3ed852e2009-09-05 21:47:34 +00002280#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2281 /* Ignore unused chunks and all unknown chunks except for vpAg */
glennrp2ad70152013-03-03 00:44:57 +00002282#if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2283 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2284#else
cristy3ed852e2009-09-05 21:47:34 +00002285 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
glennrp2ad70152013-03-03 00:44:57 +00002286#endif
cristy3ed852e2009-09-05 21:47:34 +00002287 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2288 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2289 (int)sizeof(unused_chunks)/5);
2290 /* Callback for other unknown chunks */
2291 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2292#endif
2293
glennrp2feb1412013-01-22 15:02:16 +00002294#ifdef PNG_SET_USER_LIMITS_SUPPORTED
glennrp09cd9622013-01-22 15:17:38 +00002295# if (PNG_LIBPNG_VER >= 10400)
glennrp2feb1412013-01-22 15:02:16 +00002296 /* Limit the size of the chunk storage cache used for sPLT, text,
glennrp68736192013-01-24 06:32:08 +00002297 * and unknown chunks.
glennrp2feb1412013-01-22 15:02:16 +00002298 */
glennrp68736192013-01-24 06:32:08 +00002299 png_set_chunk_cache_max(ping, 32767);
glennrp09cd9622013-01-22 15:17:38 +00002300# endif
glennrp2feb1412013-01-22 15:02:16 +00002301#endif
2302
glennrp9bf97b62012-06-06 21:03:14 +00002303#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2304 /* Disable new libpng-1.5.10 feature */
2305 png_set_check_for_invalid_index (ping, 0);
2306#endif
2307
glennrp991e92a2010-01-28 03:09:00 +00002308#if (PNG_LIBPNG_VER < 10400)
2309# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2310 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002311 /* Disable thread-unsafe features of pnggccrd */
2312 if (png_access_version_number() >= 10200)
2313 {
2314 png_uint_32 mmx_disable_mask=0;
2315 png_uint_32 asm_flags;
2316
2317 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2318 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2319 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2320 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2321 asm_flags=png_get_asm_flags(ping);
2322 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2323 }
glennrp991e92a2010-01-28 03:09:00 +00002324# endif
cristy3ed852e2009-09-05 21:47:34 +00002325#endif
2326
2327 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002328
2329 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2330 &ping_bit_depth,&ping_color_type,
2331 &ping_interlace_method,&ping_compression_method,
2332 &ping_filter_method);
2333
glennrpfcf06162012-11-05 14:57:08 +00002334 ping_file_depth = ping_bit_depth;
2335
glennrp0d767722014-01-17 16:08:31 +00002336 /* Swap bytes if requested */
2337 if (ping_file_depth == 16)
2338 {
2339 const char
glennrpa3d5f0e2014-01-30 18:58:51 +00002340 *value;
glennrp6647b972015-01-12 04:39:14 +00002341
glennrpa3d5f0e2014-01-30 18:58:51 +00002342 value=GetImageOption(image_info,"png:swap-bytes");
glennrp6647b972015-01-12 04:39:14 +00002343
glennrpa3d5f0e2014-01-30 18:58:51 +00002344 if (value == NULL)
2345 value=GetImageArtifact(image,"png:swap-bytes");
glennrp6647b972015-01-12 04:39:14 +00002346
glennrpa3d5f0e2014-01-30 18:58:51 +00002347 if (value != NULL)
2348 png_set_swap(ping);
glennrp0d767722014-01-17 16:08:31 +00002349 }
2350
glennrp5830fbc2013-01-27 06:11:45 +00002351 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2352 {
2353 char
cristy151b66d2015-04-15 10:50:31 +00002354 msg[MagickPathExtent];
glennrp5830fbc2013-01-27 06:11:45 +00002355
cristy151b66d2015-04-15 10:50:31 +00002356 (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_color_type);
glennrp3398b5b2013-05-03 23:10:31 +00002357 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
glennrp5830fbc2013-01-27 06:11:45 +00002358
cristy151b66d2015-04-15 10:50:31 +00002359 (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_bit_depth);
glennrp3398b5b2013-05-03 23:10:31 +00002360 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
glennrp5830fbc2013-01-27 06:11:45 +00002361 }
2362
glennrpfaa852b2010-03-30 12:17:00 +00002363 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2364 &ping_trans_color);
2365
2366 (void) png_get_bKGD(ping, ping_info, &ping_background);
2367
2368 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002369 {
glennrpfcf06162012-11-05 14:57:08 +00002370 png_set_packing(ping);
2371 ping_bit_depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00002372 }
glennrpfaa852b2010-03-30 12:17:00 +00002373
2374 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002375 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002376 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
glennrp98b83d42012-07-23 02:50:31 +00002377
cristy176b29a2012-06-21 13:35:15 +00002378 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2379 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2380 {
2381 image->rendering_intent=UndefinedIntent;
glennrp98b83d42012-07-23 02:50:31 +00002382 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
cristy176b29a2012-06-21 13:35:15 +00002383 (void) ResetMagickMemory(&image->chromaticity,0,
2384 sizeof(image->chromaticity));
2385 }
glennrp98b83d42012-07-23 02:50:31 +00002386
cristy3ed852e2009-09-05 21:47:34 +00002387 if (logging != MagickFalse)
2388 {
2389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00002390 " PNG width: %.20g, height: %.20g\n"
2391 " PNG color_type: %d, bit_depth: %d\n"
2392 " PNG compression_method: %d\n"
cristy3ed852e2009-09-05 21:47:34 +00002393 " PNG interlace_method: %d, filter_method: %d",
glennrp2dd19062014-07-28 00:22:24 +00002394 (double) ping_width, (double) ping_height,
2395 ping_color_type, ping_bit_depth,
2396 ping_compression_method,
glennrpfaa852b2010-03-30 12:17:00 +00002397 ping_interlace_method,ping_filter_method);
glennrp2dd19062014-07-28 00:22:24 +00002398
cristy3ed852e2009-09-05 21:47:34 +00002399 }
2400
glennrpecab7d72013-05-14 22:50:32 +00002401 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2402 {
2403 ping_found_iCCP=MagickTrue;
2404 if (logging != MagickFalse)
2405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2406 " Found PNG iCCP chunk.");
2407 }
2408
glennrp98b83d42012-07-23 02:50:31 +00002409 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2410 {
2411 ping_found_gAMA=MagickTrue;
2412 if (logging != MagickFalse)
2413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2414 " Found PNG gAMA chunk.");
2415 }
2416
2417 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2418 {
2419 ping_found_cHRM=MagickTrue;
2420 if (logging != MagickFalse)
2421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2422 " Found PNG cHRM chunk.");
2423 }
2424
glennrpecab7d72013-05-14 22:50:32 +00002425 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2426 PNG_INFO_sRGB))
glennrp98b83d42012-07-23 02:50:31 +00002427 {
2428 ping_found_sRGB=MagickTrue;
2429 if (logging != MagickFalse)
2430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2431 " Found PNG sRGB chunk.");
2432 }
2433
glennrpfaa852b2010-03-30 12:17:00 +00002434#ifdef PNG_READ_iCCP_SUPPORTED
glennrpe604a752014-06-28 02:24:30 +00002435 if (ping_found_iCCP !=MagickTrue &&
glennrpecab7d72013-05-14 22:50:32 +00002436 ping_found_sRGB != MagickTrue &&
2437 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2438 {
2439 ping_found_iCCP=MagickTrue;
2440 if (logging != MagickFalse)
2441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2442 " Found PNG iCCP chunk.");
2443 }
2444
glennrpfaa852b2010-03-30 12:17:00 +00002445 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002446 {
2447 int
2448 compression;
2449
glennrpe4017e32011-01-08 17:16:09 +00002450#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002451 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002452 info;
2453#else
2454 png_bytep
2455 info;
2456#endif
2457
2458 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002459 name;
2460
2461 png_uint_32
2462 profile_length;
2463
2464 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2465 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002466
cristy3ed852e2009-09-05 21:47:34 +00002467 if (profile_length != 0)
2468 {
2469 StringInfo
2470 *profile;
2471
2472 if (logging != MagickFalse)
2473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2474 " Reading PNG iCCP chunk.");
glennrpecab7d72013-05-14 22:50:32 +00002475
cristye8f8f382011-09-01 13:32:37 +00002476 profile=BlobToStringInfo(info,profile_length);
glennrpecab7d72013-05-14 22:50:32 +00002477
cristye8f8f382011-09-01 13:32:37 +00002478 if (profile == (StringInfo *) NULL)
glennrpecab7d72013-05-14 22:50:32 +00002479 {
2480 png_warning(ping, "ICC profile is NULL");
2481 profile=DestroyStringInfo(profile);
2482 }
glennrpedaa0382012-04-12 14:16:21 +00002483 else
glennrpecab7d72013-05-14 22:50:32 +00002484 {
2485 if (ping_preserve_iCCP == MagickFalse)
glennrpedaa0382012-04-12 14:16:21 +00002486 {
glennrpecab7d72013-05-14 22:50:32 +00002487 int
2488 icheck,
2489 got_crc=0;
2490
2491
2492 png_uint_32
2493 length,
2494 profile_crc=0;
2495
2496 unsigned char
2497 *data;
2498
2499 length=(png_uint_32) GetStringInfoLength(profile);
2500
2501 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2502 {
2503 if (length == sRGB_info[icheck].len)
2504 {
2505 if (got_crc == 0)
2506 {
2507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2508 " Got a %lu-byte ICC profile (potentially sRGB)",
2509 (unsigned long) length);
2510
2511 data=GetStringInfoDatum(profile);
2512 profile_crc=crc32(0,data,length);
2513
2514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2515 " with crc=%8x",(unsigned int) profile_crc);
2516 got_crc++;
2517 }
2518
2519 if (profile_crc == sRGB_info[icheck].crc)
2520 {
2521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2522 " It is sRGB with rendering intent = %s",
2523 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2524 sRGB_info[icheck].intent));
2525 if (image->rendering_intent==UndefinedIntent)
2526 {
2527 image->rendering_intent=
2528 Magick_RenderingIntent_from_PNG_RenderingIntent(
2529 sRGB_info[icheck].intent);
2530 }
2531 break;
2532 }
2533 }
2534 }
2535 if (sRGB_info[icheck].len == 0)
2536 {
2537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2538 " Got a %lu-byte ICC profile not recognized as sRGB",
2539 (unsigned long) length);
2540 (void) SetImageProfile(image,"icc",profile,exception);
2541 }
glennrpedaa0382012-04-12 14:16:21 +00002542 }
glennrpecab7d72013-05-14 22:50:32 +00002543 else /* Preserve-iCCP */
2544 {
2545 (void) SetImageProfile(image,"icc",profile,exception);
2546 }
2547
2548 profile=DestroyStringInfo(profile);
2549 }
cristy3ed852e2009-09-05 21:47:34 +00002550 }
2551 }
2552#endif
glennrpecab7d72013-05-14 22:50:32 +00002553
cristy3ed852e2009-09-05 21:47:34 +00002554#if defined(PNG_READ_sRGB_SUPPORTED)
2555 {
glennrpecab7d72013-05-14 22:50:32 +00002556 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2557 PNG_INFO_sRGB))
2558 {
2559 if (png_get_sRGB(ping,ping_info,&intent))
cristy2ea7a8e2012-02-08 01:04:50 +00002560 {
glennrpecab7d72013-05-14 22:50:32 +00002561 if (image->rendering_intent == UndefinedIntent)
2562 image->rendering_intent=
2563 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002564
cristy3ed852e2009-09-05 21:47:34 +00002565 if (logging != MagickFalse)
2566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002567 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002568 }
glennrpecab7d72013-05-14 22:50:32 +00002569 }
2570
2571 else if (mng_info->have_global_srgb)
2572 {
2573 if (image->rendering_intent == UndefinedIntent)
2574 image->rendering_intent=
2575 Magick_RenderingIntent_from_PNG_RenderingIntent
2576 (mng_info->global_srgb_intent);
2577 }
cristy3ed852e2009-09-05 21:47:34 +00002578 }
2579#endif
glennrpecab7d72013-05-14 22:50:32 +00002580
2581
cristy3ed852e2009-09-05 21:47:34 +00002582 {
glennrpfaa852b2010-03-30 12:17:00 +00002583 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2584 if (mng_info->have_global_gama)
2585 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002586
cristy3ed852e2009-09-05 21:47:34 +00002587 if (png_get_gAMA(ping,ping_info,&file_gamma))
2588 {
2589 image->gamma=(float) file_gamma;
2590 if (logging != MagickFalse)
2591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2592 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2593 }
2594 }
glennrp98b83d42012-07-23 02:50:31 +00002595
glennrpfaa852b2010-03-30 12:17:00 +00002596 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2597 {
2598 if (mng_info->have_global_chrm != MagickFalse)
2599 {
2600 (void) png_set_cHRM(ping,ping_info,
2601 mng_info->global_chrm.white_point.x,
2602 mng_info->global_chrm.white_point.y,
2603 mng_info->global_chrm.red_primary.x,
2604 mng_info->global_chrm.red_primary.y,
2605 mng_info->global_chrm.green_primary.x,
2606 mng_info->global_chrm.green_primary.y,
2607 mng_info->global_chrm.blue_primary.x,
2608 mng_info->global_chrm.blue_primary.y);
2609 }
2610 }
glennrp0fe50b42010-11-16 03:52:51 +00002611
glennrpfaa852b2010-03-30 12:17:00 +00002612 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002613 {
2614 (void) png_get_cHRM(ping,ping_info,
2615 &image->chromaticity.white_point.x,
2616 &image->chromaticity.white_point.y,
2617 &image->chromaticity.red_primary.x,
2618 &image->chromaticity.red_primary.y,
2619 &image->chromaticity.green_primary.x,
2620 &image->chromaticity.green_primary.y,
2621 &image->chromaticity.blue_primary.x,
2622 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002623
glennrpecab7d72013-05-14 22:50:32 +00002624 ping_found_cHRM=MagickTrue;
2625
glennrp3d627862013-02-26 00:19:34 +00002626 if (image->chromaticity.red_primary.x>0.6399f &&
2627 image->chromaticity.red_primary.x<0.6401f &&
2628 image->chromaticity.red_primary.y>0.3299f &&
2629 image->chromaticity.red_primary.y<0.3301f &&
2630 image->chromaticity.green_primary.x>0.2999f &&
2631 image->chromaticity.green_primary.x<0.3001f &&
2632 image->chromaticity.green_primary.y>0.5999f &&
2633 image->chromaticity.green_primary.y<0.6001f &&
2634 image->chromaticity.blue_primary.x>0.1499f &&
2635 image->chromaticity.blue_primary.x<0.1501f &&
2636 image->chromaticity.blue_primary.y>0.0599f &&
2637 image->chromaticity.blue_primary.y<0.0601f &&
2638 image->chromaticity.white_point.x>0.3126f &&
2639 image->chromaticity.white_point.x<0.3128f &&
2640 image->chromaticity.white_point.y>0.3289f &&
2641 image->chromaticity.white_point.y<0.3291f)
2642 ping_found_sRGB_cHRM=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002643 }
glennrp0fe50b42010-11-16 03:52:51 +00002644
glennrpe610a072010-08-05 17:08:46 +00002645 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002646 {
glennrp3d627862013-02-26 00:19:34 +00002647 if (ping_found_sRGB != MagickTrue &&
2648 (ping_found_gAMA != MagickTrue ||
glennrp84288232013-03-02 15:52:36 +00002649 (image->gamma > .45 && image->gamma < .46)) &&
glennrp3d627862013-02-26 00:19:34 +00002650 (ping_found_cHRM != MagickTrue ||
cristycd8b3312013-12-22 01:51:11 +00002651 ping_found_sRGB_cHRM != MagickFalse) &&
glennrp3d627862013-02-26 00:19:34 +00002652 ping_found_iCCP != MagickTrue)
glennrp5cf1bff2013-01-16 02:44:03 +00002653 {
2654 png_set_sRGB(ping,ping_info,
2655 Magick_RenderingIntent_to_PNG_RenderingIntent
2656 (image->rendering_intent));
glennrp5cf1bff2013-01-16 02:44:03 +00002657 file_gamma=1.000f/2.200f;
2658 ping_found_sRGB=MagickTrue;
glennrp84288232013-03-02 15:52:36 +00002659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp918b9dc2013-04-03 13:41:41 +00002660 " Setting sRGB as if in input");
glennrp5cf1bff2013-01-16 02:44:03 +00002661 }
cristy3ed852e2009-09-05 21:47:34 +00002662 }
glennrp5cf1bff2013-01-16 02:44:03 +00002663
cristy3ed852e2009-09-05 21:47:34 +00002664#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002665 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002666 {
cristy905ef802011-02-23 00:29:18 +00002667 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2668 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002669
cristy3ed852e2009-09-05 21:47:34 +00002670 if (logging != MagickFalse)
2671 if (image->page.x || image->page.y)
2672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002673 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2674 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002675 }
2676#endif
2677#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002678 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2679 {
2680 if (mng_info->have_global_phys)
2681 {
2682 png_set_pHYs(ping,ping_info,
2683 mng_info->global_x_pixels_per_unit,
2684 mng_info->global_y_pixels_per_unit,
2685 mng_info->global_phys_unit_type);
2686 }
2687 }
2688
cristy5c97f622014-01-15 12:36:09 +00002689 x_resolution=0;
2690 y_resolution=0;
2691 unit_type=0;
glennrpfaa852b2010-03-30 12:17:00 +00002692 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002693 {
cristy3ed852e2009-09-05 21:47:34 +00002694 /*
2695 Set image resolution.
2696 */
2697 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002698 &unit_type);
cristy16ea1392012-03-21 20:38:41 +00002699 image->resolution.x=(double) x_resolution;
2700 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002701
cristy3ed852e2009-09-05 21:47:34 +00002702 if (unit_type == PNG_RESOLUTION_METER)
2703 {
2704 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00002705 image->resolution.x=(double) x_resolution/100.0;
2706 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002707 }
glennrp0fe50b42010-11-16 03:52:51 +00002708
cristy3ed852e2009-09-05 21:47:34 +00002709 if (logging != MagickFalse)
2710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002711 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2712 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002713 }
cristy3ed852e2009-09-05 21:47:34 +00002714#endif
glennrp823b55c2011-03-14 18:46:46 +00002715
glennrpfaa852b2010-03-30 12:17:00 +00002716 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002717 {
cristy3ed852e2009-09-05 21:47:34 +00002718 png_colorp
2719 palette;
2720
2721 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002722
cristy3ed852e2009-09-05 21:47:34 +00002723 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002724 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002725 {
2726 if (mng_info->global_plte_length)
2727 {
2728 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2729 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002730
glennrpfaa852b2010-03-30 12:17:00 +00002731 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrpedaa0382012-04-12 14:16:21 +00002732 {
cristy3ed852e2009-09-05 21:47:34 +00002733 if (mng_info->global_trns_length)
2734 {
glennrpedaa0382012-04-12 14:16:21 +00002735 png_warning(ping,
2736 "global tRNS has more entries than global PLTE");
cristy3ed852e2009-09-05 21:47:34 +00002737 }
glennrpedaa0382012-04-12 14:16:21 +00002738 else
2739 {
2740 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2741 (int) mng_info->global_trns_length,NULL);
2742 }
2743 }
glennrpbfd9e612011-04-22 14:02:20 +00002744#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002745 if (
2746#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2747 mng_info->have_saved_bkgd_index ||
2748#endif
glennrpfaa852b2010-03-30 12:17:00 +00002749 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002750 {
2751 png_color_16
2752 background;
2753
2754#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2755 if (mng_info->have_saved_bkgd_index)
2756 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002757#endif
glennrpfaa852b2010-03-30 12:17:00 +00002758 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2759 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002760
cristy3ed852e2009-09-05 21:47:34 +00002761 background.red=(png_uint_16)
2762 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002763
cristy3ed852e2009-09-05 21:47:34 +00002764 background.green=(png_uint_16)
2765 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002766
cristy3ed852e2009-09-05 21:47:34 +00002767 background.blue=(png_uint_16)
2768 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002769
glennrpc6c391a2011-04-27 02:23:56 +00002770 background.gray=(png_uint_16)
2771 mng_info->global_plte[background.index].green;
2772
cristy3ed852e2009-09-05 21:47:34 +00002773 png_set_bKGD(ping,ping_info,&background);
2774 }
2775#endif
2776 }
2777 else
glennrpedaa0382012-04-12 14:16:21 +00002778 png_error(ping,"No global PLTE in file");
cristy3ed852e2009-09-05 21:47:34 +00002779 }
2780 }
2781
glennrpbfd9e612011-04-22 14:02:20 +00002782#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002783 if (mng_info->have_global_bkgd &&
2784 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002785 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002786
glennrpfaa852b2010-03-30 12:17:00 +00002787 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002788 {
glennrpbfd9e612011-04-22 14:02:20 +00002789 unsigned int
2790 bkgd_scale;
2791
glennrp2dd19062014-07-28 00:22:24 +00002792 /* Set image background color.
2793 * Scale background components to 16-bit, then scale
glennrpbfd9e612011-04-22 14:02:20 +00002794 * to quantum depth
2795 */
glennrp0fe50b42010-11-16 03:52:51 +00002796
glennrpbfd9e612011-04-22 14:02:20 +00002797 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002798
glennrpfcf06162012-11-05 14:57:08 +00002799 if (ping_file_depth == 1)
glennrpbfd9e612011-04-22 14:02:20 +00002800 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002801
glennrpfcf06162012-11-05 14:57:08 +00002802 else if (ping_file_depth == 2)
glennrpbfd9e612011-04-22 14:02:20 +00002803 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002804
glennrpfcf06162012-11-05 14:57:08 +00002805 else if (ping_file_depth == 4)
glennrpbfd9e612011-04-22 14:02:20 +00002806 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002807
glennrpfcf06162012-11-05 14:57:08 +00002808 if (ping_file_depth <= 8)
glennrpbfd9e612011-04-22 14:02:20 +00002809 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002810
glennrpbfd9e612011-04-22 14:02:20 +00002811 ping_background->red *= bkgd_scale;
2812 ping_background->green *= bkgd_scale;
2813 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002814
glennrpbfd9e612011-04-22 14:02:20 +00002815 if (logging != MagickFalse)
2816 {
glennrp2dd19062014-07-28 00:22:24 +00002817 if (logging != MagickFalse)
2818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2819 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d).\n"
2820 " bkgd_scale=%d. ping_background=(%d,%d,%d).",
2821 ping_background->red,ping_background->green,
2822 ping_background->blue,
2823 bkgd_scale,ping_background->red,
2824 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002825 }
glennrp2cbb4482010-06-02 04:37:24 +00002826
glennrpbfd9e612011-04-22 14:02:20 +00002827 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002828 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002829
glennrpbfd9e612011-04-22 14:02:20 +00002830 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002831 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002832
glennrpbfd9e612011-04-22 14:02:20 +00002833 image->background_color.blue=
2834 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002835
cristy16ea1392012-03-21 20:38:41 +00002836 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002837
glennrpbfd9e612011-04-22 14:02:20 +00002838 if (logging != MagickFalse)
2839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2840 " image->background_color=(%.20g,%.20g,%.20g).",
2841 (double) image->background_color.red,
2842 (double) image->background_color.green,
2843 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002844 }
glennrpbfd9e612011-04-22 14:02:20 +00002845#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002846
glennrpfaa852b2010-03-30 12:17:00 +00002847 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002848 {
2849 /*
glennrpa6a06632011-01-19 15:15:34 +00002850 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002851 */
2852 int
2853 max_sample;
2854
cristy35ef8242010-06-03 16:24:13 +00002855 size_t
2856 one=1;
2857
cristy3ed852e2009-09-05 21:47:34 +00002858 if (logging != MagickFalse)
2859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2860 " Reading PNG tRNS chunk.");
2861
glennrpfcf06162012-11-05 14:57:08 +00002862 max_sample = (int) ((one << ping_file_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002863
glennrpfaa852b2010-03-30 12:17:00 +00002864 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2865 (int)ping_trans_color->gray > max_sample) ||
2866 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2867 ((int)ping_trans_color->red > max_sample ||
2868 (int)ping_trans_color->green > max_sample ||
2869 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002870 {
2871 if (logging != MagickFalse)
2872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2873 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002874 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002875 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy8a46d822012-08-28 23:32:39 +00002876 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00002877 }
2878 else
2879 {
glennrpa6a06632011-01-19 15:15:34 +00002880 int
glennrp9b034ff2015-03-16 14:36:44 +00002881 scale_to_short;
glennrpa6a06632011-01-19 15:15:34 +00002882
glennrpfcf06162012-11-05 14:57:08 +00002883 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
glennrpa6a06632011-01-19 15:15:34 +00002884
2885 /* Scale transparent_color to short */
2886 transparent_color.red= scale_to_short*ping_trans_color->red;
2887 transparent_color.green= scale_to_short*ping_trans_color->green;
2888 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy16ea1392012-03-21 20:38:41 +00002889 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002890
glennrpfaa852b2010-03-30 12:17:00 +00002891 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002892 {
glennrp0f111982010-07-07 20:18:33 +00002893 if (logging != MagickFalse)
2894 {
2895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00002896 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
2897 (int) ping_trans_color->gray,(int) transparent_color.alpha);
glennrp0fe50b42010-11-16 03:52:51 +00002898
glennrp0f111982010-07-07 20:18:33 +00002899 }
cristy16ea1392012-03-21 20:38:41 +00002900 transparent_color.red=transparent_color.alpha;
2901 transparent_color.green=transparent_color.alpha;
2902 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002903 }
2904 }
2905 }
2906#if defined(PNG_READ_sBIT_SUPPORTED)
2907 if (mng_info->have_global_sbit)
2908 {
glennrpfaa852b2010-03-30 12:17:00 +00002909 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002910 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2911 }
2912#endif
2913 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002914
cristy3ed852e2009-09-05 21:47:34 +00002915 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002916
2917 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2918
cristy3ed852e2009-09-05 21:47:34 +00002919 /*
2920 Initialize image structure.
2921 */
2922 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002923 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002924 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002925 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002926 if (mng_info->mng_type == 0)
2927 {
glennrpfaa852b2010-03-30 12:17:00 +00002928 mng_info->mng_width=ping_width;
2929 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002930 mng_info->frame=mng_info->image_box;
2931 mng_info->clip=mng_info->image_box;
2932 }
glennrp0fe50b42010-11-16 03:52:51 +00002933
cristy3ed852e2009-09-05 21:47:34 +00002934 else
2935 {
2936 image->page.y=mng_info->y_off[mng_info->object_id];
2937 }
glennrp0fe50b42010-11-16 03:52:51 +00002938
cristy3ed852e2009-09-05 21:47:34 +00002939 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002940 image->columns=ping_width;
2941 image->rows=ping_height;
glennrpa6c5d342012-05-14 13:21:17 +00002942
cristy16ea1392012-03-21 20:38:41 +00002943 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2944 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
glennrp8d0bca52012-05-17 02:33:23 +00002945 {
glennrp0a5b92c2014-03-16 22:08:03 +00002946 double
2947 image_gamma = image->gamma;
2948
2949 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2950 " image->gamma=%f",(float) image_gamma);
2951
2952 if (image_gamma > 0.75)
glennrp8d0bca52012-05-17 02:33:23 +00002953 {
glennrp0a5b92c2014-03-16 22:08:03 +00002954 /* Set image->rendering_intent to Undefined,
glennrpe88af772012-08-22 13:59:50 +00002955 * image->colorspace to GRAY, and reset image->chromaticity.
glennrp8d0bca52012-05-17 02:33:23 +00002956 */
glennrp67429932013-10-05 14:59:56 +00002957 image->intensity = Rec709LuminancePixelIntensityMethod;
glennrp8d0bca52012-05-17 02:33:23 +00002958 SetImageColorspace(image,GRAYColorspace,exception);
2959 }
glennrpccc36af2014-09-08 23:22:03 +00002960 else
2961 {
2962 RenderingIntent
2963 save_rendering_intent = image->rendering_intent;
2964 ChromaticityInfo
2965 save_chromaticity = image->chromaticity;
2966
2967 SetImageColorspace(image,GRAYColorspace,exception);
2968 image->rendering_intent = save_rendering_intent;
2969 image->chromaticity = save_chromaticity;
2970 }
2971
2972 image->gamma = image_gamma;
glennrp8d0bca52012-05-17 02:33:23 +00002973 }
glennrp6647b972015-01-12 04:39:14 +00002974
glennrpe88af772012-08-22 13:59:50 +00002975 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0a5b92c2014-03-16 22:08:03 +00002976 " image->colorspace=%d",(int) image->colorspace);
glennrpa6c5d342012-05-14 13:21:17 +00002977
glennrpfaa852b2010-03-30 12:17:00 +00002978 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrp32340ff2012-08-25 17:36:19 +00002979 ((int) ping_bit_depth < 16 &&
2980 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002981 {
cristybefe4d22010-06-07 01:18:58 +00002982 size_t
2983 one;
2984
cristy3ed852e2009-09-05 21:47:34 +00002985 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002986 one=1;
glennrpfcf06162012-11-05 14:57:08 +00002987 image->colors=one << ping_file_depth;
cristy3ed852e2009-09-05 21:47:34 +00002988#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2989 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002990 image->colors=256;
2991#else
2992 if (image->colors > 65536L)
2993 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002994#endif
glennrpfaa852b2010-03-30 12:17:00 +00002995 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002996 {
cristy3ed852e2009-09-05 21:47:34 +00002997 png_colorp
2998 palette;
2999
3000 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00003001 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00003002
cristy3ed852e2009-09-05 21:47:34 +00003003 if (logging != MagickFalse)
3004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3005 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3006 }
3007 }
3008
3009 if (image->storage_class == PseudoClass)
3010 {
3011 /*
3012 Initialize image colormap.
3013 */
cristy16ea1392012-03-21 20:38:41 +00003014 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
glennrpedaa0382012-04-12 14:16:21 +00003015 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003016
glennrpfaa852b2010-03-30 12:17:00 +00003017 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00003018 {
cristy3ed852e2009-09-05 21:47:34 +00003019 png_colorp
3020 palette;
3021
3022 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00003023
glennrp6af6cf12011-04-22 13:05:16 +00003024 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00003025 {
3026 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3027 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3028 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3029 }
glennrp6af6cf12011-04-22 13:05:16 +00003030
glennrp67b9c1a2011-04-22 18:47:36 +00003031 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00003032 {
3033 image->colormap[i].red=0;
3034 image->colormap[i].green=0;
3035 image->colormap[i].blue=0;
3036 }
cristy3ed852e2009-09-05 21:47:34 +00003037 }
glennrp0fe50b42010-11-16 03:52:51 +00003038
cristy3ed852e2009-09-05 21:47:34 +00003039 else
3040 {
glennrp9b034ff2015-03-16 14:36:44 +00003041 Quantum
cristy3ed852e2009-09-05 21:47:34 +00003042 scale;
3043
cristyd7c4d1c2015-03-22 17:16:52 +00003044 scale = (Quantum) (65535UL)/((1UL << ping_file_depth)-1);
glennrp0fe50b42010-11-16 03:52:51 +00003045
glennrp9b034ff2015-03-16 14:36:44 +00003046#if (MAGICKCORE_QUANTUM_DEPTH > 16)
3047 scale = ScaleShortToQuantum(scale);
3048#endif
glennrp0fe50b42010-11-16 03:52:51 +00003049
cristybb503372010-05-27 20:51:26 +00003050 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00003051 {
3052 image->colormap[i].red=(Quantum) (i*scale);
3053 image->colormap[i].green=(Quantum) (i*scale);
3054 image->colormap[i].blue=(Quantum) (i*scale);
3055 }
3056 }
3057 }
glennrp147bc912011-03-30 18:47:21 +00003058
glennrpcb395ac2011-03-30 19:50:23 +00003059 /* Set some properties for reporting by "identify" */
3060 {
glennrp147bc912011-03-30 18:47:21 +00003061 char
cristy151b66d2015-04-15 10:50:31 +00003062 msg[MagickPathExtent];
glennrp147bc912011-03-30 18:47:21 +00003063
glennrpfcf06162012-11-05 14:57:08 +00003064 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
glennrp147bc912011-03-30 18:47:21 +00003065 ping_interlace_method in value */
3066
cristy151b66d2015-04-15 10:50:31 +00003067 (void) FormatLocaleString(msg,MagickPathExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00003068 "%d, %d",(int) ping_width, (int) ping_height);
glennrp3398b5b2013-05-03 23:10:31 +00003069 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00003070
cristy151b66d2015-04-15 10:50:31 +00003071 (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_file_depth);
glennrp3398b5b2013-05-03 23:10:31 +00003072 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00003073
cristy151b66d2015-04-15 10:50:31 +00003074 (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
glennrp5dff4352012-06-06 22:12:04 +00003075 (int) ping_color_type,
3076 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
glennrp3398b5b2013-05-03 23:10:31 +00003077 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00003078
glennrp913f9612012-06-27 17:50:00 +00003079 if (ping_interlace_method == 0)
3080 {
cristy151b66d2015-04-15 10:50:31 +00003081 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
glennrp913f9612012-06-27 17:50:00 +00003082 (int) ping_interlace_method);
3083 }
3084 else if (ping_interlace_method == 1)
3085 {
cristy151b66d2015-04-15 10:50:31 +00003086 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
glennrp913f9612012-06-27 17:50:00 +00003087 (int) ping_interlace_method);
3088 }
3089 else
3090 {
cristy151b66d2015-04-15 10:50:31 +00003091 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
glennrp913f9612012-06-27 17:50:00 +00003092 (int) ping_interlace_method);
3093 }
3094 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3095
3096 if (number_colors != 0)
3097 {
cristy151b66d2015-04-15 10:50:31 +00003098 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
glennrp913f9612012-06-27 17:50:00 +00003099 (int) number_colors);
glennrp3398b5b2013-05-03 23:10:31 +00003100 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
glennrp913f9612012-06-27 17:50:00 +00003101 exception);
3102 }
glennrpcb395ac2011-03-30 19:50:23 +00003103 }
glennrp39d99e12014-10-26 20:57:13 +00003104#if defined(PNG_tIME_SUPPORTED)
3105 read_tIME_chunk(image,ping,ping_info,exception);
3106#endif
3107
glennrp147bc912011-03-30 18:47:21 +00003108
cristy3ed852e2009-09-05 21:47:34 +00003109 /*
3110 Read image scanlines.
3111 */
3112 if (image->delay != 0)
3113 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00003114
glennrp0ca69b12010-07-26 01:57:52 +00003115 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00003116 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3117 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00003118 {
glennrp1b888c42012-03-02 15:18:14 +00003119 /* This happens later in non-ping decodes */
3120 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3121 image->storage_class=DirectClass;
3122
cristy3ed852e2009-09-05 21:47:34 +00003123 if (logging != MagickFalse)
3124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003125 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003126 mng_info->scenes_found-1);
3127 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00003128
glennrp868fff32014-03-16 22:09:06 +00003129#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00003130 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003131#endif
glennrpedaa0382012-04-12 14:16:21 +00003132
cristy3ed852e2009-09-05 21:47:34 +00003133 if (logging != MagickFalse)
3134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3135 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00003136
cristy3ed852e2009-09-05 21:47:34 +00003137 return(image);
3138 }
glennrp0fe50b42010-11-16 03:52:51 +00003139
cristy3ed852e2009-09-05 21:47:34 +00003140 if (logging != MagickFalse)
3141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3142 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00003143
cristy3ed852e2009-09-05 21:47:34 +00003144 if (num_passes > 1)
cristy09973322013-06-30 21:06:30 +00003145 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
glennrpcf002022011-01-30 02:38:15 +00003146 sizeof(*ping_pixels));
cristy09973322013-06-30 21:06:30 +00003147 else
3148 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00003149
cristy09973322013-06-30 21:06:30 +00003150 if (pixel_info == (MemoryInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003151 png_error(ping,"Memory allocation failed");
cristy09973322013-06-30 21:06:30 +00003152 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
glennrp0fe50b42010-11-16 03:52:51 +00003153
cristy3ed852e2009-09-05 21:47:34 +00003154 if (logging != MagickFalse)
3155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3156 " Converting PNG pixels to pixel packets");
3157 /*
3158 Convert PNG pixels to pixel packets.
3159 */
cristy5f766ef2014-12-14 21:12:47 +00003160 quantum_info=AcquireQuantumInfo(image_info,image);
cristy16ea1392012-03-21 20:38:41 +00003161
3162 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003163 png_error(ping,"Failed to allocate quantum_info");
glennrp0fe50b42010-11-16 03:52:51 +00003164
glennrp4b840d72012-11-22 16:01:16 +00003165 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3166
glennrpc8cbc5d2011-01-01 00:12:34 +00003167 {
3168
3169 MagickBooleanType
3170 found_transparent_pixel;
3171
3172 found_transparent_pixel=MagickFalse;
3173
cristy3ed852e2009-09-05 21:47:34 +00003174 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00003175 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003176 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00003177 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003178 /*
3179 Convert image to DirectClass pixel packets.
3180 */
glennrpccc36af2014-09-08 23:22:03 +00003181 image->alpha_trait=
3182 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
glennrpc8cbc5d2011-01-01 00:12:34 +00003183 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3184 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
cristyb0a657e2012-08-29 00:45:37 +00003185 BlendPixelTrait : UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00003186
glennrpc8cbc5d2011-01-01 00:12:34 +00003187 for (y=0; y < (ssize_t) image->rows; y++)
3188 {
3189 if (num_passes > 1)
3190 row_offset=ping_rowbytes*y;
3191
3192 else
3193 row_offset=0;
3194
glennrpcf002022011-01-30 02:38:15 +00003195 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc5c31be2012-11-07 18:42:18 +00003196
3197 if (pass < num_passes-1)
3198 continue;
3199
cristy862a33c2012-05-17 22:49:37 +00003200 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003201
cristy16ea1392012-03-21 20:38:41 +00003202 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00003203 break;
3204
cristy16ea1392012-03-21 20:38:41 +00003205 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3206 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3207 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003208
cristy16ea1392012-03-21 20:38:41 +00003209 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3210 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3211 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003212
cristy16ea1392012-03-21 20:38:41 +00003213 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3214 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3215 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003216
cristy16ea1392012-03-21 20:38:41 +00003217 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3218 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3219 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003220
cristy16ea1392012-03-21 20:38:41 +00003221 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3222 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3223 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00003224
glennrpc8cbc5d2011-01-01 00:12:34 +00003225 if (found_transparent_pixel == MagickFalse)
3226 {
3227 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00003228 if (y== 0 && logging != MagickFalse)
3229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3230 " Looking for cheap transparent pixel");
3231
glennrpc8cbc5d2011-01-01 00:12:34 +00003232 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3233 {
glennrp5aa37f62011-01-02 03:07:57 +00003234 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3235 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy16ea1392012-03-21 20:38:41 +00003236 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00003237 {
glennrpa6a06632011-01-19 15:15:34 +00003238 if (logging != MagickFalse)
3239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3240 " ...got one.");
3241
glennrpc8cbc5d2011-01-01 00:12:34 +00003242 found_transparent_pixel = MagickTrue;
3243 break;
3244 }
glennrp4f25bd02011-01-01 18:51:28 +00003245 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3246 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
cristy16ea1392012-03-21 20:38:41 +00003247 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3248 transparent_color.red &&
3249 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3250 transparent_color.green &&
3251 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3252 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00003253 {
glennrpa6a06632011-01-19 15:15:34 +00003254 if (logging != MagickFalse)
3255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3256 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00003257 found_transparent_pixel = MagickTrue;
3258 break;
3259 }
cristy16ea1392012-03-21 20:38:41 +00003260 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00003261 }
3262 }
3263
glennrpaf932042014-11-29 20:44:25 +00003264 if (num_passes == 1)
glennrpc8cbc5d2011-01-01 00:12:34 +00003265 {
glennrpaf932042014-11-29 20:44:25 +00003266 status=SetImageProgress(image,LoadImageTag,
3267 (MagickOffsetType) y, image->rows);
glennrpc8cbc5d2011-01-01 00:12:34 +00003268
3269 if (status == MagickFalse)
3270 break;
3271 }
3272 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3273 break;
3274 }
3275
glennrpaf932042014-11-29 20:44:25 +00003276 if (num_passes != 1)
glennrpc8cbc5d2011-01-01 00:12:34 +00003277 {
3278 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003279 if (status == MagickFalse)
3280 break;
3281 }
cristy3ed852e2009-09-05 21:47:34 +00003282 }
cristy3ed852e2009-09-05 21:47:34 +00003283 }
glennrp0fe50b42010-11-16 03:52:51 +00003284
cristy3ed852e2009-09-05 21:47:34 +00003285 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003286
cristy3ed852e2009-09-05 21:47:34 +00003287 for (pass=0; pass < num_passes; pass++)
3288 {
3289 Quantum
3290 *quantum_scanline;
3291
3292 register Quantum
3293 *r;
3294
3295 /*
3296 Convert grayscale image to PseudoClass pixel packets.
3297 */
glennrpc17d96f2011-06-27 01:20:11 +00003298 if (logging != MagickFalse)
3299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3300 " Converting grayscale pixels to pixel packets");
cristy16ea1392012-03-21 20:38:41 +00003301
cristy8a46d822012-08-28 23:32:39 +00003302 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristyb0a657e2012-08-29 00:45:37 +00003303 BlendPixelTrait : UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00003304
cristy3ed852e2009-09-05 21:47:34 +00003305 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
cristyb0a657e2012-08-29 00:45:37 +00003306 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3307 sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003308
cristy3ed852e2009-09-05 21:47:34 +00003309 if (quantum_scanline == (Quantum *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003310 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003311
cristybb503372010-05-27 20:51:26 +00003312 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003313 {
glennrp4f996392013-01-26 22:56:50 +00003314 Quantum
3315 alpha;
3316
cristy3ed852e2009-09-05 21:47:34 +00003317 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003318 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003319
cristy3ed852e2009-09-05 21:47:34 +00003320 else
3321 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003322
glennrpcf002022011-01-30 02:38:15 +00003323 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc5c31be2012-11-07 18:42:18 +00003324
3325 if (pass < num_passes-1)
3326 continue;
3327
glennrp4f996392013-01-26 22:56:50 +00003328 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003329
cristy16ea1392012-03-21 20:38:41 +00003330 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003331 break;
glennrp0fe50b42010-11-16 03:52:51 +00003332
glennrpcf002022011-01-30 02:38:15 +00003333 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003334 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003335
glennrpfaa852b2010-03-30 12:17:00 +00003336 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003337 {
cristy3ed852e2009-09-05 21:47:34 +00003338 case 8:
3339 {
glennrp4f996392013-01-26 22:56:50 +00003340
glennrpfaa852b2010-03-30 12:17:00 +00003341 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003342 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003343 {
glennrpa18d5bc2011-04-23 14:51:34 +00003344 *r++=*p++;
glennrp4f996392013-01-26 22:56:50 +00003345
3346 alpha=ScaleCharToQuantum((unsigned char)*p++);
3347
3348 SetPixelAlpha(image,alpha,q);
3349
3350 if (alpha != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003351 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003352
cristy16ea1392012-03-21 20:38:41 +00003353 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003354 }
glennrp0fe50b42010-11-16 03:52:51 +00003355
cristy3ed852e2009-09-05 21:47:34 +00003356 else
cristybb503372010-05-27 20:51:26 +00003357 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003358 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003359
cristy3ed852e2009-09-05 21:47:34 +00003360 break;
3361 }
glennrp47b9dd52010-11-24 18:12:06 +00003362
cristy3ed852e2009-09-05 21:47:34 +00003363 case 16:
3364 {
cristybb503372010-05-27 20:51:26 +00003365 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003366 {
glennrp9b034ff2015-03-16 14:36:44 +00003367#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
cristy87281ec2013-05-08 11:50:14 +00003368 unsigned short
glennrp58f77c72011-04-23 14:09:09 +00003369 quantum;
3370
3371 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003372 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003373
3374 else
glennrpc17d96f2011-06-27 01:20:11 +00003375 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003376
glennrp58f77c72011-04-23 14:09:09 +00003377 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003378 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003379 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003380
3381 if (ping_color_type == 4)
3382 {
glennrpc17d96f2011-06-27 01:20:11 +00003383 if (image->colors > 256)
3384 quantum=((*p++) << 8);
3385 else
3386 quantum=0;
3387
glennrp58f77c72011-04-23 14:09:09 +00003388 quantum|=(*p++);
glennrp4f996392013-01-26 22:56:50 +00003389
3390 alpha=ScaleShortToQuantum(quantum);
3391 SetPixelAlpha(image,alpha,q);
3392
3393 if (alpha != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003394 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003395
cristy16ea1392012-03-21 20:38:41 +00003396 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003397 }
glennrp58f77c72011-04-23 14:09:09 +00003398
3399#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3400 *r++=(*p++);
3401 p++; /* strip low byte */
3402
3403 if (ping_color_type == 4)
3404 {
cristy16ea1392012-03-21 20:38:41 +00003405 SetPixelAlpha(image,*p++,q);
glennrp4f996392013-01-26 22:56:50 +00003406
cristy16ea1392012-03-21 20:38:41 +00003407 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003408 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003409
glennrp9d0ea4d2011-04-22 18:35:57 +00003410 p++;
cristy16ea1392012-03-21 20:38:41 +00003411 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003412 }
cristy3ed852e2009-09-05 21:47:34 +00003413#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003414 }
glennrp47b9dd52010-11-24 18:12:06 +00003415
cristy3ed852e2009-09-05 21:47:34 +00003416 break;
3417 }
glennrp47b9dd52010-11-24 18:12:06 +00003418
cristy3ed852e2009-09-05 21:47:34 +00003419 default:
3420 break;
3421 }
glennrp3faa9a32011-04-23 14:00:25 +00003422
cristy3ed852e2009-09-05 21:47:34 +00003423 /*
3424 Transfer image scanline.
3425 */
3426 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003427
cristy16ea1392012-03-21 20:38:41 +00003428 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3429
3430 if (q == (Quantum *) NULL)
3431 break;
cristybb503372010-05-27 20:51:26 +00003432 for (x=0; x < (ssize_t) image->columns; x++)
cristy16ea1392012-03-21 20:38:41 +00003433 {
3434 SetPixelIndex(image,*r++,q);
3435 q+=GetPixelChannels(image);
3436 }
glennrp0fe50b42010-11-16 03:52:51 +00003437
cristy3ed852e2009-09-05 21:47:34 +00003438 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3439 break;
glennrp0fe50b42010-11-16 03:52:51 +00003440
glennrpaf932042014-11-29 20:44:25 +00003441 if (num_passes == 1)
cristy7a287bf2010-02-14 02:18:09 +00003442 {
cristycee97112010-05-28 00:44:52 +00003443 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003444 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003445
cristy7a287bf2010-02-14 02:18:09 +00003446 if (status == MagickFalse)
3447 break;
3448 }
cristy3ed852e2009-09-05 21:47:34 +00003449 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003450
glennrpaf932042014-11-29 20:44:25 +00003451 if (num_passes != 1)
cristy3ed852e2009-09-05 21:47:34 +00003452 {
3453 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003454
cristy3ed852e2009-09-05 21:47:34 +00003455 if (status == MagickFalse)
3456 break;
3457 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003458
cristy3ed852e2009-09-05 21:47:34 +00003459 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3460 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003461
cristyb0a657e2012-08-29 00:45:37 +00003462 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3463 UndefinedPixelTrait;
glennrpc8cbc5d2011-01-01 00:12:34 +00003464
3465 if (logging != MagickFalse)
3466 {
3467 if (found_transparent_pixel != MagickFalse)
3468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3469 " Found transparent pixel");
3470 else
glennrp5aa37f62011-01-02 03:07:57 +00003471 {
3472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3473 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003474
glennrp5aa37f62011-01-02 03:07:57 +00003475 ping_color_type&=0x03;
3476 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003477 }
3478 }
3479
cristy16ea1392012-03-21 20:38:41 +00003480 if (quantum_info != (QuantumInfo *) NULL)
3481 quantum_info=DestroyQuantumInfo(quantum_info);
3482
cristy5c6f7892010-05-05 22:53:29 +00003483 if (image->storage_class == PseudoClass)
3484 {
cristyb0a657e2012-08-29 00:45:37 +00003485 PixelTrait
3486 alpha_trait;
cristy5c6f7892010-05-05 22:53:29 +00003487
cristyb0a657e2012-08-29 00:45:37 +00003488 alpha_trait=image->alpha_trait;
cristy8a46d822012-08-28 23:32:39 +00003489 image->alpha_trait=UndefinedPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003490 (void) SyncImage(image,exception);
cristyb0a657e2012-08-29 00:45:37 +00003491 image->alpha_trait=alpha_trait;
cristy5c6f7892010-05-05 22:53:29 +00003492 }
glennrp47b9dd52010-11-24 18:12:06 +00003493
glennrp4eb39312011-03-30 21:34:55 +00003494 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003495
glennrpccc36af2014-09-08 23:22:03 +00003496 if (logging != MagickFalse)
3497 {
3498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3499 " image->storage_class=%d\n",(int) image->storage_class);
3500 }
3501
cristy3ed852e2009-09-05 21:47:34 +00003502 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003503 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003504 {
3505 png_destroy_read_struct(&ping,&ping_info,&end_info);
cristy09973322013-06-30 21:06:30 +00003506 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00003507 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00003508 (void) SetImageBackgroundColor(image,exception);
glennrp868fff32014-03-16 22:09:06 +00003509#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00003510 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003511#endif
3512 if (logging != MagickFalse)
3513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3514 " exit ReadOnePNGImage() early.");
3515 return(image);
3516 }
glennrp47b9dd52010-11-24 18:12:06 +00003517
glennrpfaa852b2010-03-30 12:17:00 +00003518 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003519 {
3520 ClassType
3521 storage_class;
3522
3523 /*
3524 Image has a transparent background.
3525 */
3526 storage_class=image->storage_class;
cristy8a46d822012-08-28 23:32:39 +00003527 image->alpha_trait=BlendPixelTrait;
glennrpc11cf6a2010-03-20 16:46:19 +00003528
glennrp3c218112010-11-27 15:31:26 +00003529/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003530
glennrp0fe50b42010-11-16 03:52:51 +00003531 if (storage_class == PseudoClass)
3532 {
3533 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003534 {
glennrp0fe50b42010-11-16 03:52:51 +00003535 for (x=0; x < ping_num_trans; x++)
3536 {
cristy8a46d822012-08-28 23:32:39 +00003537 image->colormap[x].alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003538 image->colormap[x].alpha =
3539 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003540 }
glennrpc11cf6a2010-03-20 16:46:19 +00003541 }
glennrp47b9dd52010-11-24 18:12:06 +00003542
glennrp0fe50b42010-11-16 03:52:51 +00003543 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3544 {
3545 for (x=0; x < (int) image->colors; x++)
3546 {
3547 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy16ea1392012-03-21 20:38:41 +00003548 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003549 {
cristy8a46d822012-08-28 23:32:39 +00003550 image->colormap[x].alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003551 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003552 }
3553 }
3554 }
cristy16ea1392012-03-21 20:38:41 +00003555 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003556 }
glennrp47b9dd52010-11-24 18:12:06 +00003557
glennrpa6a06632011-01-19 15:15:34 +00003558#if 1 /* Should have already been done above, but glennrp problem P10
3559 * needs this.
3560 */
glennrp0fe50b42010-11-16 03:52:51 +00003561 else
3562 {
3563 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003564 {
glennrp0fe50b42010-11-16 03:52:51 +00003565 image->storage_class=storage_class;
3566 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3567
cristy16ea1392012-03-21 20:38:41 +00003568 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003569 break;
3570
glennrp0fe50b42010-11-16 03:52:51 +00003571
glennrpa6a06632011-01-19 15:15:34 +00003572 /* Caution: on a Q8 build, this does not distinguish between
3573 * 16-bit colors that differ only in the low byte
3574 */
glennrp0fe50b42010-11-16 03:52:51 +00003575 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3576 {
cristy16ea1392012-03-21 20:38:41 +00003577 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3578 transparent_color.red &&
3579 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3580 transparent_color.green &&
3581 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3582 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003583 {
cristy16ea1392012-03-21 20:38:41 +00003584 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003585 }
glennrp0fe50b42010-11-16 03:52:51 +00003586
glennrp67b9c1a2011-04-22 18:47:36 +00003587#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003588 else
glennrp4f25bd02011-01-01 18:51:28 +00003589 {
cristy16ea1392012-03-21 20:38:41 +00003590 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003591 }
glennrpa6a06632011-01-19 15:15:34 +00003592#endif
glennrp0fe50b42010-11-16 03:52:51 +00003593
cristy16ea1392012-03-21 20:38:41 +00003594 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003595 }
3596
3597 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3598 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003599 }
glennrp0fe50b42010-11-16 03:52:51 +00003600 }
glennrpa6a06632011-01-19 15:15:34 +00003601#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003602
cristy3ed852e2009-09-05 21:47:34 +00003603 image->storage_class=DirectClass;
3604 }
glennrp3c218112010-11-27 15:31:26 +00003605
cristyeb3b22a2011-03-31 20:16:11 +00003606 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003607 {
3608 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003609 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3610 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003611 else
glennrpa0ed0092011-04-18 16:36:29 +00003612 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3613 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003614
glennrp4eb39312011-03-30 21:34:55 +00003615 if (status != MagickFalse)
3616 for (i=0; i < (ssize_t) num_text; i++)
3617 {
3618 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003619
glennrp4eb39312011-03-30 21:34:55 +00003620 if (logging != MagickFalse)
3621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3622 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003623
glennrp5285ae12014-12-31 17:38:12 +00003624 if (strlen(text[i].key) > 16 &&
glennrp6dfc2662014-12-31 17:45:09 +00003625 memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003626 {
glennrp99f96812014-06-30 02:57:09 +00003627 const char
3628 *value;
3629
3630 value=GetImageOption(image_info,"profile:skip");
3631
3632 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3633 {
3634 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3635 (int) i,exception);
3636 num_raw_profiles++;
3637 if (logging != MagickFalse)
3638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3639 " Read raw profile %s",text[i].key+17);
3640 }
3641 else
3642 {
3643 if (logging != MagickFalse)
3644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3645 " Skipping raw profile %s",text[i].key+17);
3646 }
glennrp97f90e22011-02-22 05:47:58 +00003647 }
glennrp0fe50b42010-11-16 03:52:51 +00003648
glennrp4eb39312011-03-30 21:34:55 +00003649 else
3650 {
3651 char
3652 *value;
3653
3654 length=text[i].text_length;
cristy151b66d2015-04-15 10:50:31 +00003655 value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
glennrp4eb39312011-03-30 21:34:55 +00003656 sizeof(*value));
3657 if (value == (char *) NULL)
3658 {
glennrpedaa0382012-04-12 14:16:21 +00003659 png_error(ping,"Memory allocation failed");
glennrp4eb39312011-03-30 21:34:55 +00003660 break;
3661 }
3662 *value='\0';
3663 (void) ConcatenateMagickString(value,text[i].text,length+2);
3664
3665 /* Don't save "density" or "units" property if we have a pHYs
3666 * chunk
3667 */
3668 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3669 (LocaleCompare(text[i].key,"density") != 0 &&
3670 LocaleCompare(text[i].key,"units") != 0))
cristy16ea1392012-03-21 20:38:41 +00003671 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003672
3673 if (logging != MagickFalse)
3674 {
3675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00003676 " length: %lu\n"
3677 " Keyword: %s",
3678 (unsigned long) length,
3679 text[i].key);
glennrp4eb39312011-03-30 21:34:55 +00003680 }
3681
3682 value=DestroyString(value);
3683 }
3684 }
dirkfd6fd072014-10-24 21:10:08 +00003685 num_text_total += num_text;
3686 }
glennrp3c218112010-11-27 15:31:26 +00003687
cristy3ed852e2009-09-05 21:47:34 +00003688#ifdef MNG_OBJECT_BUFFERS
3689 /*
3690 Store the object if necessary.
3691 */
3692 if (object_id && !mng_info->frozen[object_id])
3693 {
3694 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3695 {
3696 /*
3697 create a new object buffer.
3698 */
3699 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003700 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003701
cristy3ed852e2009-09-05 21:47:34 +00003702 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3703 {
3704 mng_info->ob[object_id]->image=(Image *) NULL;
3705 mng_info->ob[object_id]->reference_count=1;
3706 }
3707 }
glennrp47b9dd52010-11-24 18:12:06 +00003708
cristy3ed852e2009-09-05 21:47:34 +00003709 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3710 mng_info->ob[object_id]->frozen)
3711 {
3712 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003713 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003714
cristy3ed852e2009-09-05 21:47:34 +00003715 if (mng_info->ob[object_id]->frozen)
glennrpedaa0382012-04-12 14:16:21 +00003716 png_error(ping,"Cannot overwrite frozen MNG object buffer");
cristy3ed852e2009-09-05 21:47:34 +00003717 }
glennrp0fe50b42010-11-16 03:52:51 +00003718
cristy3ed852e2009-09-05 21:47:34 +00003719 else
3720 {
cristy3ed852e2009-09-05 21:47:34 +00003721
3722 if (mng_info->ob[object_id]->image != (Image *) NULL)
3723 mng_info->ob[object_id]->image=DestroyImage
3724 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003725
cristy3ed852e2009-09-05 21:47:34 +00003726 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristy16ea1392012-03-21 20:38:41 +00003727 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003728
cristy3ed852e2009-09-05 21:47:34 +00003729 if (mng_info->ob[object_id]->image != (Image *) NULL)
3730 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003731
cristy3ed852e2009-09-05 21:47:34 +00003732 else
glennrpedaa0382012-04-12 14:16:21 +00003733 png_error(ping, "Cloning image for object buffer failed");
glennrp0fe50b42010-11-16 03:52:51 +00003734
glennrpfaa852b2010-03-30 12:17:00 +00003735 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003736 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003737
glennrpfaa852b2010-03-30 12:17:00 +00003738 mng_info->ob[object_id]->width=ping_width;
3739 mng_info->ob[object_id]->height=ping_height;
3740 mng_info->ob[object_id]->color_type=ping_color_type;
3741 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3742 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3743 mng_info->ob[object_id]->compression_method=
3744 ping_compression_method;
3745 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003746
glennrpfaa852b2010-03-30 12:17:00 +00003747 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003748 {
cristy3ed852e2009-09-05 21:47:34 +00003749 png_colorp
3750 plte;
3751
3752 /*
3753 Copy the PLTE to the object buffer.
3754 */
3755 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3756 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003757
cristy3ed852e2009-09-05 21:47:34 +00003758 for (i=0; i < number_colors; i++)
3759 {
3760 mng_info->ob[object_id]->plte[i]=plte[i];
3761 }
3762 }
glennrp47b9dd52010-11-24 18:12:06 +00003763
cristy3ed852e2009-09-05 21:47:34 +00003764 else
3765 mng_info->ob[object_id]->plte_length=0;
3766 }
3767 }
3768#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003769
cristy8a46d822012-08-28 23:32:39 +00003770 /* Set image->alpha_trait to MagickTrue if the input colortype supports
glennrp0a55b4c2011-03-23 12:38:38 +00003771 * alpha or if a valid tRNS chunk is present, no matter whether there
3772 * is actual transparency present.
3773 */
cristy8a46d822012-08-28 23:32:39 +00003774 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
glennrp0a55b4c2011-03-23 12:38:38 +00003775 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3776 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
cristyb0a657e2012-08-29 00:45:37 +00003777 BlendPixelTrait : UndefinedPixelTrait;
glennrp0a55b4c2011-03-23 12:38:38 +00003778
glennrp224ad8d2013-02-01 17:04:04 +00003779#if 0 /* I'm not sure what's wrong here but it does not work. */
cristy17f11b02014-12-20 19:37:04 +00003780 if (image->alpha_trait != UndefinedPixelTrait)
glennrp5830fbc2013-01-27 06:11:45 +00003781 {
3782 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
cristydef23e52015-01-22 11:52:01 +00003783 (void) SetImageType(image,GrayscaleAlphaType,exception);
glennrp5830fbc2013-01-27 06:11:45 +00003784
3785 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristydef23e52015-01-22 11:52:01 +00003786 (void) SetImageType(image,PaletteAlphaType,exception);
glennrp5830fbc2013-01-27 06:11:45 +00003787
3788 else
cristydef23e52015-01-22 11:52:01 +00003789 (void) SetImageType(image,TrueColorAlphaType,exception);
glennrp5830fbc2013-01-27 06:11:45 +00003790 }
3791
3792 else
3793 {
3794 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3795 (void) SetImageType(image,GrayscaleType,exception);
3796
3797 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3798 (void) SetImageType(image,PaletteType,exception);
3799
3800 else
3801 (void) SetImageType(image,TrueColorType,exception);
3802 }
glennrp224ad8d2013-02-01 17:04:04 +00003803#endif
glennrp5830fbc2013-01-27 06:11:45 +00003804
glennrpcb395ac2011-03-30 19:50:23 +00003805 /* Set more properties for identify to retrieve */
3806 {
3807 char
cristy151b66d2015-04-15 10:50:31 +00003808 msg[MagickPathExtent];
glennrpcb395ac2011-03-30 19:50:23 +00003809
glennrp4eb39312011-03-30 21:34:55 +00003810 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003811 {
3812 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy151b66d2015-04-15 10:50:31 +00003813 (void) FormatLocaleString(msg,MagickPathExtent,
glennrp613276d2011-03-30 21:46:49 +00003814 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrp3398b5b2013-05-03 23:10:31 +00003815 (void) SetImageProperty(image,"png:text",msg,
cristy16ea1392012-03-21 20:38:41 +00003816 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003817 }
3818
3819 if (num_raw_profiles != 0)
3820 {
cristy151b66d2015-04-15 10:50:31 +00003821 (void) FormatLocaleString(msg,MagickPathExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003822 "%d were found", num_raw_profiles);
cristy16ea1392012-03-21 20:38:41 +00003823 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3824 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003825 }
3826
glennrp98b83d42012-07-23 02:50:31 +00003827 if (ping_found_cHRM != MagickFalse)
glennrp59612252011-03-30 21:45:21 +00003828 {
cristy151b66d2015-04-15 10:50:31 +00003829 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003830 "chunk was found (see Chromaticity, above)");
glennrp3398b5b2013-05-03 23:10:31 +00003831 (void) SetImageProperty(image,"png:cHRM",msg,
cristy16ea1392012-03-21 20:38:41 +00003832 exception);
glennrp59612252011-03-30 21:45:21 +00003833 }
glennrpcb395ac2011-03-30 19:50:23 +00003834
3835 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003836 {
cristy151b66d2015-04-15 10:50:31 +00003837 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003838 "chunk was found (see Background color, above)");
glennrp3398b5b2013-05-03 23:10:31 +00003839 (void) SetImageProperty(image,"png:bKGD",msg,
cristy16ea1392012-03-21 20:38:41 +00003840 exception);
glennrp59612252011-03-30 21:45:21 +00003841 }
3842
cristy151b66d2015-04-15 10:50:31 +00003843 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003844 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003845
glennrp98b83d42012-07-23 02:50:31 +00003846#if defined(PNG_iCCP_SUPPORTED)
3847 if (ping_found_iCCP != MagickFalse)
glennrp3398b5b2013-05-03 23:10:31 +00003848 (void) SetImageProperty(image,"png:iCCP",msg,
cristy16ea1392012-03-21 20:38:41 +00003849 exception);
glennrp98b83d42012-07-23 02:50:31 +00003850#endif
glennrpcb395ac2011-03-30 19:50:23 +00003851
glennrpcb395ac2011-03-30 19:50:23 +00003852 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrp3398b5b2013-05-03 23:10:31 +00003853 (void) SetImageProperty(image,"png:tRNS",msg,
cristy16ea1392012-03-21 20:38:41 +00003854 exception);
glennrp4eb39312011-03-30 21:34:55 +00003855
3856#if defined(PNG_sRGB_SUPPORTED)
glennrp98b83d42012-07-23 02:50:31 +00003857 if (ping_found_sRGB != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003858 {
cristy151b66d2015-04-15 10:50:31 +00003859 (void) FormatLocaleString(msg,MagickPathExtent,
glennrp98b83d42012-07-23 02:50:31 +00003860 "intent=%d (%s)",
3861 (int) intent,
3862 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
glennrp3398b5b2013-05-03 23:10:31 +00003863 (void) SetImageProperty(image,"png:sRGB",msg,
glennrp98b83d42012-07-23 02:50:31 +00003864 exception);
glennrp4eb39312011-03-30 21:34:55 +00003865 }
3866#endif
3867
glennrp98b83d42012-07-23 02:50:31 +00003868 if (ping_found_gAMA != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003869 {
cristy151b66d2015-04-15 10:50:31 +00003870 (void) FormatLocaleString(msg,MagickPathExtent,
cristy16ea1392012-03-21 20:38:41 +00003871 "gamma=%.8g (See Gamma, above)",
3872 file_gamma);
glennrp3398b5b2013-05-03 23:10:31 +00003873 (void) SetImageProperty(image,"png:gAMA",msg,
cristy16ea1392012-03-21 20:38:41 +00003874 exception);
glennrp4eb39312011-03-30 21:34:55 +00003875 }
3876
3877#if defined(PNG_pHYs_SUPPORTED)
3878 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3879 {
cristy151b66d2015-04-15 10:50:31 +00003880 (void) FormatLocaleString(msg,MagickPathExtent,
glennrp07523c72011-03-31 18:12:10 +00003881 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003882 (double) x_resolution,(double) y_resolution, unit_type);
glennrp3398b5b2013-05-03 23:10:31 +00003883 (void) SetImageProperty(image,"png:pHYs",msg,
cristy16ea1392012-03-21 20:38:41 +00003884 exception);
glennrp4eb39312011-03-30 21:34:55 +00003885 }
3886#endif
3887
3888#if defined(PNG_oFFs_SUPPORTED)
3889 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3890 {
cristy151b66d2015-04-15 10:50:31 +00003891 (void) FormatLocaleString(msg,MagickPathExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003892 (double) image->page.x,(double) image->page.y);
glennrp3398b5b2013-05-03 23:10:31 +00003893 (void) SetImageProperty(image,"png:oFFs",msg,
cristy16ea1392012-03-21 20:38:41 +00003894 exception);
glennrp4eb39312011-03-30 21:34:55 +00003895 }
3896#endif
3897
dirkfd6fd072014-10-24 21:10:08 +00003898#if defined(PNG_tIME_SUPPORTED)
3899 read_tIME_chunk(image,ping,end_info,exception);
3900#endif
3901
glennrp07523c72011-03-31 18:12:10 +00003902 if ((image->page.width != 0 && image->page.width != image->columns) ||
3903 (image->page.height != 0 && image->page.height != image->rows))
3904 {
cristy151b66d2015-04-15 10:50:31 +00003905 (void) FormatLocaleString(msg,MagickPathExtent,
glennrp07523c72011-03-31 18:12:10 +00003906 "width=%.20g, height=%.20g",
3907 (double) image->page.width,(double) image->page.height);
glennrp3398b5b2013-05-03 23:10:31 +00003908 (void) SetImageProperty(image,"png:vpAg",msg,
cristy16ea1392012-03-21 20:38:41 +00003909 exception);
glennrp07523c72011-03-31 18:12:10 +00003910 }
glennrpcb395ac2011-03-30 19:50:23 +00003911 }
3912
cristy3ed852e2009-09-05 21:47:34 +00003913 /*
3914 Relinquish resources.
3915 */
3916 png_destroy_read_struct(&ping,&ping_info,&end_info);
3917
cristy09973322013-06-30 21:06:30 +00003918 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00003919
3920 if (logging != MagickFalse)
3921 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3922 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003923
glennrp868fff32014-03-16 22:09:06 +00003924#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpedaa0382012-04-12 14:16:21 +00003925 UnlockSemaphoreInfo(ping_semaphore);
3926#endif
3927
3928 /* } for navigation to beginning of SETJMP-protected block, revert to
3929 * Throwing an Exception when an error occurs.
3930 */
3931
cristy3ed852e2009-09-05 21:47:34 +00003932 return(image);
3933
3934/* end of reading one PNG image */
3935}
3936
3937static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3938{
3939 Image
glennrp91942992014-11-25 00:46:19 +00003940 *image;
cristy3ed852e2009-09-05 21:47:34 +00003941
3942 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003943 have_mng_structure,
3944 logging,
cristy3ed852e2009-09-05 21:47:34 +00003945 status;
3946
3947 MngInfo
3948 *mng_info;
3949
3950 char
cristy151b66d2015-04-15 10:50:31 +00003951 magic_number[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00003952
cristy3ed852e2009-09-05 21:47:34 +00003953 ssize_t
3954 count;
3955
3956 /*
3957 Open image file.
3958 */
3959 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003960 assert(image_info->signature == MagickCoreSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003961
cristy3ed852e2009-09-05 21:47:34 +00003962 if (image_info->debug != MagickFalse)
3963 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3964 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003965
cristy3ed852e2009-09-05 21:47:34 +00003966 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00003967 assert(exception->signature == MagickCoreSignature);
glennrpfd05d622011-02-25 04:10:33 +00003968 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy16ea1392012-03-21 20:38:41 +00003969 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003970 mng_info=(MngInfo *) NULL;
3971 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003972
cristy3ed852e2009-09-05 21:47:34 +00003973 if (status == MagickFalse)
3974 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003975
cristy3ed852e2009-09-05 21:47:34 +00003976 /*
3977 Verify PNG signature.
3978 */
3979 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003980
glennrpdde35db2011-02-21 12:06:32 +00003981 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003982 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003983
cristy3ed852e2009-09-05 21:47:34 +00003984 /*
3985 Allocate a MngInfo structure.
3986 */
3987 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003988 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003989
cristy3ed852e2009-09-05 21:47:34 +00003990 if (mng_info == (MngInfo *) NULL)
3991 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003992
cristy3ed852e2009-09-05 21:47:34 +00003993 /*
3994 Initialize members of the MngInfo structure.
3995 */
3996 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3997 mng_info->image=image;
3998 have_mng_structure=MagickTrue;
3999
cristy3ed852e2009-09-05 21:47:34 +00004000 image=ReadOnePNGImage(mng_info,image_info,exception);
4001 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00004002
cristy3ed852e2009-09-05 21:47:34 +00004003 if (image == (Image *) NULL)
4004 {
cristy3ed852e2009-09-05 21:47:34 +00004005 if (logging != MagickFalse)
4006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4007 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004008
cristy3ed852e2009-09-05 21:47:34 +00004009 return((Image *) NULL);
4010 }
glennrp47b9dd52010-11-24 18:12:06 +00004011
cristy3ed852e2009-09-05 21:47:34 +00004012 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004013
cristy3ed852e2009-09-05 21:47:34 +00004014 if ((image->columns == 0) || (image->rows == 0))
4015 {
4016 if (logging != MagickFalse)
4017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4018 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00004019
cristy3ed852e2009-09-05 21:47:34 +00004020 ThrowReaderException(CorruptImageError,"CorruptImage");
4021 }
glennrp47b9dd52010-11-24 18:12:06 +00004022
cristy72715f52012-06-26 17:55:16 +00004023 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
glennrp3d627862013-02-26 00:19:34 +00004024 ((image->gamma < .45) || (image->gamma > .46)) &&
4025 !(image->chromaticity.red_primary.x>0.6399f &&
4026 image->chromaticity.red_primary.x<0.6401f &&
4027 image->chromaticity.red_primary.y>0.3299f &&
4028 image->chromaticity.red_primary.y<0.3301f &&
4029 image->chromaticity.green_primary.x>0.2999f &&
4030 image->chromaticity.green_primary.x<0.3001f &&
4031 image->chromaticity.green_primary.y>0.5999f &&
4032 image->chromaticity.green_primary.y<0.6001f &&
4033 image->chromaticity.blue_primary.x>0.1499f &&
4034 image->chromaticity.blue_primary.x<0.1501f &&
4035 image->chromaticity.blue_primary.y>0.0599f &&
4036 image->chromaticity.blue_primary.y<0.0601f &&
4037 image->chromaticity.white_point.x>0.3126f &&
4038 image->chromaticity.white_point.x<0.3128f &&
4039 image->chromaticity.white_point.y>0.3289f &&
4040 image->chromaticity.white_point.y<0.3291f))
glennrpccc36af2014-09-08 23:22:03 +00004041 {
4042 SetImageColorspace(image,RGBColorspace,exception);
4043 }
cristy72715f52012-06-26 17:55:16 +00004044
cristy3ed852e2009-09-05 21:47:34 +00004045 if (logging != MagickFalse)
glennrpccc36af2014-09-08 23:22:03 +00004046 {
4047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4048 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4049 (double) image->page.width,(double) image->page.height,
4050 (double) image->page.x,(double) image->page.y);
4051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4052 " image->colorspace: %d", (int) image->colorspace);
4053 }
glennrp97f90e22011-02-22 05:47:58 +00004054
4055 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004057
cristy3ed852e2009-09-05 21:47:34 +00004058 return(image);
4059}
4060
4061
4062
4063#if defined(JNG_SUPPORTED)
4064/*
4065%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4066% %
4067% %
4068% %
4069% R e a d O n e J N G I m a g e %
4070% %
4071% %
4072% %
4073%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4074%
4075% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4076% (minus the 8-byte signature) and returns it. It allocates the memory
4077% necessary for the new Image structure and returns a pointer to the new
4078% image.
4079%
4080% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4081%
4082% The format of the ReadOneJNGImage method is:
4083%
4084% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4085% ExceptionInfo *exception)
4086%
4087% A description of each parameter follows:
4088%
4089% o mng_info: Specifies a pointer to a MngInfo structure.
4090%
4091% o image_info: the image info.
4092%
4093% o exception: return any errors or warnings in this structure.
4094%
4095*/
4096static Image *ReadOneJNGImage(MngInfo *mng_info,
4097 const ImageInfo *image_info, ExceptionInfo *exception)
4098{
4099 Image
4100 *alpha_image,
4101 *color_image,
4102 *image,
4103 *jng_image;
4104
4105 ImageInfo
4106 *alpha_image_info,
4107 *color_image_info;
4108
cristy4383ec82011-01-05 15:42:32 +00004109 MagickBooleanType
4110 logging;
4111
cristybb503372010-05-27 20:51:26 +00004112 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004113 y;
4114
4115 MagickBooleanType
4116 status;
4117
4118 png_uint_32
4119 jng_height,
4120 jng_width;
4121
4122 png_byte
4123 jng_color_type,
4124 jng_image_sample_depth,
4125 jng_image_compression_method,
4126 jng_image_interlace_method,
4127 jng_alpha_sample_depth,
4128 jng_alpha_compression_method,
4129 jng_alpha_filter_method,
4130 jng_alpha_interlace_method;
4131
cristy16ea1392012-03-21 20:38:41 +00004132 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00004133 *s;
4134
cristybb503372010-05-27 20:51:26 +00004135 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004136 i,
4137 x;
4138
cristy16ea1392012-03-21 20:38:41 +00004139 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004140 *q;
4141
4142 register unsigned char
4143 *p;
4144
4145 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00004146 read_JSEP,
glennrpe0421fe2014-06-08 02:04:34 +00004147 reading_idat;
cristy3ed852e2009-09-05 21:47:34 +00004148
cristybb503372010-05-27 20:51:26 +00004149 size_t
cristy3ed852e2009-09-05 21:47:34 +00004150 length;
4151
4152 jng_alpha_compression_method=0;
4153 jng_alpha_sample_depth=8;
4154 jng_color_type=0;
4155 jng_height=0;
4156 jng_width=0;
4157 alpha_image=(Image *) NULL;
4158 color_image=(Image *) NULL;
4159 alpha_image_info=(ImageInfo *) NULL;
4160 color_image_info=(ImageInfo *) NULL;
4161
4162 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00004163 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004164
4165 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00004166
cristy16ea1392012-03-21 20:38:41 +00004167 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004168 {
4169 /*
4170 Allocate next image structure.
4171 */
4172 if (logging != MagickFalse)
4173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4174 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004175
cristy16ea1392012-03-21 20:38:41 +00004176 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004177
cristy3ed852e2009-09-05 21:47:34 +00004178 if (GetNextImageInList(image) == (Image *) NULL)
4179 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004180
cristy3ed852e2009-09-05 21:47:34 +00004181 image=SyncNextImageInList(image);
4182 }
4183 mng_info->image=image;
4184
4185 /*
4186 Signature bytes have already been read.
4187 */
4188
4189 read_JSEP=MagickFalse;
4190 reading_idat=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004191 for (;;)
4192 {
4193 char
cristy151b66d2015-04-15 10:50:31 +00004194 type[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00004195
4196 unsigned char
4197 *chunk;
4198
4199 unsigned int
4200 count;
4201
4202 /*
4203 Read a new JNG chunk.
4204 */
4205 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4206 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004207
cristy3ed852e2009-09-05 21:47:34 +00004208 if (status == MagickFalse)
4209 break;
glennrp0fe50b42010-11-16 03:52:51 +00004210
cristy3ed852e2009-09-05 21:47:34 +00004211 type[0]='\0';
cristy151b66d2015-04-15 10:50:31 +00004212 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00004213 length=ReadBlobMSBLong(image);
4214 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4215
4216 if (logging != MagickFalse)
4217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004218 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4219 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004220
4221 if (length > PNG_UINT_31_MAX || count == 0)
dirk6af512a2014-12-14 20:12:39 +00004222 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004223
cristy3ed852e2009-09-05 21:47:34 +00004224 p=NULL;
4225 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00004226
glennrp8fe91592014-10-25 13:57:25 +00004227 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00004228 {
4229 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00004230
cristy3ed852e2009-09-05 21:47:34 +00004231 if (chunk == (unsigned char *) NULL)
4232 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004233
cristybb503372010-05-27 20:51:26 +00004234 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004235 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00004236
cristy3ed852e2009-09-05 21:47:34 +00004237 p=chunk;
4238 }
glennrp47b9dd52010-11-24 18:12:06 +00004239
cristy3ed852e2009-09-05 21:47:34 +00004240 (void) ReadBlobMSBLong(image); /* read crc word */
4241
cristy3ed852e2009-09-05 21:47:34 +00004242 if (memcmp(type,mng_JHDR,4) == 0)
4243 {
4244 if (length == 16)
4245 {
cristybb503372010-05-27 20:51:26 +00004246 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristye2440c22014-05-18 12:54:47 +00004247 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00004248 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristye2440c22014-05-18 12:54:47 +00004249 (p[6] << 8) | p[7]);
4250 if ((jng_width == 0) || (jng_height == 0))
4251 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
cristy3ed852e2009-09-05 21:47:34 +00004252 jng_color_type=p[8];
4253 jng_image_sample_depth=p[9];
4254 jng_image_compression_method=p[10];
4255 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00004256
cristy3ed852e2009-09-05 21:47:34 +00004257 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4258 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00004259
cristy3ed852e2009-09-05 21:47:34 +00004260 jng_alpha_sample_depth=p[12];
4261 jng_alpha_compression_method=p[13];
4262 jng_alpha_filter_method=p[14];
4263 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00004264
cristy3ed852e2009-09-05 21:47:34 +00004265 if (logging != MagickFalse)
4266 {
4267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00004268 " jng_width: %16lu, jng_height: %16lu\n"
4269 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
cristy3ed852e2009-09-05 21:47:34 +00004270 " jng_image_compression_method:%3d",
glennrp2dd19062014-07-28 00:22:24 +00004271 (unsigned long) jng_width, (unsigned long) jng_height,
4272 jng_color_type, jng_image_sample_depth,
cristy3ed852e2009-09-05 21:47:34 +00004273 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004274
cristy3ed852e2009-09-05 21:47:34 +00004275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00004276 " jng_image_interlace_method: %3d"
cristy3ed852e2009-09-05 21:47:34 +00004277 " jng_alpha_sample_depth: %3d",
glennrp2dd19062014-07-28 00:22:24 +00004278 jng_image_interlace_method,
cristy3ed852e2009-09-05 21:47:34 +00004279 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004280
cristy3ed852e2009-09-05 21:47:34 +00004281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00004282 " jng_alpha_compression_method:%3d\n"
4283 " jng_alpha_filter_method: %3d\n"
cristy3ed852e2009-09-05 21:47:34 +00004284 " jng_alpha_interlace_method: %3d",
glennrp2dd19062014-07-28 00:22:24 +00004285 jng_alpha_compression_method,
4286 jng_alpha_filter_method,
cristy3ed852e2009-09-05 21:47:34 +00004287 jng_alpha_interlace_method);
4288 }
4289 }
glennrp47b9dd52010-11-24 18:12:06 +00004290
glennrp8fe91592014-10-25 13:57:25 +00004291 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00004292 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004293
cristy3ed852e2009-09-05 21:47:34 +00004294 continue;
4295 }
4296
4297
4298 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4299 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4300 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4301 {
4302 /*
4303 o create color_image
4304 o open color_blob, attached to color_image
4305 o if (color type has alpha)
4306 open alpha_blob, attached to alpha_image
4307 */
4308
cristy73bd4a52010-10-05 11:24:23 +00004309 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004310
cristy3ed852e2009-09-05 21:47:34 +00004311 if (color_image_info == (ImageInfo *) NULL)
4312 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004313
cristy3ed852e2009-09-05 21:47:34 +00004314 GetImageInfo(color_image_info);
cristy16ea1392012-03-21 20:38:41 +00004315 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004316
cristy3ed852e2009-09-05 21:47:34 +00004317 if (color_image == (Image *) NULL)
4318 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4319
4320 if (logging != MagickFalse)
4321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4322 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004323
cristy3ed852e2009-09-05 21:47:34 +00004324 (void) AcquireUniqueFilename(color_image->filename);
4325 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4326 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004327
cristy3ed852e2009-09-05 21:47:34 +00004328 if (status == MagickFalse)
4329 return((Image *) NULL);
4330
4331 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4332 {
4333 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004334 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004335
cristy3ed852e2009-09-05 21:47:34 +00004336 if (alpha_image_info == (ImageInfo *) NULL)
4337 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004338
cristy3ed852e2009-09-05 21:47:34 +00004339 GetImageInfo(alpha_image_info);
cristy16ea1392012-03-21 20:38:41 +00004340 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004341
cristy3ed852e2009-09-05 21:47:34 +00004342 if (alpha_image == (Image *) NULL)
4343 {
4344 alpha_image=DestroyImage(alpha_image);
4345 ThrowReaderException(ResourceLimitError,
4346 "MemoryAllocationFailed");
4347 }
glennrp0fe50b42010-11-16 03:52:51 +00004348
cristy3ed852e2009-09-05 21:47:34 +00004349 if (logging != MagickFalse)
4350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4351 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004352
cristy3ed852e2009-09-05 21:47:34 +00004353 (void) AcquireUniqueFilename(alpha_image->filename);
4354 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4355 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004356
cristy3ed852e2009-09-05 21:47:34 +00004357 if (status == MagickFalse)
4358 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004359
cristy3ed852e2009-09-05 21:47:34 +00004360 if (jng_alpha_compression_method == 0)
4361 {
4362 unsigned char
4363 data[18];
4364
4365 if (logging != MagickFalse)
4366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4367 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004368
cristy3ed852e2009-09-05 21:47:34 +00004369 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4370 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004371
cristy3ed852e2009-09-05 21:47:34 +00004372 (void) WriteBlobMSBULong(alpha_image,13L);
4373 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004374 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004375 PNGLong(data+4,jng_width);
4376 PNGLong(data+8,jng_height);
4377 data[12]=jng_alpha_sample_depth;
4378 data[13]=0; /* color_type gray */
4379 data[14]=0; /* compression method 0 */
4380 data[15]=0; /* filter_method 0 */
4381 data[16]=0; /* interlace_method 0 */
4382 (void) WriteBlob(alpha_image,17,data);
4383 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4384 }
4385 }
4386 reading_idat=MagickTrue;
4387 }
4388
4389 if (memcmp(type,mng_JDAT,4) == 0)
4390 {
glennrp47b9dd52010-11-24 18:12:06 +00004391 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004392
4393 if (logging != MagickFalse)
4394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4395 " Copying JDAT chunk data to color_blob.");
4396
4397 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004398
glennrp8fe91592014-10-25 13:57:25 +00004399 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00004400 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004401
cristy3ed852e2009-09-05 21:47:34 +00004402 continue;
4403 }
4404
4405 if (memcmp(type,mng_IDAT,4) == 0)
4406 {
4407 png_byte
4408 data[5];
4409
glennrp47b9dd52010-11-24 18:12:06 +00004410 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004411
glennrp889a9282015-04-09 16:51:44 +00004412 if (alpha_image != NULL && image_info->ping == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004413 {
4414 if (logging != MagickFalse)
4415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4416 " Copying IDAT chunk data to alpha_blob.");
4417
cristybb503372010-05-27 20:51:26 +00004418 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004419 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004420 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004421 (void) WriteBlob(alpha_image,4,data);
4422 (void) WriteBlob(alpha_image,length,chunk);
4423 (void) WriteBlobMSBULong(alpha_image,
4424 crc32(crc32(0,data,4),chunk,(uInt) length));
4425 }
glennrp0fe50b42010-11-16 03:52:51 +00004426
glennrp8fe91592014-10-25 13:57:25 +00004427 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00004428 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004429
cristy3ed852e2009-09-05 21:47:34 +00004430 continue;
4431 }
4432
4433 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4434 {
glennrp47b9dd52010-11-24 18:12:06 +00004435 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004436
glennrp889a9282015-04-09 16:51:44 +00004437 if (alpha_image != NULL && image_info->ping == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004438 {
4439 if (logging != MagickFalse)
4440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4441 " Copying JDAA chunk data to alpha_blob.");
4442
4443 (void) WriteBlob(alpha_image,length,chunk);
4444 }
glennrp0fe50b42010-11-16 03:52:51 +00004445
glennrp8fe91592014-10-25 13:57:25 +00004446 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00004447 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004448
cristy3ed852e2009-09-05 21:47:34 +00004449 continue;
4450 }
4451
4452 if (memcmp(type,mng_JSEP,4) == 0)
4453 {
4454 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004455
glennrp8fe91592014-10-25 13:57:25 +00004456 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00004457 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004458
cristy3ed852e2009-09-05 21:47:34 +00004459 continue;
4460 }
4461
4462 if (memcmp(type,mng_bKGD,4) == 0)
4463 {
4464 if (length == 2)
4465 {
4466 image->background_color.red=ScaleCharToQuantum(p[1]);
4467 image->background_color.green=image->background_color.red;
4468 image->background_color.blue=image->background_color.red;
4469 }
glennrp0fe50b42010-11-16 03:52:51 +00004470
cristy3ed852e2009-09-05 21:47:34 +00004471 if (length == 6)
4472 {
4473 image->background_color.red=ScaleCharToQuantum(p[1]);
4474 image->background_color.green=ScaleCharToQuantum(p[3]);
4475 image->background_color.blue=ScaleCharToQuantum(p[5]);
4476 }
glennrp0fe50b42010-11-16 03:52:51 +00004477
cristy3ed852e2009-09-05 21:47:34 +00004478 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4479 continue;
4480 }
4481
4482 if (memcmp(type,mng_gAMA,4) == 0)
4483 {
4484 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004485 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004486
cristy3ed852e2009-09-05 21:47:34 +00004487 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4488 continue;
4489 }
4490
4491 if (memcmp(type,mng_cHRM,4) == 0)
4492 {
4493 if (length == 32)
4494 {
cristy8182b072010-05-30 20:10:53 +00004495 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4496 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4497 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4498 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4499 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4500 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4501 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4502 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004503 }
glennrp47b9dd52010-11-24 18:12:06 +00004504
cristy3ed852e2009-09-05 21:47:34 +00004505 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4506 continue;
4507 }
4508
4509 if (memcmp(type,mng_sRGB,4) == 0)
4510 {
4511 if (length == 1)
4512 {
glennrpe610a072010-08-05 17:08:46 +00004513 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004514 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristyda7803d2012-05-03 01:22:45 +00004515 image->gamma=1.000f/2.200f;
cristy3ed852e2009-09-05 21:47:34 +00004516 image->chromaticity.red_primary.x=0.6400f;
4517 image->chromaticity.red_primary.y=0.3300f;
4518 image->chromaticity.green_primary.x=0.3000f;
4519 image->chromaticity.green_primary.y=0.6000f;
4520 image->chromaticity.blue_primary.x=0.1500f;
4521 image->chromaticity.blue_primary.y=0.0600f;
4522 image->chromaticity.white_point.x=0.3127f;
4523 image->chromaticity.white_point.y=0.3290f;
4524 }
glennrp47b9dd52010-11-24 18:12:06 +00004525
cristy3ed852e2009-09-05 21:47:34 +00004526 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4527 continue;
4528 }
4529
4530 if (memcmp(type,mng_oFFs,4) == 0)
4531 {
4532 if (length > 8)
4533 {
glennrp5eae7602011-02-22 15:21:32 +00004534 image->page.x=(ssize_t) mng_get_long(p);
4535 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004536
cristy3ed852e2009-09-05 21:47:34 +00004537 if ((int) p[8] != 0)
4538 {
4539 image->page.x/=10000;
4540 image->page.y/=10000;
4541 }
4542 }
glennrp47b9dd52010-11-24 18:12:06 +00004543
glennrp8fe91592014-10-25 13:57:25 +00004544 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00004545 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004546
cristy3ed852e2009-09-05 21:47:34 +00004547 continue;
4548 }
4549
4550 if (memcmp(type,mng_pHYs,4) == 0)
4551 {
4552 if (length > 8)
4553 {
cristy16ea1392012-03-21 20:38:41 +00004554 image->resolution.x=(double) mng_get_long(p);
4555 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004556 if ((int) p[8] == PNG_RESOLUTION_METER)
4557 {
4558 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00004559 image->resolution.x=image->resolution.x/100.0f;
4560 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004561 }
4562 }
glennrp0fe50b42010-11-16 03:52:51 +00004563
cristy3ed852e2009-09-05 21:47:34 +00004564 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4565 continue;
4566 }
4567
4568#if 0
4569 if (memcmp(type,mng_iCCP,4) == 0)
4570 {
glennrpfd05d622011-02-25 04:10:33 +00004571 /* To do: */
glennrp8fe91592014-10-25 13:57:25 +00004572 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00004573 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004574
cristy3ed852e2009-09-05 21:47:34 +00004575 continue;
4576 }
4577#endif
4578
glennrp8fe91592014-10-25 13:57:25 +00004579 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00004580 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4581
4582 if (memcmp(type,mng_IEND,4))
4583 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004584
cristy3ed852e2009-09-05 21:47:34 +00004585 break;
4586 }
4587
4588
4589 /* IEND found */
4590
4591 /*
4592 Finish up reading image data:
4593
4594 o read main image from color_blob.
4595
4596 o close color_blob.
4597
4598 o if (color_type has alpha)
4599 if alpha_encoding is PNG
4600 read secondary image from alpha_blob via ReadPNG
4601 if alpha_encoding is JPEG
4602 read secondary image from alpha_blob via ReadJPEG
4603
4604 o close alpha_blob.
4605
4606 o copy intensity of secondary image into
cristy16ea1392012-03-21 20:38:41 +00004607 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004608
4609 o destroy the secondary image.
4610 */
4611
Cristy32565792015-09-17 20:44:51 -04004612 if (color_image_info == (ImageInfo *) NULL)
4613 {
4614 assert(color_image == (Image *) NULL);
4615 assert(alpha_image == (Image *) NULL);
4616 return((Image *) NULL);
4617 }
4618
4619 if (color_image == (Image *) NULL)
4620 {
4621 assert(alpha_image == (Image *) NULL);
4622 return((Image *) NULL);
4623 }
4624
4625 (void) SeekBlob(color_image,0,SEEK_SET);
4626
cristy3ed852e2009-09-05 21:47:34 +00004627 if (logging != MagickFalse)
4628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4629 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004630
cristyf0eec752014-01-16 01:28:43 +00004631 assert(color_image_info != (ImageInfo *) NULL);
cristy151b66d2015-04-15 10:50:31 +00004632 (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004633 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004634
cristy3ed852e2009-09-05 21:47:34 +00004635 color_image_info->ping=MagickFalse; /* To do: avoid this */
4636 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004637
cristy3ed852e2009-09-05 21:47:34 +00004638 (void) RelinquishUniqueFileResource(color_image->filename);
4639 color_image=DestroyImage(color_image);
4640 color_image_info=DestroyImageInfo(color_image_info);
4641
4642 if (jng_image == (Image *) NULL)
4643 return((Image *) NULL);
4644
4645 if (logging != MagickFalse)
4646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4647 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004648
cristy3ed852e2009-09-05 21:47:34 +00004649 image->rows=jng_height;
4650 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004651
cristybb503372010-05-27 20:51:26 +00004652 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004653 {
cristy16ea1392012-03-21 20:38:41 +00004654 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004655 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00004656 for (x=(ssize_t) image->columns; x != 0; x--)
4657 {
4658 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4659 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4660 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4661 q+=GetPixelChannels(image);
4662 s+=GetPixelChannels(jng_image);
4663 }
glennrp47b9dd52010-11-24 18:12:06 +00004664
cristy3ed852e2009-09-05 21:47:34 +00004665 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4666 break;
4667 }
glennrp0fe50b42010-11-16 03:52:51 +00004668
cristy3ed852e2009-09-05 21:47:34 +00004669 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004670
cristy3ed852e2009-09-05 21:47:34 +00004671 if (image_info->ping == MagickFalse)
4672 {
4673 if (jng_color_type >= 12)
4674 {
4675 if (jng_alpha_compression_method == 0)
4676 {
4677 png_byte
4678 data[5];
4679 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4680 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004681 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004682 (void) WriteBlob(alpha_image,4,data);
4683 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4684 }
glennrp0fe50b42010-11-16 03:52:51 +00004685
cristy3ed852e2009-09-05 21:47:34 +00004686 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004687
cristy3ed852e2009-09-05 21:47:34 +00004688 if (logging != MagickFalse)
4689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00004690 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004691
cristy151b66d2015-04-15 10:50:31 +00004692 (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00004693 "%s",alpha_image->filename);
4694
4695 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004696
cristy3ed852e2009-09-05 21:47:34 +00004697 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004698 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004699 {
4700 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy16ea1392012-03-21 20:38:41 +00004701 exception);
cristy3ed852e2009-09-05 21:47:34 +00004702 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004703
cristy17f11b02014-12-20 19:37:04 +00004704 if (image->alpha_trait != UndefinedPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00004705 for (x=(ssize_t) image->columns; x != 0; x--)
4706 {
4707 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4708 q+=GetPixelChannels(image);
4709 s+=GetPixelChannels(jng_image);
4710 }
glennrp0fe50b42010-11-16 03:52:51 +00004711
cristy3ed852e2009-09-05 21:47:34 +00004712 else
cristy16ea1392012-03-21 20:38:41 +00004713 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004714 {
cristy16ea1392012-03-21 20:38:41 +00004715 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4716 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy8a46d822012-08-28 23:32:39 +00004717 image->alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00004718 q+=GetPixelChannels(image);
4719 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004720 }
glennrp0fe50b42010-11-16 03:52:51 +00004721
cristy3ed852e2009-09-05 21:47:34 +00004722 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4723 break;
4724 }
4725 (void) RelinquishUniqueFileResource(alpha_image->filename);
4726 alpha_image=DestroyImage(alpha_image);
4727 alpha_image_info=DestroyImageInfo(alpha_image_info);
4728 if (jng_image != (Image *) NULL)
4729 jng_image=DestroyImage(jng_image);
4730 }
4731 }
4732
glennrp47b9dd52010-11-24 18:12:06 +00004733 /* Read the JNG image. */
4734
cristy3ed852e2009-09-05 21:47:34 +00004735 if (mng_info->mng_type == 0)
4736 {
4737 mng_info->mng_width=jng_width;
4738 mng_info->mng_height=jng_height;
4739 }
glennrp0fe50b42010-11-16 03:52:51 +00004740
cristy3ed852e2009-09-05 21:47:34 +00004741 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004742 {
4743 image->page.width=jng_width;
4744 image->page.height=jng_height;
4745 }
4746
cristy3ed852e2009-09-05 21:47:34 +00004747 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004748 {
4749 image->page.x=mng_info->x_off[mng_info->object_id];
4750 image->page.y=mng_info->y_off[mng_info->object_id];
4751 }
4752
cristy3ed852e2009-09-05 21:47:34 +00004753 else
glennrp0fe50b42010-11-16 03:52:51 +00004754 {
4755 image->page.y=mng_info->y_off[mng_info->object_id];
4756 }
4757
cristy3ed852e2009-09-05 21:47:34 +00004758 mng_info->image_found++;
4759 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4760 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004761
glennrp44c22972015-01-25 03:10:16 +00004762 if (status == MagickFalse)
4763 return((Image *) NULL);
4764
cristy3ed852e2009-09-05 21:47:34 +00004765 if (logging != MagickFalse)
4766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4767 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004768
cristy3ed852e2009-09-05 21:47:34 +00004769 return(image);
4770}
4771
4772/*
4773%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4774% %
4775% %
4776% %
4777% R e a d J N G I m a g e %
4778% %
4779% %
4780% %
4781%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4782%
4783% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4784% (including the 8-byte signature) and returns it. It allocates the memory
4785% necessary for the new Image structure and returns a pointer to the new
4786% image.
4787%
4788% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4789%
4790% The format of the ReadJNGImage method is:
4791%
4792% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4793% *exception)
4794%
4795% A description of each parameter follows:
4796%
4797% o image_info: the image info.
4798%
4799% o exception: return any errors or warnings in this structure.
4800%
4801*/
4802
4803static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4804{
4805 Image
glennrp7ee973a2014-11-20 18:30:54 +00004806 *image;
cristy3ed852e2009-09-05 21:47:34 +00004807
4808 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004809 have_mng_structure,
4810 logging,
cristy3ed852e2009-09-05 21:47:34 +00004811 status;
4812
4813 MngInfo
4814 *mng_info;
4815
4816 char
cristy151b66d2015-04-15 10:50:31 +00004817 magic_number[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00004818
cristy3ed852e2009-09-05 21:47:34 +00004819 size_t
4820 count;
4821
4822 /*
4823 Open image file.
4824 */
4825 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00004826 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00004827 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4828 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00004829 assert(exception->signature == MagickCoreSignature);
glennrpfd05d622011-02-25 04:10:33 +00004830 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004831 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004832 mng_info=(MngInfo *) NULL;
4833 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004834
cristy3ed852e2009-09-05 21:47:34 +00004835 if (status == MagickFalse)
4836 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004837
cristy3ed852e2009-09-05 21:47:34 +00004838 if (LocaleCompare(image_info->magick,"JNG") != 0)
4839 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004840
glennrp47b9dd52010-11-24 18:12:06 +00004841 /* Verify JNG signature. */
4842
cristy3ed852e2009-09-05 21:47:34 +00004843 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004844
glennrp3b8763e2011-02-21 12:08:18 +00004845 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004846 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004847
glennrp47b9dd52010-11-24 18:12:06 +00004848 /* Allocate a MngInfo structure. */
4849
cristy3ed852e2009-09-05 21:47:34 +00004850 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004851 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004852
cristy3ed852e2009-09-05 21:47:34 +00004853 if (mng_info == (MngInfo *) NULL)
4854 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004855
glennrp47b9dd52010-11-24 18:12:06 +00004856 /* Initialize members of the MngInfo structure. */
4857
cristy3ed852e2009-09-05 21:47:34 +00004858 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4859 have_mng_structure=MagickTrue;
4860
4861 mng_info->image=image;
cristy3ed852e2009-09-05 21:47:34 +00004862 image=ReadOneJNGImage(mng_info,image_info,exception);
4863 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004864
cristy3ed852e2009-09-05 21:47:34 +00004865 if (image == (Image *) NULL)
4866 {
cristy3ed852e2009-09-05 21:47:34 +00004867 if (logging != MagickFalse)
4868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4869 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004870
cristy3ed852e2009-09-05 21:47:34 +00004871 return((Image *) NULL);
4872 }
4873 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004874
cristy3ed852e2009-09-05 21:47:34 +00004875 if (image->columns == 0 || image->rows == 0)
4876 {
4877 if (logging != MagickFalse)
4878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4879 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004880
cristy3ed852e2009-09-05 21:47:34 +00004881 ThrowReaderException(CorruptImageError,"CorruptImage");
4882 }
glennrp0fe50b42010-11-16 03:52:51 +00004883
cristy3ed852e2009-09-05 21:47:34 +00004884 if (logging != MagickFalse)
4885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004886
cristy3ed852e2009-09-05 21:47:34 +00004887 return(image);
4888}
4889#endif
4890
4891static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4892{
4893 char
cristy151b66d2015-04-15 10:50:31 +00004894 page_geometry[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00004895
4896 Image
glennrp889a9282015-04-09 16:51:44 +00004897 *image;
cristy3ed852e2009-09-05 21:47:34 +00004898
cristy4383ec82011-01-05 15:42:32 +00004899 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004900 logging,
4901 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004902
cristy3ed852e2009-09-05 21:47:34 +00004903 volatile int
4904 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004905 object_id,
4906 term_chunk_found,
4907 skip_to_iend;
4908
cristybb503372010-05-27 20:51:26 +00004909 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004910 image_count=0;
4911
4912 MagickBooleanType
4913 status;
4914
4915 MagickOffsetType
4916 offset;
4917
4918 MngInfo
4919 *mng_info;
4920
4921 MngBox
4922 default_fb,
4923 fb,
4924 previous_fb;
4925
4926#if defined(MNG_INSERT_LAYERS)
cristy16ea1392012-03-21 20:38:41 +00004927 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004928 mng_background_color;
4929#endif
4930
4931 register unsigned char
4932 *p;
4933
cristybb503372010-05-27 20:51:26 +00004934 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004935 i;
4936
4937 size_t
4938 count;
4939
cristybb503372010-05-27 20:51:26 +00004940 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004941 loop_level;
4942
4943 volatile short
4944 skipping_loop;
4945
4946#if defined(MNG_INSERT_LAYERS)
4947 unsigned int
4948 mandatory_back=0;
4949#endif
4950
4951 volatile unsigned int
4952#ifdef MNG_OBJECT_BUFFERS
4953 mng_background_object=0,
4954#endif
4955 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4956
cristybb503372010-05-27 20:51:26 +00004957 size_t
cristy3ed852e2009-09-05 21:47:34 +00004958 default_frame_timeout,
4959 frame_timeout,
4960#if defined(MNG_INSERT_LAYERS)
4961 image_height,
4962 image_width,
4963#endif
4964 length;
4965
glennrp38ea0832010-06-02 18:50:28 +00004966 /* These delays are all measured in image ticks_per_second,
4967 * not in MNG ticks_per_second
4968 */
cristybb503372010-05-27 20:51:26 +00004969 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004970 default_frame_delay,
4971 final_delay,
4972 final_image_delay,
4973 frame_delay,
4974#if defined(MNG_INSERT_LAYERS)
4975 insert_layers,
4976#endif
4977 mng_iterations=1,
4978 simplicity=0,
4979 subframe_height=0,
4980 subframe_width=0;
4981
4982 previous_fb.top=0;
4983 previous_fb.bottom=0;
4984 previous_fb.left=0;
4985 previous_fb.right=0;
4986 default_fb.top=0;
4987 default_fb.bottom=0;
4988 default_fb.left=0;
4989 default_fb.right=0;
4990
glennrp47b9dd52010-11-24 18:12:06 +00004991 /* Open image file. */
4992
cristy3ed852e2009-09-05 21:47:34 +00004993 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00004994 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +00004995 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4996 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00004997 assert(exception->signature == MagickCoreSignature);
glennrpfd05d622011-02-25 04:10:33 +00004998 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004999 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005000 mng_info=(MngInfo *) NULL;
5001 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005002
cristy3ed852e2009-09-05 21:47:34 +00005003 if (status == MagickFalse)
5004 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00005005
cristy3ed852e2009-09-05 21:47:34 +00005006 first_mng_object=MagickFalse;
5007 skipping_loop=(-1);
5008 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005009
5010 /* Allocate a MngInfo structure. */
5011
cristy73bd4a52010-10-05 11:24:23 +00005012 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00005013
cristy3ed852e2009-09-05 21:47:34 +00005014 if (mng_info == (MngInfo *) NULL)
5015 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00005016
glennrp47b9dd52010-11-24 18:12:06 +00005017 /* Initialize members of the MngInfo structure. */
5018
cristy3ed852e2009-09-05 21:47:34 +00005019 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5020 mng_info->image=image;
5021 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00005022
5023 if (LocaleCompare(image_info->magick,"MNG") == 0)
5024 {
5025 char
cristy151b66d2015-04-15 10:50:31 +00005026 magic_number[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00005027
glennrp47b9dd52010-11-24 18:12:06 +00005028 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00005029 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5030 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5031 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005032
5033 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00005034 for (i=0; i < MNG_MAX_OBJECTS; i++)
5035 {
cristybb503372010-05-27 20:51:26 +00005036 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5037 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00005038 }
5039 mng_info->exists[0]=MagickTrue;
5040 }
glennrp47b9dd52010-11-24 18:12:06 +00005041
cristy3ed852e2009-09-05 21:47:34 +00005042 first_mng_object=MagickTrue;
5043 mng_type=0;
5044#if defined(MNG_INSERT_LAYERS)
5045 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5046#endif
5047 default_frame_delay=0;
5048 default_frame_timeout=0;
5049 frame_delay=0;
5050 final_delay=1;
5051 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5052 object_id=0;
5053 skip_to_iend=MagickFalse;
5054 term_chunk_found=MagickFalse;
5055 mng_info->framing_mode=1;
5056#if defined(MNG_INSERT_LAYERS)
5057 mandatory_back=MagickFalse;
5058#endif
5059#if defined(MNG_INSERT_LAYERS)
5060 mng_background_color=image->background_color;
5061#endif
5062 default_fb=mng_info->frame;
5063 previous_fb=mng_info->frame;
5064 do
5065 {
5066 char
cristy151b66d2015-04-15 10:50:31 +00005067 type[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00005068
5069 if (LocaleCompare(image_info->magick,"MNG") == 0)
5070 {
5071 unsigned char
5072 *chunk;
5073
5074 /*
5075 Read a new chunk.
5076 */
5077 type[0]='\0';
cristy151b66d2015-04-15 10:50:31 +00005078 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00005079 length=ReadBlobMSBLong(image);
5080 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5081
5082 if (logging != MagickFalse)
5083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005084 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5085 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00005086
5087 if (length > PNG_UINT_31_MAX)
glennrp44c22972015-01-25 03:10:16 +00005088 {
5089 status=MagickFalse;
5090 break;
5091 }
glennrp0fe50b42010-11-16 03:52:51 +00005092
cristy3ed852e2009-09-05 21:47:34 +00005093 if (count == 0)
5094 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00005095
cristy3ed852e2009-09-05 21:47:34 +00005096 p=NULL;
5097 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00005098
glennrp8fe91592014-10-25 13:57:25 +00005099 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00005100 {
5101 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00005102
cristy3ed852e2009-09-05 21:47:34 +00005103 if (chunk == (unsigned char *) NULL)
5104 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00005105
cristybb503372010-05-27 20:51:26 +00005106 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005107 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00005108
cristy3ed852e2009-09-05 21:47:34 +00005109 p=chunk;
5110 }
glennrp0fe50b42010-11-16 03:52:51 +00005111
cristy3ed852e2009-09-05 21:47:34 +00005112 (void) ReadBlobMSBLong(image); /* read crc word */
5113
5114#if !defined(JNG_SUPPORTED)
5115 if (memcmp(type,mng_JHDR,4) == 0)
5116 {
5117 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005118
cristy3ed852e2009-09-05 21:47:34 +00005119 if (mng_info->jhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005120 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005121 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005122
cristy3ed852e2009-09-05 21:47:34 +00005123 mng_info->jhdr_warning++;
5124 }
5125#endif
5126 if (memcmp(type,mng_DHDR,4) == 0)
5127 {
5128 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005129
cristy3ed852e2009-09-05 21:47:34 +00005130 if (mng_info->dhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005131 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005132 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005133
cristy3ed852e2009-09-05 21:47:34 +00005134 mng_info->dhdr_warning++;
5135 }
5136 if (memcmp(type,mng_MEND,4) == 0)
5137 break;
glennrp47b9dd52010-11-24 18:12:06 +00005138
cristy3ed852e2009-09-05 21:47:34 +00005139 if (skip_to_iend)
5140 {
5141 if (memcmp(type,mng_IEND,4) == 0)
5142 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005143
glennrp8fe91592014-10-25 13:57:25 +00005144 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00005145 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005146
cristy3ed852e2009-09-05 21:47:34 +00005147 if (logging != MagickFalse)
5148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5149 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00005150
cristy3ed852e2009-09-05 21:47:34 +00005151 continue;
5152 }
glennrp0fe50b42010-11-16 03:52:51 +00005153
cristy3ed852e2009-09-05 21:47:34 +00005154 if (memcmp(type,mng_MHDR,4) == 0)
5155 {
Cristyf8d9cb82015-09-11 11:51:22 -04005156 if (length != 28)
5157 {
5158 if (chunk)
5159 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5160 ThrowReaderException(CorruptImageError,"CorruptImage");
5161 }
5162
cristybb503372010-05-27 20:51:26 +00005163 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005164 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00005165
cristybb503372010-05-27 20:51:26 +00005166 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005167 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00005168
cristy3ed852e2009-09-05 21:47:34 +00005169 if (logging != MagickFalse)
5170 {
5171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005172 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00005173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005174 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005175 }
glennrp0fe50b42010-11-16 03:52:51 +00005176
cristy3ed852e2009-09-05 21:47:34 +00005177 p+=8;
cristy8182b072010-05-30 20:10:53 +00005178 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005179
cristy3ed852e2009-09-05 21:47:34 +00005180 if (mng_info->ticks_per_second == 0)
5181 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005182
cristy3ed852e2009-09-05 21:47:34 +00005183 else
5184 default_frame_delay=1UL*image->ticks_per_second/
5185 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005186
cristy3ed852e2009-09-05 21:47:34 +00005187 frame_delay=default_frame_delay;
5188 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00005189
Cristyf8d9cb82015-09-11 11:51:22 -04005190 p+=16;
5191 simplicity=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005192
cristy3ed852e2009-09-05 21:47:34 +00005193 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00005194
cristy3ed852e2009-09-05 21:47:34 +00005195 if ((simplicity != 0) && ((simplicity | 11) == 11))
5196 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00005197
cristy3ed852e2009-09-05 21:47:34 +00005198 if ((simplicity != 0) && ((simplicity | 9) == 9))
5199 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00005200
cristy3ed852e2009-09-05 21:47:34 +00005201#if defined(MNG_INSERT_LAYERS)
5202 if (mng_type != 3)
5203 insert_layers=MagickTrue;
5204#endif
cristy16ea1392012-03-21 20:38:41 +00005205 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005206 {
glennrp47b9dd52010-11-24 18:12:06 +00005207 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005208 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005209
cristy3ed852e2009-09-05 21:47:34 +00005210 if (GetNextImageInList(image) == (Image *) NULL)
5211 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00005212
cristy3ed852e2009-09-05 21:47:34 +00005213 image=SyncNextImageInList(image);
5214 mng_info->image=image;
5215 }
5216
5217 if ((mng_info->mng_width > 65535L) ||
5218 (mng_info->mng_height > 65535L))
5219 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00005220
cristy151b66d2015-04-15 10:50:31 +00005221 (void) FormatLocaleString(page_geometry,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00005222 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00005223 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00005224
cristy3ed852e2009-09-05 21:47:34 +00005225 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00005226 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00005227 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00005228 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00005229 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005230
cristy3ed852e2009-09-05 21:47:34 +00005231 for (i=0; i < MNG_MAX_OBJECTS; i++)
5232 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005233
cristy3ed852e2009-09-05 21:47:34 +00005234 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5235 continue;
5236 }
5237
5238 if (memcmp(type,mng_TERM,4) == 0)
5239 {
5240 int
5241 repeat=0;
5242
glennrp8fe91592014-10-25 13:57:25 +00005243 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00005244 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005245
cristy3ed852e2009-09-05 21:47:34 +00005246 if (repeat == 3)
5247 {
cristy8182b072010-05-30 20:10:53 +00005248 final_delay=(png_uint_32) mng_get_long(&p[2]);
5249 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00005250
cristy3ed852e2009-09-05 21:47:34 +00005251 if (mng_iterations == PNG_UINT_31_MAX)
5252 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00005253
cristy3ed852e2009-09-05 21:47:34 +00005254 image->iterations=mng_iterations;
5255 term_chunk_found=MagickTrue;
5256 }
glennrp0fe50b42010-11-16 03:52:51 +00005257
cristy3ed852e2009-09-05 21:47:34 +00005258 if (logging != MagickFalse)
5259 {
5260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp280283d2014-08-01 15:02:04 +00005261 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5262 repeat,(double) final_delay, (double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00005263 }
glennrp0fe50b42010-11-16 03:52:51 +00005264
cristy3ed852e2009-09-05 21:47:34 +00005265 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5266 continue;
5267 }
5268 if (memcmp(type,mng_DEFI,4) == 0)
5269 {
5270 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005271 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005272 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5273 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005274
Cristyf8d9cb82015-09-11 11:51:22 -04005275 if (length < 2)
5276 {
5277 if (chunk)
5278 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5279 ThrowReaderException(CorruptImageError,"CorruptImage");
5280 }
5281
cristy3ed852e2009-09-05 21:47:34 +00005282 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005283
cristy3ed852e2009-09-05 21:47:34 +00005284 if (mng_type == 2 && object_id != 0)
cristy16ea1392012-03-21 20:38:41 +00005285 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005286 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5287 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005288
cristy3ed852e2009-09-05 21:47:34 +00005289 if (object_id > MNG_MAX_OBJECTS)
5290 {
5291 /*
glennrpedaa0382012-04-12 14:16:21 +00005292 Instead of using a warning we should allocate a larger
cristy3ed852e2009-09-05 21:47:34 +00005293 MngInfo structure and continue.
5294 */
cristy16ea1392012-03-21 20:38:41 +00005295 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005296 CoderError,"object id too large","`%s'",image->filename);
5297 object_id=MNG_MAX_OBJECTS;
5298 }
glennrp0fe50b42010-11-16 03:52:51 +00005299
cristy3ed852e2009-09-05 21:47:34 +00005300 if (mng_info->exists[object_id])
5301 if (mng_info->frozen[object_id])
5302 {
5303 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristy16ea1392012-03-21 20:38:41 +00005304 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005305 GetMagickModule(),CoderError,
5306 "DEFI cannot redefine a frozen MNG object","`%s'",
5307 image->filename);
5308 continue;
5309 }
glennrp0fe50b42010-11-16 03:52:51 +00005310
cristy3ed852e2009-09-05 21:47:34 +00005311 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005312
cristy3ed852e2009-09-05 21:47:34 +00005313 if (length > 2)
5314 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005315
cristy3ed852e2009-09-05 21:47:34 +00005316 /*
5317 Extract object offset info.
5318 */
5319 if (length > 11)
5320 {
glennrp0fe50b42010-11-16 03:52:51 +00005321 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5322 (p[5] << 16) | (p[6] << 8) | p[7]);
5323
5324 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5325 (p[9] << 16) | (p[10] << 8) | p[11]);
5326
cristy3ed852e2009-09-05 21:47:34 +00005327 if (logging != MagickFalse)
5328 {
5329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp280283d2014-08-01 15:02:04 +00005330 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5331 object_id,(double) mng_info->x_off[object_id],
5332 object_id,(double) mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005333 }
5334 }
glennrp0fe50b42010-11-16 03:52:51 +00005335
cristy3ed852e2009-09-05 21:47:34 +00005336 /*
5337 Extract object clipping info.
5338 */
5339 if (length > 27)
5340 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5341 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005342
cristy3ed852e2009-09-05 21:47:34 +00005343 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5344 continue;
5345 }
5346 if (memcmp(type,mng_bKGD,4) == 0)
5347 {
5348 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005349
cristy3ed852e2009-09-05 21:47:34 +00005350 if (length > 5)
5351 {
5352 mng_info->mng_global_bkgd.red=
5353 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005354
cristy3ed852e2009-09-05 21:47:34 +00005355 mng_info->mng_global_bkgd.green=
5356 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005357
cristy3ed852e2009-09-05 21:47:34 +00005358 mng_info->mng_global_bkgd.blue=
5359 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005360
cristy3ed852e2009-09-05 21:47:34 +00005361 mng_info->have_global_bkgd=MagickTrue;
5362 }
glennrp0fe50b42010-11-16 03:52:51 +00005363
cristy3ed852e2009-09-05 21:47:34 +00005364 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5365 continue;
5366 }
5367 if (memcmp(type,mng_BACK,4) == 0)
5368 {
5369#if defined(MNG_INSERT_LAYERS)
5370 if (length > 6)
5371 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005372
cristy3ed852e2009-09-05 21:47:34 +00005373 else
5374 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005375
cristy3ed852e2009-09-05 21:47:34 +00005376 if (mandatory_back && length > 5)
5377 {
5378 mng_background_color.red=
5379 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005380
cristy3ed852e2009-09-05 21:47:34 +00005381 mng_background_color.green=
5382 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005383
cristy3ed852e2009-09-05 21:47:34 +00005384 mng_background_color.blue=
5385 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005386
cristy16ea1392012-03-21 20:38:41 +00005387 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005388 }
glennrp0fe50b42010-11-16 03:52:51 +00005389
cristy3ed852e2009-09-05 21:47:34 +00005390#ifdef MNG_OBJECT_BUFFERS
5391 if (length > 8)
5392 mng_background_object=(p[7] << 8) | p[8];
5393#endif
5394#endif
5395 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5396 continue;
5397 }
glennrp47b9dd52010-11-24 18:12:06 +00005398
cristy3ed852e2009-09-05 21:47:34 +00005399 if (memcmp(type,mng_PLTE,4) == 0)
5400 {
glennrp47b9dd52010-11-24 18:12:06 +00005401 /* Read global PLTE. */
5402
cristy3ed852e2009-09-05 21:47:34 +00005403 if (length && (length < 769))
5404 {
5405 if (mng_info->global_plte == (png_colorp) NULL)
5406 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5407 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005408
cristybb503372010-05-27 20:51:26 +00005409 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005410 {
5411 mng_info->global_plte[i].red=p[3*i];
5412 mng_info->global_plte[i].green=p[3*i+1];
5413 mng_info->global_plte[i].blue=p[3*i+2];
5414 }
glennrp0fe50b42010-11-16 03:52:51 +00005415
cristy35ef8242010-06-03 16:24:13 +00005416 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005417 }
5418#ifdef MNG_LOOSE
5419 for ( ; i < 256; i++)
5420 {
5421 mng_info->global_plte[i].red=i;
5422 mng_info->global_plte[i].green=i;
5423 mng_info->global_plte[i].blue=i;
5424 }
glennrp0fe50b42010-11-16 03:52:51 +00005425
glennrp8fe91592014-10-25 13:57:25 +00005426 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00005427 mng_info->global_plte_length=256;
5428#endif
5429 else
5430 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005431
cristy3ed852e2009-09-05 21:47:34 +00005432 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5433 continue;
5434 }
glennrp47b9dd52010-11-24 18:12:06 +00005435
cristy3ed852e2009-09-05 21:47:34 +00005436 if (memcmp(type,mng_tRNS,4) == 0)
5437 {
5438 /* read global tRNS */
5439
Cristyf8d9cb82015-09-11 11:51:22 -04005440 if (length > 0 && length < 257)
cristybb503372010-05-27 20:51:26 +00005441 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005442 mng_info->global_trns[i]=p[i];
5443
5444#ifdef MNG_LOOSE
5445 for ( ; i < 256; i++)
5446 mng_info->global_trns[i]=255;
5447#endif
cristy12560f32010-06-03 16:51:08 +00005448 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005449 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5450 continue;
5451 }
5452 if (memcmp(type,mng_gAMA,4) == 0)
5453 {
5454 if (length == 4)
5455 {
cristybb503372010-05-27 20:51:26 +00005456 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005457 igamma;
5458
cristy8182b072010-05-30 20:10:53 +00005459 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005460 mng_info->global_gamma=((float) igamma)*0.00001;
5461 mng_info->have_global_gama=MagickTrue;
5462 }
glennrp0fe50b42010-11-16 03:52:51 +00005463
cristy3ed852e2009-09-05 21:47:34 +00005464 else
5465 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005466
cristy3ed852e2009-09-05 21:47:34 +00005467 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5468 continue;
5469 }
5470
5471 if (memcmp(type,mng_cHRM,4) == 0)
5472 {
glennrp47b9dd52010-11-24 18:12:06 +00005473 /* Read global cHRM */
5474
cristy3ed852e2009-09-05 21:47:34 +00005475 if (length == 32)
5476 {
cristy8182b072010-05-30 20:10:53 +00005477 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5478 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5479 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005480 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005481 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005482 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005483 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005484 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005485 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005486 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005487 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005488 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005489 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005490 mng_info->have_global_chrm=MagickTrue;
5491 }
5492 else
5493 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005494
cristy3ed852e2009-09-05 21:47:34 +00005495 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5496 continue;
5497 }
glennrp47b9dd52010-11-24 18:12:06 +00005498
cristy3ed852e2009-09-05 21:47:34 +00005499 if (memcmp(type,mng_sRGB,4) == 0)
5500 {
5501 /*
5502 Read global sRGB.
5503 */
glennrp8fe91592014-10-25 13:57:25 +00005504 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00005505 {
glennrpe610a072010-08-05 17:08:46 +00005506 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005507 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005508 mng_info->have_global_srgb=MagickTrue;
5509 }
5510 else
5511 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005512
cristy3ed852e2009-09-05 21:47:34 +00005513 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5514 continue;
5515 }
glennrp47b9dd52010-11-24 18:12:06 +00005516
cristy3ed852e2009-09-05 21:47:34 +00005517 if (memcmp(type,mng_iCCP,4) == 0)
5518 {
glennrpfd05d622011-02-25 04:10:33 +00005519 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005520
5521 /*
5522 Read global iCCP.
5523 */
glennrp8fe91592014-10-25 13:57:25 +00005524 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00005525 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005526
cristy3ed852e2009-09-05 21:47:34 +00005527 continue;
5528 }
glennrp47b9dd52010-11-24 18:12:06 +00005529
cristy3ed852e2009-09-05 21:47:34 +00005530 if (memcmp(type,mng_FRAM,4) == 0)
5531 {
5532 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005533 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005534 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5535 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005536
cristy3ed852e2009-09-05 21:47:34 +00005537 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5538 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005539
cristy3ed852e2009-09-05 21:47:34 +00005540 frame_delay=default_frame_delay;
5541 frame_timeout=default_frame_timeout;
5542 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005543
glennrp8fe91592014-10-25 13:57:25 +00005544 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00005545 if (p[0])
5546 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005547
cristy3ed852e2009-09-05 21:47:34 +00005548 if (logging != MagickFalse)
5549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5550 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005551
cristy3ed852e2009-09-05 21:47:34 +00005552 if (length > 6)
5553 {
glennrp47b9dd52010-11-24 18:12:06 +00005554 /* Note the delay and frame clipping boundaries. */
5555
cristy3ed852e2009-09-05 21:47:34 +00005556 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005557
cristybb503372010-05-27 20:51:26 +00005558 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005559 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005560
cristy3ed852e2009-09-05 21:47:34 +00005561 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005562
cristybb503372010-05-27 20:51:26 +00005563 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005564 {
5565 int
5566 change_delay,
5567 change_timeout,
5568 change_clipping;
5569
5570 change_delay=(*p++);
5571 change_timeout=(*p++);
5572 change_clipping=(*p++);
5573 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005574
cristy3ed852e2009-09-05 21:47:34 +00005575 if (change_delay)
5576 {
cristy8182b072010-05-30 20:10:53 +00005577 frame_delay=1UL*image->ticks_per_second*
5578 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005579
cristy8182b072010-05-30 20:10:53 +00005580 if (mng_info->ticks_per_second != 0)
5581 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005582
glennrpbb010dd2010-06-01 13:07:15 +00005583 else
5584 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005585
cristy3ed852e2009-09-05 21:47:34 +00005586 if (change_delay == 2)
5587 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005588
cristy3ed852e2009-09-05 21:47:34 +00005589 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005590
cristy3ed852e2009-09-05 21:47:34 +00005591 if (logging != MagickFalse)
5592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005593 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005594 }
glennrp47b9dd52010-11-24 18:12:06 +00005595
cristy3ed852e2009-09-05 21:47:34 +00005596 if (change_timeout)
5597 {
glennrpbb010dd2010-06-01 13:07:15 +00005598 frame_timeout=1UL*image->ticks_per_second*
5599 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005600
glennrpbb010dd2010-06-01 13:07:15 +00005601 if (mng_info->ticks_per_second != 0)
5602 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005603
glennrpbb010dd2010-06-01 13:07:15 +00005604 else
5605 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005606
glennrp3a89fa42015-01-22 02:48:04 +00005607 if (change_timeout == 2)
cristy3ed852e2009-09-05 21:47:34 +00005608 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005609
cristy3ed852e2009-09-05 21:47:34 +00005610 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005611
cristy3ed852e2009-09-05 21:47:34 +00005612 if (logging != MagickFalse)
5613 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005614 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005615 }
glennrp47b9dd52010-11-24 18:12:06 +00005616
cristy3ed852e2009-09-05 21:47:34 +00005617 if (change_clipping)
5618 {
5619 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5620 p+=17;
5621 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005622
cristy3ed852e2009-09-05 21:47:34 +00005623 if (logging != MagickFalse)
5624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005625 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005626 (double) fb.left,(double) fb.right,(double) fb.top,
5627 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005628
cristy3ed852e2009-09-05 21:47:34 +00005629 if (change_clipping == 2)
5630 default_fb=fb;
5631 }
5632 }
5633 }
5634 mng_info->clip=fb;
5635 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005636
cristybb503372010-05-27 20:51:26 +00005637 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005638 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005639
cristybb503372010-05-27 20:51:26 +00005640 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005641 -mng_info->clip.top);
5642 /*
5643 Insert a background layer behind the frame if framing_mode is 4.
5644 */
5645#if defined(MNG_INSERT_LAYERS)
5646 if (logging != MagickFalse)
5647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005648 " subframe_width=%.20g, subframe_height=%.20g",(double)
5649 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005650
cristy3ed852e2009-09-05 21:47:34 +00005651 if (insert_layers && (mng_info->framing_mode == 4) &&
5652 (subframe_width) && (subframe_height))
5653 {
glennrp47b9dd52010-11-24 18:12:06 +00005654 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005655 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005656 {
cristy16ea1392012-03-21 20:38:41 +00005657 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005658
cristy3ed852e2009-09-05 21:47:34 +00005659 if (GetNextImageInList(image) == (Image *) NULL)
5660 {
5661 image=DestroyImageList(image);
5662 MngInfoFreeStruct(mng_info,&have_mng_structure);
5663 return((Image *) NULL);
5664 }
glennrp47b9dd52010-11-24 18:12:06 +00005665
cristy3ed852e2009-09-05 21:47:34 +00005666 image=SyncNextImageInList(image);
5667 }
glennrp0fe50b42010-11-16 03:52:51 +00005668
cristy3ed852e2009-09-05 21:47:34 +00005669 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005670
cristy3ed852e2009-09-05 21:47:34 +00005671 if (term_chunk_found)
5672 {
5673 image->start_loop=MagickTrue;
5674 image->iterations=mng_iterations;
5675 term_chunk_found=MagickFalse;
5676 }
glennrp0fe50b42010-11-16 03:52:51 +00005677
cristy3ed852e2009-09-05 21:47:34 +00005678 else
5679 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005680
cristy3ed852e2009-09-05 21:47:34 +00005681 image->columns=subframe_width;
5682 image->rows=subframe_height;
5683 image->page.width=subframe_width;
5684 image->page.height=subframe_height;
5685 image->page.x=mng_info->clip.left;
5686 image->page.y=mng_info->clip.top;
5687 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00005688 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00005689 image->delay=0;
cristy16ea1392012-03-21 20:38:41 +00005690 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005691
cristy3ed852e2009-09-05 21:47:34 +00005692 if (logging != MagickFalse)
5693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005694 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005695 (double) mng_info->clip.left,(double) mng_info->clip.right,
5696 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005697 }
5698#endif
5699 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5700 continue;
5701 }
glennrpa454cdc2015-01-24 01:10:45 +00005702
cristy3ed852e2009-09-05 21:47:34 +00005703 if (memcmp(type,mng_CLIP,4) == 0)
5704 {
5705 unsigned int
5706 first_object,
5707 last_object;
5708
5709 /*
5710 Read CLIP.
5711 */
glennrpa454cdc2015-01-24 01:10:45 +00005712 if (length > 3)
5713 {
5714 first_object=(p[0] << 8) | p[1];
5715 last_object=(p[2] << 8) | p[3];
5716 p+=4;
glennrp47b9dd52010-11-24 18:12:06 +00005717
glennrpa454cdc2015-01-24 01:10:45 +00005718 for (i=(int) first_object; i <= (int) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005719 {
glennrpa454cdc2015-01-24 01:10:45 +00005720 if (mng_info->exists[i] && !mng_info->frozen[i])
5721 {
5722 MngBox
5723 box;
cristy3ed852e2009-09-05 21:47:34 +00005724
glennrpa454cdc2015-01-24 01:10:45 +00005725 box=mng_info->object_clip[i];
5726 if ((p-chunk) < (ssize_t) (length-17))
5727 mng_info->object_clip[i]=
5728 mng_read_box(box,(char) p[0],&p[1]);
5729 }
cristy3ed852e2009-09-05 21:47:34 +00005730 }
glennrp47b9dd52010-11-24 18:12:06 +00005731
glennrpa454cdc2015-01-24 01:10:45 +00005732 }
cristy3ed852e2009-09-05 21:47:34 +00005733 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5734 continue;
5735 }
glennrpa454cdc2015-01-24 01:10:45 +00005736
cristy3ed852e2009-09-05 21:47:34 +00005737 if (memcmp(type,mng_SAVE,4) == 0)
5738 {
5739 for (i=1; i < MNG_MAX_OBJECTS; i++)
5740 if (mng_info->exists[i])
5741 {
5742 mng_info->frozen[i]=MagickTrue;
5743#ifdef MNG_OBJECT_BUFFERS
5744 if (mng_info->ob[i] != (MngBuffer *) NULL)
5745 mng_info->ob[i]->frozen=MagickTrue;
5746#endif
5747 }
glennrp0fe50b42010-11-16 03:52:51 +00005748
glennrp8fe91592014-10-25 13:57:25 +00005749 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00005750 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005751
cristy3ed852e2009-09-05 21:47:34 +00005752 continue;
5753 }
5754
5755 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5756 {
glennrp47b9dd52010-11-24 18:12:06 +00005757 /* Read DISC or SEEK. */
5758
cristy3ed852e2009-09-05 21:47:34 +00005759 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5760 {
5761 for (i=1; i < MNG_MAX_OBJECTS; i++)
5762 MngInfoDiscardObject(mng_info,i);
5763 }
glennrp0fe50b42010-11-16 03:52:51 +00005764
cristy3ed852e2009-09-05 21:47:34 +00005765 else
5766 {
cristybb503372010-05-27 20:51:26 +00005767 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005768 j;
5769
Cristyf8d9cb82015-09-11 11:51:22 -04005770 for (j=1; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005771 {
Cristyf8d9cb82015-09-11 11:51:22 -04005772 i=p[j-1] << 8 | p[j];
cristy3ed852e2009-09-05 21:47:34 +00005773 MngInfoDiscardObject(mng_info,i);
5774 }
5775 }
glennrp0fe50b42010-11-16 03:52:51 +00005776
glennrp8fe91592014-10-25 13:57:25 +00005777 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00005778 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005779
cristy3ed852e2009-09-05 21:47:34 +00005780 continue;
5781 }
glennrp47b9dd52010-11-24 18:12:06 +00005782
cristy3ed852e2009-09-05 21:47:34 +00005783 if (memcmp(type,mng_MOVE,4) == 0)
5784 {
cristybb503372010-05-27 20:51:26 +00005785 size_t
cristy3ed852e2009-09-05 21:47:34 +00005786 first_object,
5787 last_object;
5788
glennrp47b9dd52010-11-24 18:12:06 +00005789 /* read MOVE */
5790
glennrpa454cdc2015-01-24 01:10:45 +00005791 if (length > 3)
cristy3ed852e2009-09-05 21:47:34 +00005792 {
glennrpa454cdc2015-01-24 01:10:45 +00005793 first_object=(p[0] << 8) | p[1];
5794 last_object=(p[2] << 8) | p[3];
5795 p+=4;
cristy3ed852e2009-09-05 21:47:34 +00005796
glennrpa454cdc2015-01-24 01:10:45 +00005797 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5798 {
5799 if (mng_info->exists[i] && !mng_info->frozen[i] &&
5800 (p-chunk) < (ssize_t) (length-8))
5801 {
5802 MngPair
5803 new_pair;
cristy3ed852e2009-09-05 21:47:34 +00005804
glennrpa454cdc2015-01-24 01:10:45 +00005805 MngPair
5806 old_pair;
5807
5808 old_pair.a=mng_info->x_off[i];
5809 old_pair.b=mng_info->y_off[i];
5810 new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
5811 mng_info->x_off[i]=new_pair.a;
5812 mng_info->y_off[i]=new_pair.b;
5813 }
5814 }
cristy3ed852e2009-09-05 21:47:34 +00005815 }
glennrp47b9dd52010-11-24 18:12:06 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5818 continue;
5819 }
5820
5821 if (memcmp(type,mng_LOOP,4) == 0)
5822 {
cristybb503372010-05-27 20:51:26 +00005823 ssize_t loop_iters=1;
glennrp24e6d422015-01-24 03:54:57 +00005824 if (length > 4)
cristy3ed852e2009-09-05 21:47:34 +00005825 {
glennrpa454cdc2015-01-24 01:10:45 +00005826 loop_level=chunk[0];
5827 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp0fe50b42010-11-16 03:52:51 +00005828
glennrpa454cdc2015-01-24 01:10:45 +00005829 /* Record starting point. */
5830 loop_iters=mng_get_long(&chunk[1]);
5831
5832 if (logging != MagickFalse)
5833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5834 " LOOP level %.20g has %.20g iterations ",
5835 (double) loop_level, (double) loop_iters);
5836
5837 if (loop_iters == 0)
5838 skipping_loop=loop_level;
5839
5840 else
5841 {
5842 mng_info->loop_jump[loop_level]=TellBlob(image);
5843 mng_info->loop_count[loop_level]=loop_iters;
5844 }
5845
5846 mng_info->loop_iteration[loop_level]=0;
5847 }
cristy3ed852e2009-09-05 21:47:34 +00005848 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5849 continue;
5850 }
glennrp47b9dd52010-11-24 18:12:06 +00005851
cristy3ed852e2009-09-05 21:47:34 +00005852 if (memcmp(type,mng_ENDL,4) == 0)
5853 {
glennrp24e6d422015-01-24 03:54:57 +00005854 if (length > 0)
cristy3ed852e2009-09-05 21:47:34 +00005855 {
glennrp24e6d422015-01-24 03:54:57 +00005856 loop_level=chunk[0];
5857
5858 if (skipping_loop > 0)
cristy3ed852e2009-09-05 21:47:34 +00005859 {
glennrp24e6d422015-01-24 03:54:57 +00005860 if (skipping_loop == loop_level)
cristy3ed852e2009-09-05 21:47:34 +00005861 {
cristy3ed852e2009-09-05 21:47:34 +00005862 /*
glennrp24e6d422015-01-24 03:54:57 +00005863 Found end of zero-iteration loop.
cristy3ed852e2009-09-05 21:47:34 +00005864 */
glennrp24e6d422015-01-24 03:54:57 +00005865 skipping_loop=(-1);
cristy3ed852e2009-09-05 21:47:34 +00005866 mng_info->loop_active[loop_level]=0;
glennrp24e6d422015-01-24 03:54:57 +00005867 }
5868 }
5869
5870 else
5871 {
5872 if (mng_info->loop_active[loop_level] == 1)
5873 {
5874 mng_info->loop_count[loop_level]--;
5875 mng_info->loop_iteration[loop_level]++;
5876
5877 if (logging != MagickFalse)
5878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5879 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5880 (double) loop_level,(double)
5881 mng_info->loop_count[loop_level]);
5882
5883 if (mng_info->loop_count[loop_level] != 0)
5884 {
5885 offset=
5886 SeekBlob(image,mng_info->loop_jump[loop_level],
5887 SEEK_SET);
5888
5889 if (offset < 0)
5890 ThrowReaderException(CorruptImageError,
5891 "ImproperImageHeader");
5892 }
5893
5894 else
5895 {
5896 short
5897 last_level;
5898
5899 /*
5900 Finished loop.
5901 */
5902 mng_info->loop_active[loop_level]=0;
5903 last_level=(-1);
5904 for (i=0; i < loop_level; i++)
5905 if (mng_info->loop_active[i] == 1)
5906 last_level=(short) i;
5907 loop_level=last_level;
5908 }
cristy3ed852e2009-09-05 21:47:34 +00005909 }
5910 }
5911 }
glennrp47b9dd52010-11-24 18:12:06 +00005912
cristy3ed852e2009-09-05 21:47:34 +00005913 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5914 continue;
5915 }
glennrp47b9dd52010-11-24 18:12:06 +00005916
cristy3ed852e2009-09-05 21:47:34 +00005917 if (memcmp(type,mng_CLON,4) == 0)
5918 {
5919 if (mng_info->clon_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005920 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005921 CoderError,"CLON is not implemented yet","`%s'",
5922 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005923
cristy3ed852e2009-09-05 21:47:34 +00005924 mng_info->clon_warning++;
5925 }
glennrp47b9dd52010-11-24 18:12:06 +00005926
cristy3ed852e2009-09-05 21:47:34 +00005927 if (memcmp(type,mng_MAGN,4) == 0)
5928 {
5929 png_uint_16
5930 magn_first,
5931 magn_last,
5932 magn_mb,
5933 magn_ml,
5934 magn_mr,
5935 magn_mt,
5936 magn_mx,
5937 magn_my,
5938 magn_methx,
5939 magn_methy;
5940
5941 if (length > 1)
5942 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005943
cristy3ed852e2009-09-05 21:47:34 +00005944 else
5945 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005946
cristy3ed852e2009-09-05 21:47:34 +00005947 if (length > 3)
5948 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005949
cristy3ed852e2009-09-05 21:47:34 +00005950 else
5951 magn_last=magn_first;
5952#ifndef MNG_OBJECT_BUFFERS
5953 if (magn_first || magn_last)
5954 if (mng_info->magn_warning == 0)
5955 {
cristy16ea1392012-03-21 20:38:41 +00005956 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005957 GetMagickModule(),CoderError,
5958 "MAGN is not implemented yet for nonzero objects",
5959 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005960
cristy3ed852e2009-09-05 21:47:34 +00005961 mng_info->magn_warning++;
5962 }
5963#endif
5964 if (length > 4)
5965 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005966
cristy3ed852e2009-09-05 21:47:34 +00005967 else
5968 magn_methx=0;
5969
5970 if (length > 6)
5971 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005972
cristy3ed852e2009-09-05 21:47:34 +00005973 else
5974 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005975
cristy3ed852e2009-09-05 21:47:34 +00005976 if (magn_mx == 0)
5977 magn_mx=1;
5978
5979 if (length > 8)
5980 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005981
cristy3ed852e2009-09-05 21:47:34 +00005982 else
5983 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005984
cristy3ed852e2009-09-05 21:47:34 +00005985 if (magn_my == 0)
5986 magn_my=1;
5987
5988 if (length > 10)
5989 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005990
cristy3ed852e2009-09-05 21:47:34 +00005991 else
5992 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005993
cristy3ed852e2009-09-05 21:47:34 +00005994 if (magn_ml == 0)
5995 magn_ml=1;
5996
5997 if (length > 12)
5998 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005999
cristy3ed852e2009-09-05 21:47:34 +00006000 else
6001 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006002
cristy3ed852e2009-09-05 21:47:34 +00006003 if (magn_mr == 0)
6004 magn_mr=1;
6005
6006 if (length > 14)
6007 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00006008
cristy3ed852e2009-09-05 21:47:34 +00006009 else
6010 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006011
cristy3ed852e2009-09-05 21:47:34 +00006012 if (magn_mt == 0)
6013 magn_mt=1;
6014
6015 if (length > 16)
6016 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00006017
cristy3ed852e2009-09-05 21:47:34 +00006018 else
6019 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006020
cristy3ed852e2009-09-05 21:47:34 +00006021 if (magn_mb == 0)
6022 magn_mb=1;
6023
6024 if (length > 17)
6025 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00006026
cristy3ed852e2009-09-05 21:47:34 +00006027 else
6028 magn_methy=magn_methx;
6029
glennrp47b9dd52010-11-24 18:12:06 +00006030
cristy3ed852e2009-09-05 21:47:34 +00006031 if (magn_methx > 5 || magn_methy > 5)
6032 if (mng_info->magn_warning == 0)
6033 {
cristy16ea1392012-03-21 20:38:41 +00006034 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00006035 GetMagickModule(),CoderError,
6036 "Unknown MAGN method in MNG datastream","`%s'",
6037 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006038
cristy3ed852e2009-09-05 21:47:34 +00006039 mng_info->magn_warning++;
6040 }
6041#ifdef MNG_OBJECT_BUFFERS
6042 /* Magnify existing objects in the range magn_first to magn_last */
6043#endif
6044 if (magn_first == 0 || magn_last == 0)
6045 {
6046 /* Save the magnification factors for object 0 */
6047 mng_info->magn_mb=magn_mb;
6048 mng_info->magn_ml=magn_ml;
6049 mng_info->magn_mr=magn_mr;
6050 mng_info->magn_mt=magn_mt;
6051 mng_info->magn_mx=magn_mx;
6052 mng_info->magn_my=magn_my;
6053 mng_info->magn_methx=magn_methx;
6054 mng_info->magn_methy=magn_methy;
6055 }
6056 }
glennrp47b9dd52010-11-24 18:12:06 +00006057
cristy3ed852e2009-09-05 21:47:34 +00006058 if (memcmp(type,mng_PAST,4) == 0)
6059 {
6060 if (mng_info->past_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006061 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006062 CoderError,"PAST is not implemented yet","`%s'",
6063 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006064
cristy3ed852e2009-09-05 21:47:34 +00006065 mng_info->past_warning++;
6066 }
glennrp47b9dd52010-11-24 18:12:06 +00006067
cristy3ed852e2009-09-05 21:47:34 +00006068 if (memcmp(type,mng_SHOW,4) == 0)
6069 {
6070 if (mng_info->show_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006071 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006072 CoderError,"SHOW is not implemented yet","`%s'",
6073 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006074
cristy3ed852e2009-09-05 21:47:34 +00006075 mng_info->show_warning++;
6076 }
glennrp47b9dd52010-11-24 18:12:06 +00006077
cristy3ed852e2009-09-05 21:47:34 +00006078 if (memcmp(type,mng_sBIT,4) == 0)
6079 {
6080 if (length < 4)
6081 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006082
cristy3ed852e2009-09-05 21:47:34 +00006083 else
6084 {
6085 mng_info->global_sbit.gray=p[0];
6086 mng_info->global_sbit.red=p[0];
6087 mng_info->global_sbit.green=p[1];
6088 mng_info->global_sbit.blue=p[2];
6089 mng_info->global_sbit.alpha=p[3];
6090 mng_info->have_global_sbit=MagickTrue;
6091 }
6092 }
6093 if (memcmp(type,mng_pHYs,4) == 0)
6094 {
6095 if (length > 8)
6096 {
6097 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00006098 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00006099 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00006100 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00006101 mng_info->global_phys_unit_type=p[8];
6102 mng_info->have_global_phys=MagickTrue;
6103 }
glennrp47b9dd52010-11-24 18:12:06 +00006104
cristy3ed852e2009-09-05 21:47:34 +00006105 else
6106 mng_info->have_global_phys=MagickFalse;
6107 }
6108 if (memcmp(type,mng_pHYg,4) == 0)
6109 {
6110 if (mng_info->phyg_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006111 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006112 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006113
cristy3ed852e2009-09-05 21:47:34 +00006114 mng_info->phyg_warning++;
6115 }
6116 if (memcmp(type,mng_BASI,4) == 0)
6117 {
6118 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00006119
cristy3ed852e2009-09-05 21:47:34 +00006120 if (mng_info->basi_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006121 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006122 CoderError,"BASI is not implemented yet","`%s'",
6123 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006124
cristy3ed852e2009-09-05 21:47:34 +00006125 mng_info->basi_warning++;
6126#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00006127 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00006128 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00006129 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00006130 (p[6] << 8) | p[7]);
6131 basi_color_type=p[8];
6132 basi_compression_method=p[9];
6133 basi_filter_type=p[10];
6134 basi_interlace_method=p[11];
6135 if (length > 11)
6136 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00006137
cristy3ed852e2009-09-05 21:47:34 +00006138 else
6139 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00006140
cristy3ed852e2009-09-05 21:47:34 +00006141 if (length > 13)
6142 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00006143
cristy3ed852e2009-09-05 21:47:34 +00006144 else
6145 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00006146
cristy3ed852e2009-09-05 21:47:34 +00006147 if (length > 15)
6148 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00006149
cristy3ed852e2009-09-05 21:47:34 +00006150 else
6151 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00006152
cristy3ed852e2009-09-05 21:47:34 +00006153 if (length > 17)
6154 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00006155
cristy3ed852e2009-09-05 21:47:34 +00006156 else
6157 {
6158 if (basi_sample_depth == 16)
6159 basi_alpha=65535L;
6160 else
6161 basi_alpha=255;
6162 }
glennrp47b9dd52010-11-24 18:12:06 +00006163
cristy3ed852e2009-09-05 21:47:34 +00006164 if (length > 19)
6165 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00006166
cristy3ed852e2009-09-05 21:47:34 +00006167 else
6168 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00006169
cristy3ed852e2009-09-05 21:47:34 +00006170#endif
6171 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6172 continue;
6173 }
glennrp47b9dd52010-11-24 18:12:06 +00006174
cristy3ed852e2009-09-05 21:47:34 +00006175 if (memcmp(type,mng_IHDR,4)
6176#if defined(JNG_SUPPORTED)
6177 && memcmp(type,mng_JHDR,4)
6178#endif
6179 )
6180 {
6181 /* Not an IHDR or JHDR chunk */
glennrp8fe91592014-10-25 13:57:25 +00006182 if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +00006183 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00006184
cristy3ed852e2009-09-05 21:47:34 +00006185 continue;
6186 }
6187/* Process IHDR */
6188 if (logging != MagickFalse)
6189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6190 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006191
cristy3ed852e2009-09-05 21:47:34 +00006192 mng_info->exists[object_id]=MagickTrue;
6193 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00006194
cristy3ed852e2009-09-05 21:47:34 +00006195 if (mng_info->invisible[object_id])
6196 {
6197 if (logging != MagickFalse)
6198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6199 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00006200
cristy3ed852e2009-09-05 21:47:34 +00006201 skip_to_iend=MagickTrue;
6202 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6203 continue;
6204 }
6205#if defined(MNG_INSERT_LAYERS)
6206 if (length < 8)
6207 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00006208
cristy8182b072010-05-30 20:10:53 +00006209 image_width=(size_t) mng_get_long(p);
6210 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00006211#endif
6212 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6213
6214 /*
6215 Insert a transparent background layer behind the entire animation
6216 if it is not full screen.
6217 */
6218#if defined(MNG_INSERT_LAYERS)
6219 if (insert_layers && mng_type && first_mng_object)
6220 {
6221 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6222 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00006223 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00006224 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00006225 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00006226 {
cristy16ea1392012-03-21 20:38:41 +00006227 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006228 {
6229 /*
6230 Allocate next image structure.
6231 */
cristy16ea1392012-03-21 20:38:41 +00006232 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006233
cristy3ed852e2009-09-05 21:47:34 +00006234 if (GetNextImageInList(image) == (Image *) NULL)
6235 {
6236 image=DestroyImageList(image);
6237 MngInfoFreeStruct(mng_info,&have_mng_structure);
6238 return((Image *) NULL);
6239 }
glennrp47b9dd52010-11-24 18:12:06 +00006240
cristy3ed852e2009-09-05 21:47:34 +00006241 image=SyncNextImageInList(image);
6242 }
6243 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00006244
cristy3ed852e2009-09-05 21:47:34 +00006245 if (term_chunk_found)
6246 {
6247 image->start_loop=MagickTrue;
6248 image->iterations=mng_iterations;
6249 term_chunk_found=MagickFalse;
6250 }
glennrp47b9dd52010-11-24 18:12:06 +00006251
cristy3ed852e2009-09-05 21:47:34 +00006252 else
6253 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006254
6255 /* Make a background rectangle. */
6256
cristy3ed852e2009-09-05 21:47:34 +00006257 image->delay=0;
6258 image->columns=mng_info->mng_width;
6259 image->rows=mng_info->mng_height;
6260 image->page.width=mng_info->mng_width;
6261 image->page.height=mng_info->mng_height;
6262 image->page.x=0;
6263 image->page.y=0;
6264 image->background_color=mng_background_color;
cristy16ea1392012-03-21 20:38:41 +00006265 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006266 if (logging != MagickFalse)
6267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006268 " Inserted transparent background layer, W=%.20g, H=%.20g",
6269 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00006270 }
6271 }
6272 /*
6273 Insert a background layer behind the upcoming image if
6274 framing_mode is 3, and we haven't already inserted one.
6275 */
6276 if (insert_layers && (mng_info->framing_mode == 3) &&
6277 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6278 (simplicity & 0x08)))
6279 {
cristy16ea1392012-03-21 20:38:41 +00006280 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006281 {
6282 /*
6283 Allocate next image structure.
6284 */
cristy16ea1392012-03-21 20:38:41 +00006285 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006286
cristy3ed852e2009-09-05 21:47:34 +00006287 if (GetNextImageInList(image) == (Image *) NULL)
6288 {
6289 image=DestroyImageList(image);
6290 MngInfoFreeStruct(mng_info,&have_mng_structure);
6291 return((Image *) NULL);
6292 }
glennrp47b9dd52010-11-24 18:12:06 +00006293
cristy3ed852e2009-09-05 21:47:34 +00006294 image=SyncNextImageInList(image);
6295 }
glennrp0fe50b42010-11-16 03:52:51 +00006296
cristy3ed852e2009-09-05 21:47:34 +00006297 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00006298
cristy3ed852e2009-09-05 21:47:34 +00006299 if (term_chunk_found)
6300 {
6301 image->start_loop=MagickTrue;
6302 image->iterations=mng_iterations;
6303 term_chunk_found=MagickFalse;
6304 }
glennrp0fe50b42010-11-16 03:52:51 +00006305
cristy3ed852e2009-09-05 21:47:34 +00006306 else
6307 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006308
cristy3ed852e2009-09-05 21:47:34 +00006309 image->delay=0;
6310 image->columns=subframe_width;
6311 image->rows=subframe_height;
6312 image->page.width=subframe_width;
6313 image->page.height=subframe_height;
6314 image->page.x=mng_info->clip.left;
6315 image->page.y=mng_info->clip.top;
6316 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00006317 image->alpha_trait=UndefinedPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00006318 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006319
cristy3ed852e2009-09-05 21:47:34 +00006320 if (logging != MagickFalse)
6321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006322 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006323 (double) mng_info->clip.left,(double) mng_info->clip.right,
6324 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006325 }
6326#endif /* MNG_INSERT_LAYERS */
6327 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006328
cristy16ea1392012-03-21 20:38:41 +00006329 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006330 {
6331 /*
6332 Allocate next image structure.
6333 */
cristy16ea1392012-03-21 20:38:41 +00006334 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006335
cristy3ed852e2009-09-05 21:47:34 +00006336 if (GetNextImageInList(image) == (Image *) NULL)
6337 {
6338 image=DestroyImageList(image);
6339 MngInfoFreeStruct(mng_info,&have_mng_structure);
6340 return((Image *) NULL);
6341 }
glennrp47b9dd52010-11-24 18:12:06 +00006342
cristy3ed852e2009-09-05 21:47:34 +00006343 image=SyncNextImageInList(image);
6344 }
6345 mng_info->image=image;
6346 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6347 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006348
cristy3ed852e2009-09-05 21:47:34 +00006349 if (status == MagickFalse)
6350 break;
glennrp0fe50b42010-11-16 03:52:51 +00006351
cristy3ed852e2009-09-05 21:47:34 +00006352 if (term_chunk_found)
6353 {
6354 image->start_loop=MagickTrue;
6355 term_chunk_found=MagickFalse;
6356 }
glennrp0fe50b42010-11-16 03:52:51 +00006357
cristy3ed852e2009-09-05 21:47:34 +00006358 else
6359 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006360
cristy3ed852e2009-09-05 21:47:34 +00006361 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6362 {
6363 image->delay=frame_delay;
6364 frame_delay=default_frame_delay;
6365 }
glennrp0fe50b42010-11-16 03:52:51 +00006366
cristy3ed852e2009-09-05 21:47:34 +00006367 else
6368 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006369
cristy3ed852e2009-09-05 21:47:34 +00006370 image->page.width=mng_info->mng_width;
6371 image->page.height=mng_info->mng_height;
6372 image->page.x=mng_info->x_off[object_id];
6373 image->page.y=mng_info->y_off[object_id];
6374 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006375
cristy3ed852e2009-09-05 21:47:34 +00006376 /*
6377 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6378 */
glennrp47b9dd52010-11-24 18:12:06 +00006379
cristy3ed852e2009-09-05 21:47:34 +00006380 if (logging != MagickFalse)
6381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6382 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6383 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006384
cristybb503372010-05-27 20:51:26 +00006385 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006386
cristy3ed852e2009-09-05 21:47:34 +00006387 if (offset < 0)
6388 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6389 }
6390
cristy3ed852e2009-09-05 21:47:34 +00006391 mng_info->image=image;
6392 mng_info->mng_type=mng_type;
6393 mng_info->object_id=object_id;
6394
6395 if (memcmp(type,mng_IHDR,4) == 0)
6396 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006397
cristy3ed852e2009-09-05 21:47:34 +00006398#if defined(JNG_SUPPORTED)
6399 else
6400 image=ReadOneJNGImage(mng_info,image_info,exception);
6401#endif
6402
6403 if (image == (Image *) NULL)
6404 {
glennrp889a9282015-04-09 16:51:44 +00006405 if (logging != MagickFalse)
6406 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6407 "exit ReadJNGImage() with error");
glennrp47b9dd52010-11-24 18:12:06 +00006408
cristy3ed852e2009-09-05 21:47:34 +00006409 MngInfoFreeStruct(mng_info,&have_mng_structure);
6410 return((Image *) NULL);
6411 }
glennrp0fe50b42010-11-16 03:52:51 +00006412
cristy3ed852e2009-09-05 21:47:34 +00006413 if (image->columns == 0 || image->rows == 0)
6414 {
6415 (void) CloseBlob(image);
6416 image=DestroyImageList(image);
6417 MngInfoFreeStruct(mng_info,&have_mng_structure);
6418 return((Image *) NULL);
6419 }
glennrp0fe50b42010-11-16 03:52:51 +00006420
cristy3ed852e2009-09-05 21:47:34 +00006421 mng_info->image=image;
6422
6423 if (mng_type)
6424 {
6425 MngBox
6426 crop_box;
6427
6428 if (mng_info->magn_methx || mng_info->magn_methy)
6429 {
6430 png_uint_32
6431 magnified_height,
6432 magnified_width;
6433
6434 if (logging != MagickFalse)
6435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6436 " Processing MNG MAGN chunk");
6437
6438 if (mng_info->magn_methx == 1)
6439 {
6440 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006441
cristy3ed852e2009-09-05 21:47:34 +00006442 if (image->columns > 1)
6443 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006444
cristy3ed852e2009-09-05 21:47:34 +00006445 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006446 magnified_width += (png_uint_32)
6447 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006448 }
glennrp47b9dd52010-11-24 18:12:06 +00006449
cristy3ed852e2009-09-05 21:47:34 +00006450 else
6451 {
cristy4e5bc842010-06-09 13:56:01 +00006452 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006453
cristy3ed852e2009-09-05 21:47:34 +00006454 if (image->columns > 1)
6455 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006456
cristy3ed852e2009-09-05 21:47:34 +00006457 if (image->columns > 2)
6458 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006459
cristy3ed852e2009-09-05 21:47:34 +00006460 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006461 magnified_width += (png_uint_32)
6462 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006463 }
glennrp47b9dd52010-11-24 18:12:06 +00006464
cristy3ed852e2009-09-05 21:47:34 +00006465 if (mng_info->magn_methy == 1)
6466 {
6467 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006468
cristy3ed852e2009-09-05 21:47:34 +00006469 if (image->rows > 1)
6470 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006471
cristy3ed852e2009-09-05 21:47:34 +00006472 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006473 magnified_height += (png_uint_32)
6474 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006475 }
glennrp47b9dd52010-11-24 18:12:06 +00006476
cristy3ed852e2009-09-05 21:47:34 +00006477 else
6478 {
cristy4e5bc842010-06-09 13:56:01 +00006479 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006480
cristy3ed852e2009-09-05 21:47:34 +00006481 if (image->rows > 1)
6482 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006483
cristy3ed852e2009-09-05 21:47:34 +00006484 if (image->rows > 2)
6485 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006486
cristy3ed852e2009-09-05 21:47:34 +00006487 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006488 magnified_height += (png_uint_32)
6489 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006490 }
glennrp47b9dd52010-11-24 18:12:06 +00006491
cristy3ed852e2009-09-05 21:47:34 +00006492 if (magnified_height > image->rows ||
6493 magnified_width > image->columns)
6494 {
6495 Image
6496 *large_image;
6497
6498 int
6499 yy;
6500
cristy16ea1392012-03-21 20:38:41 +00006501 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006502 *next,
6503 *prev;
6504
6505 png_uint_16
6506 magn_methx,
6507 magn_methy;
6508
cristy16ea1392012-03-21 20:38:41 +00006509 ssize_t
6510 m,
6511 y;
6512
6513 register Quantum
6514 *n,
6515 *q;
6516
6517 register ssize_t
6518 x;
6519
glennrp47b9dd52010-11-24 18:12:06 +00006520 /* Allocate next image structure. */
6521
cristy3ed852e2009-09-05 21:47:34 +00006522 if (logging != MagickFalse)
6523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6524 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006525
cristy16ea1392012-03-21 20:38:41 +00006526 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006527
cristy3ed852e2009-09-05 21:47:34 +00006528 if (GetNextImageInList(image) == (Image *) NULL)
6529 {
6530 image=DestroyImageList(image);
6531 MngInfoFreeStruct(mng_info,&have_mng_structure);
6532 return((Image *) NULL);
6533 }
6534
6535 large_image=SyncNextImageInList(image);
6536
6537 large_image->columns=magnified_width;
6538 large_image->rows=magnified_height;
6539
6540 magn_methx=mng_info->magn_methx;
6541 magn_methy=mng_info->magn_methy;
6542
glennrp3faa9a32011-04-23 14:00:25 +00006543#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006544#define QM unsigned short
6545 if (magn_methx != 1 || magn_methy != 1)
6546 {
6547 /*
6548 Scale pixels to unsigned shorts to prevent
6549 overflow of intermediate values of interpolations
6550 */
cristybb503372010-05-27 20:51:26 +00006551 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006552 {
6553 q=GetAuthenticPixels(image,0,y,image->columns,1,
6554 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006555
cristybb503372010-05-27 20:51:26 +00006556 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006557 {
cristy16ea1392012-03-21 20:38:41 +00006558 SetPixelRed(image,ScaleQuantumToShort(
6559 GetPixelRed(image,q)),q);
6560 SetPixelGreen(image,ScaleQuantumToShort(
6561 GetPixelGreen(image,q)),q);
6562 SetPixelBlue(image,ScaleQuantumToShort(
6563 GetPixelBlue(image,q)),q);
6564 SetPixelAlpha(image,ScaleQuantumToShort(
6565 GetPixelAlpha(image,q)),q);
6566 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006567 }
glennrp47b9dd52010-11-24 18:12:06 +00006568
cristy3ed852e2009-09-05 21:47:34 +00006569 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6570 break;
6571 }
6572 }
6573#else
6574#define QM Quantum
6575#endif
6576
cristy17f11b02014-12-20 19:37:04 +00006577 if (image->alpha_trait != UndefinedPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006578 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006579
cristy3ed852e2009-09-05 21:47:34 +00006580 else
6581 {
cristy16ea1392012-03-21 20:38:41 +00006582 large_image->background_color.alpha=OpaqueAlpha;
6583 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006584
cristy3ed852e2009-09-05 21:47:34 +00006585 if (magn_methx == 4)
6586 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006587
cristy3ed852e2009-09-05 21:47:34 +00006588 if (magn_methx == 5)
6589 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006590
cristy3ed852e2009-09-05 21:47:34 +00006591 if (magn_methy == 4)
6592 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006593
cristy3ed852e2009-09-05 21:47:34 +00006594 if (magn_methy == 5)
6595 magn_methy=3;
6596 }
6597
6598 /* magnify the rows into the right side of the large image */
6599
6600 if (logging != MagickFalse)
6601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006602 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006603 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006604 yy=0;
cristy4f7c4342014-12-07 15:55:04 +00006605 length=(size_t) GetPixelChannels(image)*image->columns;
cristy16ea1392012-03-21 20:38:41 +00006606 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6607 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006608
cristy16ea1392012-03-21 20:38:41 +00006609 if ((prev == (Quantum *) NULL) ||
6610 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006611 {
6612 image=DestroyImageList(image);
6613 MngInfoFreeStruct(mng_info,&have_mng_structure);
6614 ThrowReaderException(ResourceLimitError,
6615 "MemoryAllocationFailed");
6616 }
glennrp47b9dd52010-11-24 18:12:06 +00006617
cristy3ed852e2009-09-05 21:47:34 +00006618 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6619 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006620
cristybb503372010-05-27 20:51:26 +00006621 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006622 {
6623 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006624 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006625
cristybb503372010-05-27 20:51:26 +00006626 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6627 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006628
cristybb503372010-05-27 20:51:26 +00006629 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6630 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006631
cristybb503372010-05-27 20:51:26 +00006632 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006633 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006634
cristy3ed852e2009-09-05 21:47:34 +00006635 else
cristybb503372010-05-27 20:51:26 +00006636 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006637
cristy3ed852e2009-09-05 21:47:34 +00006638 n=prev;
6639 prev=next;
6640 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006641
cristybb503372010-05-27 20:51:26 +00006642 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006643 {
6644 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6645 exception);
6646 (void) CopyMagickMemory(next,n,length);
6647 }
glennrp47b9dd52010-11-24 18:12:06 +00006648
cristy3ed852e2009-09-05 21:47:34 +00006649 for (i=0; i < m; i++, yy++)
6650 {
cristy16ea1392012-03-21 20:38:41 +00006651 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006652 *pixels;
6653
cristybb503372010-05-27 20:51:26 +00006654 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006655 pixels=prev;
6656 n=next;
6657 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006658 1,exception);
cristy16ea1392012-03-21 20:38:41 +00006659 q+=(large_image->columns-image->columns)*
6660 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006661
cristybb503372010-05-27 20:51:26 +00006662 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006663 {
glennrpfd05d622011-02-25 04:10:33 +00006664 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006665 /*
6666 if (image->storage_class == PseudoClass)
6667 {
6668 }
6669 */
6670
6671 if (magn_methy <= 1)
6672 {
glennrpbb4f99d2011-05-22 11:13:17 +00006673 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006674 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6675 SetPixelGreen(large_image,GetPixelGreen(image,
6676 pixels),q);
6677 SetPixelBlue(large_image,GetPixelBlue(image,
6678 pixels),q);
6679 SetPixelAlpha(large_image,GetPixelAlpha(image,
6680 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006681 }
glennrp47b9dd52010-11-24 18:12:06 +00006682
cristy3ed852e2009-09-05 21:47:34 +00006683 else if (magn_methy == 2 || magn_methy == 4)
6684 {
6685 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006686 {
cristy16ea1392012-03-21 20:38:41 +00006687 SetPixelRed(large_image,GetPixelRed(image,
6688 pixels),q);
6689 SetPixelGreen(large_image,GetPixelGreen(image,
6690 pixels),q);
6691 SetPixelBlue(large_image,GetPixelBlue(image,
6692 pixels),q);
6693 SetPixelAlpha(large_image,GetPixelAlpha(image,
6694 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006695 }
glennrp47b9dd52010-11-24 18:12:06 +00006696
cristy3ed852e2009-09-05 21:47:34 +00006697 else
6698 {
6699 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006700 SetPixelRed(large_image,((QM) (((ssize_t)
6701 (2*i*(GetPixelRed(image,n)
6702 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006703 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006704 +GetPixelRed(image,pixels)))),q);
6705 SetPixelGreen(large_image,((QM) (((ssize_t)
6706 (2*i*(GetPixelGreen(image,n)
6707 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006708 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006709 +GetPixelGreen(image,pixels)))),q);
6710 SetPixelBlue(large_image,((QM) (((ssize_t)
6711 (2*i*(GetPixelBlue(image,n)
6712 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006713 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006714 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006715
cristy17f11b02014-12-20 19:37:04 +00006716 if (image->alpha_trait != UndefinedPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006717 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6718 (2*i*(GetPixelAlpha(image,n)
6719 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006720 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006721 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006722 }
glennrp47b9dd52010-11-24 18:12:06 +00006723
cristy3ed852e2009-09-05 21:47:34 +00006724 if (magn_methy == 4)
6725 {
6726 /* Replicate nearest */
6727 if (i <= ((m+1) << 1))
cristy16ea1392012-03-21 20:38:41 +00006728 SetPixelAlpha(large_image,GetPixelAlpha(image,
6729 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006730 else
cristy16ea1392012-03-21 20:38:41 +00006731 SetPixelAlpha(large_image,GetPixelAlpha(image,
6732 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006733 }
6734 }
glennrp47b9dd52010-11-24 18:12:06 +00006735
cristy3ed852e2009-09-05 21:47:34 +00006736 else /* if (magn_methy == 3 || magn_methy == 5) */
6737 {
6738 /* Replicate nearest */
6739 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006740 {
cristy16ea1392012-03-21 20:38:41 +00006741 SetPixelRed(large_image,GetPixelRed(image,
6742 pixels),q);
6743 SetPixelGreen(large_image,GetPixelGreen(image,
6744 pixels),q);
6745 SetPixelBlue(large_image,GetPixelBlue(image,
6746 pixels),q);
6747 SetPixelAlpha(large_image,GetPixelAlpha(image,
6748 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006749 }
glennrp47b9dd52010-11-24 18:12:06 +00006750
cristy3ed852e2009-09-05 21:47:34 +00006751 else
glennrpbb4f99d2011-05-22 11:13:17 +00006752 {
cristy16ea1392012-03-21 20:38:41 +00006753 SetPixelRed(large_image,GetPixelRed(image,n),q);
6754 SetPixelGreen(large_image,GetPixelGreen(image,n),
6755 q);
6756 SetPixelBlue(large_image,GetPixelBlue(image,n),
6757 q);
6758 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6759 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006760 }
glennrp47b9dd52010-11-24 18:12:06 +00006761
cristy3ed852e2009-09-05 21:47:34 +00006762 if (magn_methy == 5)
6763 {
cristy16ea1392012-03-21 20:38:41 +00006764 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6765 (GetPixelAlpha(image,n)
6766 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006767 +m))/((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006768 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006769 }
6770 }
cristy16ea1392012-03-21 20:38:41 +00006771 n+=GetPixelChannels(image);
6772 q+=GetPixelChannels(large_image);
6773 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006774 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006775
cristy3ed852e2009-09-05 21:47:34 +00006776 if (SyncAuthenticPixels(large_image,exception) == 0)
6777 break;
glennrp47b9dd52010-11-24 18:12:06 +00006778
cristy3ed852e2009-09-05 21:47:34 +00006779 } /* i */
6780 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006781
cristy16ea1392012-03-21 20:38:41 +00006782 prev=(Quantum *) RelinquishMagickMemory(prev);
6783 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006784
6785 length=image->columns;
6786
6787 if (logging != MagickFalse)
6788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6789 " Delete original image");
6790
6791 DeleteImageFromList(&image);
6792
6793 image=large_image;
6794
6795 mng_info->image=image;
6796
6797 /* magnify the columns */
6798 if (logging != MagickFalse)
6799 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006800 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006801
cristybb503372010-05-27 20:51:26 +00006802 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006803 {
cristy16ea1392012-03-21 20:38:41 +00006804 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006805 *pixels;
6806
6807 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00006808 pixels=q+(image->columns-length)*GetPixelChannels(image);
6809 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006810
cristybb503372010-05-27 20:51:26 +00006811 for (x=(ssize_t) (image->columns-length);
6812 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006813 {
cristy16ea1392012-03-21 20:38:41 +00006814 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006815
cristybb503372010-05-27 20:51:26 +00006816 if (x == (ssize_t) (image->columns-length))
6817 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006818
cristybb503372010-05-27 20:51:26 +00006819 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6820 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006821
cristybb503372010-05-27 20:51:26 +00006822 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6823 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006824
cristybb503372010-05-27 20:51:26 +00006825 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006826 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006827
cristy3ed852e2009-09-05 21:47:34 +00006828 else
cristybb503372010-05-27 20:51:26 +00006829 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006830
cristy3ed852e2009-09-05 21:47:34 +00006831 for (i=0; i < m; i++)
6832 {
6833 if (magn_methx <= 1)
6834 {
6835 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006836 SetPixelRed(image,GetPixelRed(image,pixels),q);
6837 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6838 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6839 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006840 }
glennrp47b9dd52010-11-24 18:12:06 +00006841
cristy3ed852e2009-09-05 21:47:34 +00006842 else if (magn_methx == 2 || magn_methx == 4)
6843 {
6844 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006845 {
cristy16ea1392012-03-21 20:38:41 +00006846 SetPixelRed(image,GetPixelRed(image,pixels),q);
6847 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6848 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6849 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006850 }
glennrp47b9dd52010-11-24 18:12:06 +00006851
cristy16ea1392012-03-21 20:38:41 +00006852 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006853 else
6854 {
6855 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006856 SetPixelRed(image,(QM) ((2*i*(
6857 GetPixelRed(image,n)
6858 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006859 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006860 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006861
cristy16ea1392012-03-21 20:38:41 +00006862 SetPixelGreen(image,(QM) ((2*i*(
6863 GetPixelGreen(image,n)
6864 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006865 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006866 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006867
cristy16ea1392012-03-21 20:38:41 +00006868 SetPixelBlue(image,(QM) ((2*i*(
6869 GetPixelBlue(image,n)
6870 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006871 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006872 GetPixelBlue(image,pixels)),q);
cristy17f11b02014-12-20 19:37:04 +00006873 if (image->alpha_trait != UndefinedPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006874 SetPixelAlpha(image,(QM) ((2*i*(
6875 GetPixelAlpha(image,n)
6876 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006877 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006878 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006879 }
glennrp47b9dd52010-11-24 18:12:06 +00006880
cristy3ed852e2009-09-05 21:47:34 +00006881 if (magn_methx == 4)
6882 {
6883 /* Replicate nearest */
6884 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006885 {
cristy16ea1392012-03-21 20:38:41 +00006886 SetPixelAlpha(image,
6887 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006888 }
cristy3ed852e2009-09-05 21:47:34 +00006889 else
glennrpbb4f99d2011-05-22 11:13:17 +00006890 {
cristy16ea1392012-03-21 20:38:41 +00006891 SetPixelAlpha(image,
6892 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006893 }
cristy3ed852e2009-09-05 21:47:34 +00006894 }
6895 }
glennrp47b9dd52010-11-24 18:12:06 +00006896
cristy3ed852e2009-09-05 21:47:34 +00006897 else /* if (magn_methx == 3 || magn_methx == 5) */
6898 {
6899 /* Replicate nearest */
6900 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006901 {
cristy16ea1392012-03-21 20:38:41 +00006902 SetPixelRed(image,GetPixelRed(image,pixels),q);
6903 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6904 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6905 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006906 }
glennrp47b9dd52010-11-24 18:12:06 +00006907
cristy3ed852e2009-09-05 21:47:34 +00006908 else
glennrpbb4f99d2011-05-22 11:13:17 +00006909 {
cristy16ea1392012-03-21 20:38:41 +00006910 SetPixelRed(image,GetPixelRed(image,n),q);
6911 SetPixelGreen(image,GetPixelGreen(image,n),q);
6912 SetPixelBlue(image,GetPixelBlue(image,n),q);
6913 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006914 }
glennrp47b9dd52010-11-24 18:12:06 +00006915
cristy3ed852e2009-09-05 21:47:34 +00006916 if (magn_methx == 5)
6917 {
6918 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006919 SetPixelAlpha(image,
6920 (QM) ((2*i*( GetPixelAlpha(image,n)
6921 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006922 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006923 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006924 }
6925 }
cristy16ea1392012-03-21 20:38:41 +00006926 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006927 }
cristy16ea1392012-03-21 20:38:41 +00006928 n+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006929 }
glennrp47b9dd52010-11-24 18:12:06 +00006930
cristy3ed852e2009-09-05 21:47:34 +00006931 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6932 break;
6933 }
glennrp3faa9a32011-04-23 14:00:25 +00006934#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006935 if (magn_methx != 1 || magn_methy != 1)
6936 {
6937 /*
6938 Rescale pixels to Quantum
6939 */
cristybb503372010-05-27 20:51:26 +00006940 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006941 {
6942 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006943
cristybb503372010-05-27 20:51:26 +00006944 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006945 {
cristy16ea1392012-03-21 20:38:41 +00006946 SetPixelRed(image,ScaleShortToQuantum(
6947 GetPixelRed(image,q)),q);
6948 SetPixelGreen(image,ScaleShortToQuantum(
6949 GetPixelGreen(image,q)),q);
6950 SetPixelBlue(image,ScaleShortToQuantum(
6951 GetPixelBlue(image,q)),q);
6952 SetPixelAlpha(image,ScaleShortToQuantum(
6953 GetPixelAlpha(image,q)),q);
6954 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006955 }
glennrp47b9dd52010-11-24 18:12:06 +00006956
cristy3ed852e2009-09-05 21:47:34 +00006957 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6958 break;
6959 }
6960 }
6961#endif
6962 if (logging != MagickFalse)
6963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6964 " Finished MAGN processing");
6965 }
6966 }
6967
6968 /*
6969 Crop_box is with respect to the upper left corner of the MNG.
6970 */
6971 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6972 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6973 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6974 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6975 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6976 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6977 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6978 if ((crop_box.left != (mng_info->image_box.left
6979 +mng_info->x_off[object_id])) ||
6980 (crop_box.right != (mng_info->image_box.right
6981 +mng_info->x_off[object_id])) ||
6982 (crop_box.top != (mng_info->image_box.top
6983 +mng_info->y_off[object_id])) ||
6984 (crop_box.bottom != (mng_info->image_box.bottom
6985 +mng_info->y_off[object_id])))
6986 {
6987 if (logging != MagickFalse)
6988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6989 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006990
cristy3ed852e2009-09-05 21:47:34 +00006991 if ((crop_box.left < crop_box.right) &&
6992 (crop_box.top < crop_box.bottom))
6993 {
6994 Image
6995 *im;
6996
6997 RectangleInfo
6998 crop_info;
6999
7000 /*
7001 Crop_info is with respect to the upper left corner of
7002 the image.
7003 */
7004 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7005 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00007006 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7007 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00007008 image->page.width=image->columns;
7009 image->page.height=image->rows;
7010 image->page.x=0;
7011 image->page.y=0;
7012 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00007013
cristy3ed852e2009-09-05 21:47:34 +00007014 if (im != (Image *) NULL)
7015 {
7016 image->columns=im->columns;
7017 image->rows=im->rows;
7018 im=DestroyImage(im);
7019 image->page.width=image->columns;
7020 image->page.height=image->rows;
7021 image->page.x=crop_box.left;
7022 image->page.y=crop_box.top;
7023 }
7024 }
glennrp47b9dd52010-11-24 18:12:06 +00007025
cristy3ed852e2009-09-05 21:47:34 +00007026 else
7027 {
7028 /*
7029 No pixels in crop area. The MNG spec still requires
7030 a layer, though, so make a single transparent pixel in
7031 the top left corner.
7032 */
7033 image->columns=1;
7034 image->rows=1;
7035 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00007036 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007037 image->page.width=1;
7038 image->page.height=1;
7039 image->page.x=0;
7040 image->page.y=0;
7041 }
7042 }
7043#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7044 image=mng_info->image;
7045#endif
7046 }
7047
glennrp2b013e42010-11-24 16:55:50 +00007048#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7049 /* PNG does not handle depths greater than 16 so reduce it even
cristy16ea1392012-03-21 20:38:41 +00007050 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00007051 */
7052 if (image->depth > 16)
7053 image->depth=16;
7054#endif
7055
glennrp3faa9a32011-04-23 14:00:25 +00007056#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00007057 if (image->depth > 8)
7058 {
7059 /* To do: fill low byte properly */
7060 image->depth=16;
7061 }
7062
cristy16ea1392012-03-21 20:38:41 +00007063 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007064 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00007065#endif
glennrpd6afd542010-11-19 01:53:05 +00007066
cristy3ed852e2009-09-05 21:47:34 +00007067 if (image_info->number_scenes != 0)
7068 {
7069 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00007070 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00007071 break;
7072 }
glennrpd6afd542010-11-19 01:53:05 +00007073
cristy3ed852e2009-09-05 21:47:34 +00007074 if (logging != MagickFalse)
7075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7076 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00007077
cristy3ed852e2009-09-05 21:47:34 +00007078 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00007079
cristy3ed852e2009-09-05 21:47:34 +00007080 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00007081
cristy3ed852e2009-09-05 21:47:34 +00007082 if (logging != MagickFalse)
7083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7084 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00007085
cristy3ed852e2009-09-05 21:47:34 +00007086#if defined(MNG_INSERT_LAYERS)
7087 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7088 (mng_info->mng_height))
7089 {
7090 /*
7091 Insert a background layer if nothing else was found.
7092 */
7093 if (logging != MagickFalse)
7094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7095 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00007096
cristy16ea1392012-03-21 20:38:41 +00007097 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00007098 {
7099 /*
7100 Allocate next image structure.
7101 */
cristy16ea1392012-03-21 20:38:41 +00007102 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007103 if (GetNextImageInList(image) == (Image *) NULL)
7104 {
7105 image=DestroyImageList(image);
7106 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00007107
cristy3ed852e2009-09-05 21:47:34 +00007108 if (logging != MagickFalse)
7109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7110 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00007111
cristy3ed852e2009-09-05 21:47:34 +00007112 return((Image *) NULL);
7113 }
7114 image=SyncNextImageInList(image);
7115 }
7116 image->columns=mng_info->mng_width;
7117 image->rows=mng_info->mng_height;
7118 image->page.width=mng_info->mng_width;
7119 image->page.height=mng_info->mng_height;
7120 image->page.x=0;
7121 image->page.y=0;
7122 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00007123 image->alpha_trait=UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00007124
cristy3ed852e2009-09-05 21:47:34 +00007125 if (image_info->ping == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00007126 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007127
cristy3ed852e2009-09-05 21:47:34 +00007128 mng_info->image_found++;
7129 }
7130#endif
7131 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00007132
cristy3ed852e2009-09-05 21:47:34 +00007133 if (mng_iterations == 1)
7134 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00007135
cristy3ed852e2009-09-05 21:47:34 +00007136 while (GetPreviousImageInList(image) != (Image *) NULL)
7137 {
7138 image_count++;
7139 if (image_count > 10*mng_info->image_found)
7140 {
7141 if (logging != MagickFalse)
7142 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00007143
cristy16ea1392012-03-21 20:38:41 +00007144 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007145 CoderError,"Linked list is corrupted, beginning of list not found",
7146 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007147
cristy3ed852e2009-09-05 21:47:34 +00007148 return((Image *) NULL);
7149 }
glennrp0fe50b42010-11-16 03:52:51 +00007150
cristy3ed852e2009-09-05 21:47:34 +00007151 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00007152
cristy3ed852e2009-09-05 21:47:34 +00007153 if (GetNextImageInList(image) == (Image *) NULL)
7154 {
7155 if (logging != MagickFalse)
7156 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00007157
cristy16ea1392012-03-21 20:38:41 +00007158 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007159 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7160 image_info->filename);
7161 }
7162 }
glennrp47b9dd52010-11-24 18:12:06 +00007163
cristy3ed852e2009-09-05 21:47:34 +00007164 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7165 GetNextImageInList(image) ==
7166 (Image *) NULL)
7167 {
7168 if (logging != MagickFalse)
7169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7170 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00007171
cristy16ea1392012-03-21 20:38:41 +00007172 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007173 CoderError,"image->next for first image is NULL but shouldn't be.",
7174 "`%s'",image_info->filename);
7175 }
glennrp47b9dd52010-11-24 18:12:06 +00007176
cristy3ed852e2009-09-05 21:47:34 +00007177 if (mng_info->image_found == 0)
7178 {
7179 if (logging != MagickFalse)
7180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7181 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00007182
cristy16ea1392012-03-21 20:38:41 +00007183 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007184 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007185
cristy3ed852e2009-09-05 21:47:34 +00007186 if (image != (Image *) NULL)
7187 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007188
cristy3ed852e2009-09-05 21:47:34 +00007189 MngInfoFreeStruct(mng_info,&have_mng_structure);
7190 return((Image *) NULL);
7191 }
7192
7193 if (mng_info->ticks_per_second)
7194 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7195 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00007196
cristy3ed852e2009-09-05 21:47:34 +00007197 else
7198 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00007199
cristy3ed852e2009-09-05 21:47:34 +00007200 /* Find final nonzero image delay */
7201 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00007202
cristy3ed852e2009-09-05 21:47:34 +00007203 while (GetNextImageInList(image) != (Image *) NULL)
7204 {
7205 if (image->delay)
7206 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00007207
cristy3ed852e2009-09-05 21:47:34 +00007208 image=GetNextImageInList(image);
7209 }
glennrp0fe50b42010-11-16 03:52:51 +00007210
cristy3ed852e2009-09-05 21:47:34 +00007211 if (final_delay < final_image_delay)
7212 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00007213
cristy3ed852e2009-09-05 21:47:34 +00007214 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00007215
cristy3ed852e2009-09-05 21:47:34 +00007216 if (logging != MagickFalse)
7217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007218 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7219 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00007220
cristy3ed852e2009-09-05 21:47:34 +00007221 if (logging != MagickFalse)
7222 {
7223 int
7224 scene;
7225
7226 scene=0;
7227 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007228
cristy3ed852e2009-09-05 21:47:34 +00007229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7230 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007231
cristy3ed852e2009-09-05 21:47:34 +00007232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007233 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00007234
cristy3ed852e2009-09-05 21:47:34 +00007235 while (GetNextImageInList(image) != (Image *) NULL)
7236 {
7237 image=GetNextImageInList(image);
7238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007239 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00007240 }
7241 }
7242
7243 image=GetFirstImageInList(image);
7244#ifdef MNG_COALESCE_LAYERS
7245 if (insert_layers)
7246 {
7247 Image
7248 *next_image,
7249 *next;
7250
cristybb503372010-05-27 20:51:26 +00007251 size_t
cristy3ed852e2009-09-05 21:47:34 +00007252 scene;
7253
7254 if (logging != MagickFalse)
7255 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00007256
cristy3ed852e2009-09-05 21:47:34 +00007257 scene=image->scene;
cristy16ea1392012-03-21 20:38:41 +00007258 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00007259
cristy3ed852e2009-09-05 21:47:34 +00007260 if (next_image == (Image *) NULL)
7261 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00007262
cristy3ed852e2009-09-05 21:47:34 +00007263 image=DestroyImageList(image);
7264 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00007265
cristy3ed852e2009-09-05 21:47:34 +00007266 for (next=image; next != (Image *) NULL; next=next_image)
7267 {
7268 next->page.width=mng_info->mng_width;
7269 next->page.height=mng_info->mng_height;
7270 next->page.x=0;
7271 next->page.y=0;
7272 next->scene=scene++;
7273 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00007274
cristy3ed852e2009-09-05 21:47:34 +00007275 if (next_image == (Image *) NULL)
7276 break;
glennrp47b9dd52010-11-24 18:12:06 +00007277
cristy3ed852e2009-09-05 21:47:34 +00007278 if (next->delay == 0)
7279 {
7280 scene--;
7281 next_image->previous=GetPreviousImageInList(next);
7282 if (GetPreviousImageInList(next) == (Image *) NULL)
7283 image=next_image;
7284 else
7285 next->previous->next=next_image;
7286 next=DestroyImage(next);
7287 }
7288 }
7289 }
7290#endif
7291
7292 while (GetNextImageInList(image) != (Image *) NULL)
7293 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007294
cristy3ed852e2009-09-05 21:47:34 +00007295 image->dispose=BackgroundDispose;
7296
7297 if (logging != MagickFalse)
7298 {
7299 int
7300 scene;
7301
7302 scene=0;
7303 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007304
cristy3ed852e2009-09-05 21:47:34 +00007305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7306 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007307
cristy3ed852e2009-09-05 21:47:34 +00007308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007309 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7310 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007311
cristy3ed852e2009-09-05 21:47:34 +00007312 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007313 {
7314 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007315
cristyf2faecf2010-05-28 19:19:36 +00007316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007317 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7318 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007319 }
7320 }
glennrp47b9dd52010-11-24 18:12:06 +00007321
cristy3ed852e2009-09-05 21:47:34 +00007322 image=GetFirstImageInList(image);
7323 MngInfoFreeStruct(mng_info,&have_mng_structure);
7324 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007325
cristy3ed852e2009-09-05 21:47:34 +00007326 if (logging != MagickFalse)
7327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007328
cristy3ed852e2009-09-05 21:47:34 +00007329 return(GetFirstImageInList(image));
7330}
glennrp25c1e2b2010-03-25 01:39:56 +00007331#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007332static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7333{
7334 printf("Your PNG library is too old: You have libpng-%s\n",
7335 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007336
cristy3ed852e2009-09-05 21:47:34 +00007337 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7338 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007339
cristy3ed852e2009-09-05 21:47:34 +00007340 return(Image *) NULL;
7341}
glennrp47b9dd52010-11-24 18:12:06 +00007342
cristy3ed852e2009-09-05 21:47:34 +00007343static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7344{
7345 return(ReadPNGImage(image_info,exception));
7346}
glennrp25c1e2b2010-03-25 01:39:56 +00007347#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007348#endif
7349
7350/*
7351%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7352% %
7353% %
7354% %
7355% R e g i s t e r P N G I m a g e %
7356% %
7357% %
7358% %
7359%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7360%
7361% RegisterPNGImage() adds properties for the PNG image format to
7362% the list of supported formats. The properties include the image format
7363% tag, a method to read and/or write the format, whether the format
7364% supports the saving of more than one frame to the same file or blob,
7365% whether the format supports native in-memory I/O, and a brief
7366% description of the format.
7367%
7368% The format of the RegisterPNGImage method is:
7369%
cristybb503372010-05-27 20:51:26 +00007370% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007371%
7372*/
cristybb503372010-05-27 20:51:26 +00007373ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007374{
7375 char
cristy151b66d2015-04-15 10:50:31 +00007376 version[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00007377
7378 MagickInfo
7379 *entry;
7380
7381 static const char
7382 *PNGNote=
7383 {
7384 "See http://www.libpng.org/ for details about the PNG format."
7385 },
glennrp47b9dd52010-11-24 18:12:06 +00007386
cristy3ed852e2009-09-05 21:47:34 +00007387 *JNGNote=
7388 {
7389 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7390 "format."
7391 },
glennrp47b9dd52010-11-24 18:12:06 +00007392
cristy3ed852e2009-09-05 21:47:34 +00007393 *MNGNote=
7394 {
7395 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7396 "format."
7397 };
7398
7399 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007400
cristy3ed852e2009-09-05 21:47:34 +00007401#if defined(PNG_LIBPNG_VER_STRING)
cristy151b66d2015-04-15 10:50:31 +00007402 (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7403 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MagickPathExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007404
cristy3ed852e2009-09-05 21:47:34 +00007405 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7406 {
cristy151b66d2015-04-15 10:50:31 +00007407 (void) ConcatenateMagickString(version,",",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00007408 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
cristy151b66d2015-04-15 10:50:31 +00007409 MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00007410 }
7411#endif
glennrp47b9dd52010-11-24 18:12:06 +00007412
dirk06b627a2015-04-06 18:59:17 +00007413 entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
dirk08e9a112015-02-22 01:51:41 +00007414 entry->flags|=CoderSeekableStreamFlag; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007415
cristy3ed852e2009-09-05 21:47:34 +00007416#if defined(MAGICKCORE_PNG_DELEGATE)
7417 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7418 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7419#endif
glennrp47b9dd52010-11-24 18:12:06 +00007420
cristy3ed852e2009-09-05 21:47:34 +00007421 entry->magick=(IsImageFormatHandler *) IsMNG;
glennrp47b9dd52010-11-24 18:12:06 +00007422
cristy3ed852e2009-09-05 21:47:34 +00007423 if (*version != '\0')
7424 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007425
glennrpafc97b12013-08-10 00:39:01 +00007426 entry->mime_type=ConstantString("video/x-mng");
cristy3ed852e2009-09-05 21:47:34 +00007427 entry->note=ConstantString(MNGNote);
7428 (void) RegisterMagickInfo(entry);
7429
dirk06b627a2015-04-06 18:59:17 +00007430 entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007431
cristy3ed852e2009-09-05 21:47:34 +00007432#if defined(MAGICKCORE_PNG_DELEGATE)
7433 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7434 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7435#endif
glennrp47b9dd52010-11-24 18:12:06 +00007436
cristy3ed852e2009-09-05 21:47:34 +00007437 entry->magick=(IsImageFormatHandler *) IsPNG;
dirk08e9a112015-02-22 01:51:41 +00007438 entry->flags^=CoderAdjoinFlag;
cristyd625c522013-08-09 11:45:57 +00007439 entry->mime_type=ConstantString("image/png");
glennrp47b9dd52010-11-24 18:12:06 +00007440
cristy3ed852e2009-09-05 21:47:34 +00007441 if (*version != '\0')
7442 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007443
cristy3ed852e2009-09-05 21:47:34 +00007444 entry->note=ConstantString(PNGNote);
7445 (void) RegisterMagickInfo(entry);
7446
dirk06b627a2015-04-06 18:59:17 +00007447 entry=AcquireMagickInfo("PNG","PNG8",
7448 "8-bit indexed with optional binary transparency");
glennrp47b9dd52010-11-24 18:12:06 +00007449
cristy3ed852e2009-09-05 21:47:34 +00007450#if defined(MAGICKCORE_PNG_DELEGATE)
7451 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7452 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7453#endif
glennrp47b9dd52010-11-24 18:12:06 +00007454
cristy3ed852e2009-09-05 21:47:34 +00007455 entry->magick=(IsImageFormatHandler *) IsPNG;
dirk08e9a112015-02-22 01:51:41 +00007456 entry->flags^=CoderAdjoinFlag;
cristyd625c522013-08-09 11:45:57 +00007457 entry->mime_type=ConstantString("image/png");
cristy3ed852e2009-09-05 21:47:34 +00007458 (void) RegisterMagickInfo(entry);
7459
dirk06b627a2015-04-06 18:59:17 +00007460 entry=AcquireMagickInfo("PNG","PNG24",
7461 "opaque or binary transparent 24-bit RGB");
cristy3ed852e2009-09-05 21:47:34 +00007462 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007463
cristy3ed852e2009-09-05 21:47:34 +00007464#if defined(ZLIB_VERSION)
cristy151b66d2015-04-15 10:50:31 +00007465 (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7466 (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007467
cristy3ed852e2009-09-05 21:47:34 +00007468 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7469 {
cristy151b66d2015-04-15 10:50:31 +00007470 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7471 (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00007472 }
7473#endif
glennrp47b9dd52010-11-24 18:12:06 +00007474
cristy3ed852e2009-09-05 21:47:34 +00007475 if (*version != '\0')
7476 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007477
cristy3ed852e2009-09-05 21:47:34 +00007478#if defined(MAGICKCORE_PNG_DELEGATE)
7479 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7480 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7481#endif
glennrp47b9dd52010-11-24 18:12:06 +00007482
cristy3ed852e2009-09-05 21:47:34 +00007483 entry->magick=(IsImageFormatHandler *) IsPNG;
dirk08e9a112015-02-22 01:51:41 +00007484 entry->flags^=CoderAdjoinFlag;
cristyd625c522013-08-09 11:45:57 +00007485 entry->mime_type=ConstantString("image/png");
cristy3ed852e2009-09-05 21:47:34 +00007486 (void) RegisterMagickInfo(entry);
7487
dirk06b627a2015-04-06 18:59:17 +00007488 entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
glennrp47b9dd52010-11-24 18:12:06 +00007489
cristy3ed852e2009-09-05 21:47:34 +00007490#if defined(MAGICKCORE_PNG_DELEGATE)
7491 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7492 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7493#endif
glennrp47b9dd52010-11-24 18:12:06 +00007494
cristy3ed852e2009-09-05 21:47:34 +00007495 entry->magick=(IsImageFormatHandler *) IsPNG;
dirk08e9a112015-02-22 01:51:41 +00007496 entry->flags^=CoderAdjoinFlag;
cristyd625c522013-08-09 11:45:57 +00007497 entry->mime_type=ConstantString("image/png");
cristy3ed852e2009-09-05 21:47:34 +00007498 (void) RegisterMagickInfo(entry);
7499
dirk06b627a2015-04-06 18:59:17 +00007500 entry=AcquireMagickInfo("PNG","PNG48",
7501 "opaque or binary transparent 48-bit RGB");
glennrpfd164d22013-01-26 21:10:22 +00007502
7503#if defined(MAGICKCORE_PNG_DELEGATE)
7504 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7505 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7506#endif
7507
7508 entry->magick=(IsImageFormatHandler *) IsPNG;
dirk08e9a112015-02-22 01:51:41 +00007509 entry->flags^=CoderAdjoinFlag;
cristyd625c522013-08-09 11:45:57 +00007510 entry->mime_type=ConstantString("image/png");
glennrpfd164d22013-01-26 21:10:22 +00007511 (void) RegisterMagickInfo(entry);
7512
dirk06b627a2015-04-06 18:59:17 +00007513 entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
glennrpfd164d22013-01-26 21:10:22 +00007514
7515#if defined(MAGICKCORE_PNG_DELEGATE)
7516 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7517 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7518#endif
7519
7520 entry->magick=(IsImageFormatHandler *) IsPNG;
dirk08e9a112015-02-22 01:51:41 +00007521 entry->flags^=CoderAdjoinFlag;
cristyd625c522013-08-09 11:45:57 +00007522 entry->mime_type=ConstantString("image/png");
glennrpfd164d22013-01-26 21:10:22 +00007523 (void) RegisterMagickInfo(entry);
7524
dirk06b627a2015-04-06 18:59:17 +00007525 entry=AcquireMagickInfo("PNG","PNG00",
glennrp698aab62015-07-22 19:27:21 +00007526 "PNG inheriting bit-depth, color-type from original, if possible");
dirk06b627a2015-04-06 18:59:17 +00007527
7528#if defined(MAGICKCORE_PNG_DELEGATE)
7529 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7530 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7531#endif
7532
7533 entry->magick=(IsImageFormatHandler *) IsPNG;
7534 entry->flags^=CoderAdjoinFlag;
cristyd625c522013-08-09 11:45:57 +00007535 entry->mime_type=ConstantString("image/png");
glennrp5830fbc2013-01-27 06:11:45 +00007536 (void) RegisterMagickInfo(entry);
7537
dirk06b627a2015-04-06 18:59:17 +00007538 entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007539
cristy3ed852e2009-09-05 21:47:34 +00007540#if defined(JNG_SUPPORTED)
7541#if defined(MAGICKCORE_PNG_DELEGATE)
7542 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7543 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7544#endif
7545#endif
glennrp47b9dd52010-11-24 18:12:06 +00007546
cristy3ed852e2009-09-05 21:47:34 +00007547 entry->magick=(IsImageFormatHandler *) IsJNG;
dirk08e9a112015-02-22 01:51:41 +00007548 entry->flags^=CoderAdjoinFlag;
glennrp7fee3292013-08-09 15:25:52 +00007549 entry->mime_type=ConstantString("image/x-jng");
cristy3ed852e2009-09-05 21:47:34 +00007550 entry->note=ConstantString(JNGNote);
7551 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007552
glennrp868fff32014-03-16 22:09:06 +00007553#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
cristy3d162a92014-02-16 14:05:06 +00007554 ping_semaphore=AcquireSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007555#endif
glennrp47b9dd52010-11-24 18:12:06 +00007556
cristy3ed852e2009-09-05 21:47:34 +00007557 return(MagickImageCoderSignature);
7558}
7559
7560/*
7561%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7562% %
7563% %
7564% %
7565% U n r e g i s t e r P N G I m a g e %
7566% %
7567% %
7568% %
7569%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7570%
7571% UnregisterPNGImage() removes format registrations made by the
7572% PNG module from the list of supported formats.
7573%
7574% The format of the UnregisterPNGImage method is:
7575%
7576% UnregisterPNGImage(void)
7577%
7578*/
7579ModuleExport void UnregisterPNGImage(void)
7580{
7581 (void) UnregisterMagickInfo("MNG");
7582 (void) UnregisterMagickInfo("PNG");
7583 (void) UnregisterMagickInfo("PNG8");
7584 (void) UnregisterMagickInfo("PNG24");
7585 (void) UnregisterMagickInfo("PNG32");
glennrpfd164d22013-01-26 21:10:22 +00007586 (void) UnregisterMagickInfo("PNG48");
7587 (void) UnregisterMagickInfo("PNG64");
glennrp5830fbc2013-01-27 06:11:45 +00007588 (void) UnregisterMagickInfo("PNG00");
cristy3ed852e2009-09-05 21:47:34 +00007589 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007590
glennrp868fff32014-03-16 22:09:06 +00007591#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007592 if (ping_semaphore != (SemaphoreInfo *) NULL)
cristy3d162a92014-02-16 14:05:06 +00007593 RelinquishSemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007594#endif
7595}
7596
7597#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007598#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007599/*
7600%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7601% %
7602% %
7603% %
7604% W r i t e M N G I m a g e %
7605% %
7606% %
7607% %
7608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7609%
7610% WriteMNGImage() writes an image in the Portable Network Graphics
7611% Group's "Multiple-image Network Graphics" encoded image format.
7612%
7613% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7614%
7615% The format of the WriteMNGImage method is:
7616%
cristy16ea1392012-03-21 20:38:41 +00007617% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7618% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007619%
7620% A description of each parameter follows.
7621%
7622% o image_info: the image info.
7623%
7624% o image: The image.
7625%
cristy16ea1392012-03-21 20:38:41 +00007626% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007627%
7628% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7629% "To do" under ReadPNGImage):
7630%
cristy3ed852e2009-09-05 21:47:34 +00007631% Preserve all unknown and not-yet-handled known chunks found in input
7632% PNG file and copy them into output PNG files according to the PNG
7633% copying rules.
7634%
7635% Write the iCCP chunk at MNG level when (icc profile length > 0)
7636%
7637% Improve selection of color type (use indexed-colour or indexed-colour
7638% with tRNS when 256 or fewer unique RGBA values are present).
7639%
7640% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7641% This will be complicated if we limit ourselves to generating MNG-LC
7642% files. For now we ignore disposal method 3 and simply overlay the next
7643% image on it.
7644%
7645% Check for identical PLTE's or PLTE/tRNS combinations and use a
7646% global MNG PLTE or PLTE/tRNS combination when appropriate.
7647% [mostly done 15 June 1999 but still need to take care of tRNS]
7648%
7649% Check for identical sRGB and replace with a global sRGB (and remove
7650% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7651% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7652% local gAMA/cHRM with local sRGB if appropriate).
7653%
7654% Check for identical sBIT chunks and write global ones.
7655%
7656% Provide option to skip writing the signature tEXt chunks.
7657%
7658% Use signatures to detect identical objects and reuse the first
7659% instance of such objects instead of writing duplicate objects.
7660%
7661% Use a smaller-than-32k value of compression window size when
7662% appropriate.
7663%
7664% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7665% ancillary text chunks and save profiles.
7666%
7667% Provide an option to force LC files (to ensure exact framing rate)
7668% instead of VLC.
7669%
7670% Provide an option to force VLC files instead of LC, even when offsets
7671% are present. This will involve expanding the embedded images with a
7672% transparent region at the top and/or left.
7673*/
7674
cristy3ed852e2009-09-05 21:47:34 +00007675static void
glennrpcf002022011-01-30 02:38:15 +00007676Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007677 png_info *ping_info, unsigned char *profile_type, unsigned char
7678 *profile_description, unsigned char *profile_data, png_uint_32 length)
7679{
cristy3ed852e2009-09-05 21:47:34 +00007680 png_textp
7681 text;
7682
cristybb503372010-05-27 20:51:26 +00007683 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007684 i;
7685
7686 unsigned char
7687 *sp;
7688
7689 png_charp
7690 dp;
7691
7692 png_uint_32
7693 allocated_length,
7694 description_length;
7695
7696 unsigned char
7697 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007698
cristy3ed852e2009-09-05 21:47:34 +00007699 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7700 return;
7701
7702 if (image_info->verbose)
7703 {
glennrp0fe50b42010-11-16 03:52:51 +00007704 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7705 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007706 }
glennrp0fe50b42010-11-16 03:52:51 +00007707
glennrpecab7d72013-05-14 22:50:32 +00007708#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +00007709 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7710#else
7711 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7712#endif
cristy3ed852e2009-09-05 21:47:34 +00007713 description_length=(png_uint_32) strlen((const char *) profile_description);
7714 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7715 + description_length);
glennrpecab7d72013-05-14 22:50:32 +00007716#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +00007717 text[0].text=(png_charp) png_malloc(ping,
7718 (png_alloc_size_t) allocated_length);
7719 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7720#else
7721 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7722 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7723#endif
cristy3ed852e2009-09-05 21:47:34 +00007724 text[0].key[0]='\0';
7725 (void) ConcatenateMagickString(text[0].key,
cristy151b66d2015-04-15 10:50:31 +00007726 "Raw profile type ",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00007727 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7728 sp=profile_data;
7729 dp=text[0].text;
7730 *dp++='\n';
7731 (void) CopyMagickString(dp,(const char *) profile_description,
7732 allocated_length);
7733 dp+=description_length;
7734 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007735 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007736 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007737 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007738
cristybb503372010-05-27 20:51:26 +00007739 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007740 {
7741 if (i%36 == 0)
7742 *dp++='\n';
7743 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7744 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7745 }
glennrp47b9dd52010-11-24 18:12:06 +00007746
cristy3ed852e2009-09-05 21:47:34 +00007747 *dp++='\n';
7748 *dp='\0';
7749 text[0].text_length=(png_size_t) (dp-text[0].text);
7750 text[0].compression=image_info->compression == NoCompression ||
7751 (image_info->compression == UndefinedCompression &&
7752 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007753
cristy3ed852e2009-09-05 21:47:34 +00007754 if (text[0].text_length <= allocated_length)
7755 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007756
cristy3ed852e2009-09-05 21:47:34 +00007757 png_free(ping,text[0].text);
7758 png_free(ping,text[0].key);
7759 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007760}
7761
glennrpcf002022011-01-30 02:38:15 +00007762static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007763 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007764{
7765 char
7766 *name;
7767
7768 const StringInfo
7769 *profile;
7770
7771 unsigned char
7772 *data;
7773
7774 png_uint_32 length;
7775
7776 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007777
7778 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7779 {
cristy3ed852e2009-09-05 21:47:34 +00007780 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007781
cristy3ed852e2009-09-05 21:47:34 +00007782 if (profile != (const StringInfo *) NULL)
7783 {
7784 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007785 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007786
glennrp47b9dd52010-11-24 18:12:06 +00007787 if (LocaleNCompare(name,string,11) == 0)
7788 {
7789 if (logging != MagickFalse)
7790 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7791 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007792
glennrpcf002022011-01-30 02:38:15 +00007793 ping_profile=CloneStringInfo(profile);
7794 data=GetStringInfoDatum(ping_profile),
7795 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007796 data[4]=data[3];
7797 data[3]=data[2];
7798 data[2]=data[1];
7799 data[1]=data[0];
7800 (void) WriteBlobMSBULong(image,length-5); /* data length */
7801 (void) WriteBlob(image,length-1,data+1);
7802 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007803 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007804 }
cristy3ed852e2009-09-05 21:47:34 +00007805 }
glennrp47b9dd52010-11-24 18:12:06 +00007806
cristy3ed852e2009-09-05 21:47:34 +00007807 name=GetNextImageProfile(image);
7808 }
glennrp47b9dd52010-11-24 18:12:06 +00007809
cristy3ed852e2009-09-05 21:47:34 +00007810 return(MagickTrue);
7811}
7812
dirk68a6b502016-01-02 17:47:58 +01007813static inline MagickBooleanType Magick_png_color_equal(const Image *image,
7814 const Quantum *p, const PixelInfo *q)
7815{
7816 MagickRealType
7817 value;
7818
7819 value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
7820 if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
7821 return(MagickFalse);
7822 value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
7823 if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
7824 return(MagickFalse);
7825 value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
7826 if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
7827 return(MagickFalse);
7828
7829 return(MagickTrue);
7830}
7831
dirkfd6fd072014-10-24 21:10:08 +00007832#if defined(PNG_tIME_SUPPORTED)
7833static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
7834 const char *date,ExceptionInfo *exception)
7835{
7836 unsigned int
7837 day,
7838 hour,
7839 minute,
7840 month,
7841 second,
7842 year;
7843
7844 png_time
7845 ptime;
7846
7847 time_t
7848 ttime;
7849
7850 if (date != (const char *) NULL)
7851 {
7852 if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
7853 &second) != 6)
7854 {
7855 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7856 "Invalid date format specified for png:tIME","`%s'",
7857 image->filename);
7858 return;
7859 }
7860 ptime.year=(png_uint_16) year;
7861 ptime.month=(png_byte) month;
7862 ptime.day=(png_byte) day;
7863 ptime.hour=(png_byte) hour;
7864 ptime.minute=(png_byte) minute;
7865 ptime.second=(png_byte) second;
7866 }
7867 else
7868 {
7869 time(&ttime);
7870 png_convert_from_time_t(&ptime,ttime);
7871 }
7872 png_set_tIME(ping,info,&ptime);
7873}
7874#endif
glennrpb9cfe272010-12-21 15:08:06 +00007875
cristy3ed852e2009-09-05 21:47:34 +00007876/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007877static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +00007878 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007879{
cristy09973322013-06-30 21:06:30 +00007880 char
7881 im_vers[32],
7882 libpng_runv[32],
7883 libpng_vers[32],
7884 zlib_runv[32],
7885 zlib_vers[32];
7886
cristy16ea1392012-03-21 20:38:41 +00007887 Image
7888 *image;
7889
7890 ImageInfo
7891 *image_info;
7892
cristy3ed852e2009-09-05 21:47:34 +00007893 char
7894 s[2];
7895
7896 const char
7897 *name,
7898 *property,
7899 *value;
7900
7901 const StringInfo
7902 *profile;
7903
cristy3ed852e2009-09-05 21:47:34 +00007904 int
cristy3ed852e2009-09-05 21:47:34 +00007905 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007906 pass;
7907
glennrpe9c26dc2010-05-30 01:56:35 +00007908 png_byte
7909 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007910
glennrp39992b42010-11-14 00:03:43 +00007911 png_color
7912 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007913
glennrp5af765f2010-03-30 11:12:18 +00007914 png_color_16
7915 ping_background,
7916 ping_trans_color;
7917
cristy3ed852e2009-09-05 21:47:34 +00007918 png_info
7919 *ping_info;
7920
7921 png_struct
7922 *ping;
7923
glennrp5af765f2010-03-30 11:12:18 +00007924 png_uint_32
7925 ping_height,
7926 ping_width;
7927
cristybb503372010-05-27 20:51:26 +00007928 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007929 y;
7930
7931 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007932 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007933 logging,
glennrp58e01762011-01-07 15:28:54 +00007934 matte,
7935
glennrpda8f3a72011-02-27 23:54:12 +00007936 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007937 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007938 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007939 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007940 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007941 ping_have_bKGD,
glennrp918b9dc2013-04-03 13:41:41 +00007942 ping_have_iCCP,
glennrp991d11d2010-11-12 21:55:28 +00007943 ping_have_pHYs,
glennrp918b9dc2013-04-03 13:41:41 +00007944 ping_have_sRGB,
glennrp991d11d2010-11-12 21:55:28 +00007945 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007946
7947 ping_exclude_bKGD,
7948 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007949 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007950 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007951 ping_exclude_gAMA,
7952 ping_exclude_iCCP,
7953 /* ping_exclude_iTXt, */
7954 ping_exclude_oFFs,
7955 ping_exclude_pHYs,
7956 ping_exclude_sRGB,
7957 ping_exclude_tEXt,
dirkfd6fd072014-10-24 21:10:08 +00007958 ping_exclude_tIME,
glennrpe4e2d792011-02-21 12:11:27 +00007959 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007960 ping_exclude_vpAg,
7961 ping_exclude_zCCP, /* hex-encoded iCCP */
7962 ping_exclude_zTXt,
7963
glennrp8d3d6e52011-04-19 04:39:51 +00007964 ping_preserve_colormap,
glennrpecab7d72013-05-14 22:50:32 +00007965 ping_preserve_iCCP,
glennrp0e8ea192010-12-24 18:00:33 +00007966 ping_need_colortype_warning,
7967
glennrp82b3c532011-03-22 19:20:54 +00007968 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007969 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007970 tried_333,
7971 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007972
cristy09973322013-06-30 21:06:30 +00007973 MemoryInfo
cristyaf1534a2013-06-30 21:12:25 +00007974 *volatile pixel_info;
cristy09973322013-06-30 21:06:30 +00007975
cristy3ed852e2009-09-05 21:47:34 +00007976 QuantumInfo
7977 *quantum_info;
7978
cristy16ea1392012-03-21 20:38:41 +00007979 PNGErrorInfo
7980 error_info;
7981
cristybb503372010-05-27 20:51:26 +00007982 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007983 i,
7984 x;
7985
7986 unsigned char
cristy09973322013-06-30 21:06:30 +00007987 *ping_pixels;
glennrpd0cae252013-03-15 22:30:41 +00007988
glennrp5af765f2010-03-30 11:12:18 +00007989 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007990 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007991 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007992 ping_color_type,
7993 ping_interlace_method,
7994 ping_compression_method,
7995 ping_filter_method,
7996 ping_num_trans;
7997
cristybb503372010-05-27 20:51:26 +00007998 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007999 image_depth,
8000 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00008001
cristybb503372010-05-27 20:51:26 +00008002 size_t
cristy3ed852e2009-09-05 21:47:34 +00008003 quality,
8004 rowbytes,
8005 save_image_depth;
8006
glennrpdfd70802010-11-14 01:23:35 +00008007 int
glennrpfd05d622011-02-25 04:10:33 +00008008 j,
glennrpf09bded2011-01-08 01:15:59 +00008009 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00008010 number_opaque,
8011 number_semitransparent,
8012 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00008013 ping_pHYs_unit_type;
8014
8015 png_uint_32
8016 ping_pHYs_x_resolution,
8017 ping_pHYs_y_resolution;
8018
cristy3ed852e2009-09-05 21:47:34 +00008019 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00008020 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00008021
cristy16ea1392012-03-21 20:38:41 +00008022 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8023 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8024 if (image_info == (ImageInfo *) NULL)
8025 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00008026
glennrpd0cae252013-03-15 22:30:41 +00008027 /* Define these outside of the following "if logging()" block so they will
8028 * show in debuggers.
8029 */
8030 *im_vers='\0';
8031 (void) ConcatenateMagickString(im_vers,
cristy151b66d2015-04-15 10:50:31 +00008032 MagickLibVersionText,MagickPathExtent);
glennrpd0cae252013-03-15 22:30:41 +00008033 (void) ConcatenateMagickString(im_vers,
cristy151b66d2015-04-15 10:50:31 +00008034 MagickLibAddendum,MagickPathExtent);
glennrpec0ddbc2013-03-16 12:40:12 +00008035
glennrpd0cae252013-03-15 22:30:41 +00008036 *libpng_vers='\0';
8037 (void) ConcatenateMagickString(libpng_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00008038 PNG_LIBPNG_VER_STRING,32);
8039 *libpng_runv='\0';
8040 (void) ConcatenateMagickString(libpng_runv,
8041 png_get_libpng_ver(NULL),32);
8042
glennrpd0cae252013-03-15 22:30:41 +00008043 *zlib_vers='\0';
8044 (void) ConcatenateMagickString(zlib_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00008045 ZLIB_VERSION,32);
8046 *zlib_runv='\0';
8047 (void) ConcatenateMagickString(zlib_runv,
8048 zlib_version,32);
8049
glennrp8fe91592014-10-25 13:57:25 +00008050 if (logging != MagickFalse)
glennrpd0cae252013-03-15 22:30:41 +00008051 {
8052 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8053 im_vers);
8054 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8055 libpng_vers);
glennrpec0ddbc2013-03-16 12:40:12 +00008056 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8057 {
8058 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8059 libpng_runv);
8060 }
glennrpd0cae252013-03-15 22:30:41 +00008061 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8062 zlib_vers);
glennrpec0ddbc2013-03-16 12:40:12 +00008063 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8064 {
8065 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8066 zlib_runv);
8067 }
glennrpd0cae252013-03-15 22:30:41 +00008068 }
8069
glennrp5af765f2010-03-30 11:12:18 +00008070 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00008071 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00008072 ping_color_type=0,
8073 ping_interlace_method=0,
8074 ping_compression_method=0,
8075 ping_filter_method=0,
8076 ping_num_trans = 0;
8077
8078 ping_background.red = 0;
8079 ping_background.green = 0;
8080 ping_background.blue = 0;
8081 ping_background.gray = 0;
8082 ping_background.index = 0;
8083
8084 ping_trans_color.red=0;
8085 ping_trans_color.green=0;
8086 ping_trans_color.blue=0;
8087 ping_trans_color.gray=0;
8088
glennrpdfd70802010-11-14 01:23:35 +00008089 ping_pHYs_unit_type = 0;
8090 ping_pHYs_x_resolution = 0;
8091 ping_pHYs_y_resolution = 0;
8092
glennrpda8f3a72011-02-27 23:54:12 +00008093 ping_have_blob=MagickFalse;
glennrpf70c4d22013-03-19 15:26:48 +00008094 ping_have_cheap_transparency=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00008095 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00008096 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00008097 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00008098 ping_have_bKGD=MagickFalse;
glennrp918b9dc2013-04-03 13:41:41 +00008099 ping_have_iCCP=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00008100 ping_have_pHYs=MagickFalse;
glennrp918b9dc2013-04-03 13:41:41 +00008101 ping_have_sRGB=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00008102 ping_have_tRNS=MagickFalse;
8103
glennrp0e8ea192010-12-24 18:00:33 +00008104 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8105 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00008106 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00008107 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00008108 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00008109 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8110 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8111 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8112 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8113 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8114 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
dirkfd6fd072014-10-24 21:10:08 +00008115 ping_exclude_tIME=mng_info->ping_exclude_tIME;
glennrpdde35db2011-02-21 12:06:32 +00008116 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00008117 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8118 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8119 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8120
glennrp8d3d6e52011-04-19 04:39:51 +00008121 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrpecab7d72013-05-14 22:50:32 +00008122 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
glennrp0e8ea192010-12-24 18:00:33 +00008123 ping_need_colortype_warning = MagickFalse;
8124
cristy0d57eec2011-09-04 22:13:56 +00008125 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8126 * i.e., eliminate the ICC profile and set image->rendering_intent.
8127 * Note that this will not involve any changes to the actual pixels
8128 * but merely passes information to applications that read the resulting
8129 * PNG image.
glennrpecab7d72013-05-14 22:50:32 +00008130 *
8131 * To do: recognize other variants of the sRGB profile, using the CRC to
8132 * verify all recognized variants including the 7 already known.
8133 *
8134 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8135 *
8136 * Use something other than image->rendering_intent to record the fact
8137 * that the sRGB profile was found.
8138 *
8139 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8140 * profile. Record the Blackpoint Compensation, if any.
cristy0d57eec2011-09-04 22:13:56 +00008141 */
glennrpecab7d72013-05-14 22:50:32 +00008142 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
cristy0d57eec2011-09-04 22:13:56 +00008143 {
8144 char
8145 *name;
8146
8147 const StringInfo
8148 *profile;
8149
8150 ResetImageProfileIterator(image);
8151 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8152 {
8153 profile=GetImageProfile(image,name);
8154
8155 if (profile != (StringInfo *) NULL)
8156 {
8157 if ((LocaleCompare(name,"ICC") == 0) ||
glennrpecab7d72013-05-14 22:50:32 +00008158 (LocaleCompare(name,"ICM") == 0))
glennrpee7b4c02011-10-04 01:21:09 +00008159
glennrpecab7d72013-05-14 22:50:32 +00008160 {
8161 int
8162 icheck,
8163 got_crc=0;
8164
glennrpee7b4c02011-10-04 01:21:09 +00008165
8166 png_uint_32
8167 length,
glennrpecab7d72013-05-14 22:50:32 +00008168 profile_crc=0;
glennrpee7b4c02011-10-04 01:21:09 +00008169
cristy0d57eec2011-09-04 22:13:56 +00008170 unsigned char
8171 *data;
8172
glennrp29a106e2011-09-06 17:11:42 +00008173 length=(png_uint_32) GetStringInfoLength(profile);
8174
glennrpecab7d72013-05-14 22:50:32 +00008175 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
cristy0d57eec2011-09-04 22:13:56 +00008176 {
glennrpecab7d72013-05-14 22:50:32 +00008177 if (length == sRGB_info[icheck].len)
glennrp29a106e2011-09-06 17:11:42 +00008178 {
glennrpecab7d72013-05-14 22:50:32 +00008179 if (got_crc == 0)
8180 {
8181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00008182 " Got a %lu-byte ICC profile (potentially sRGB)",
8183 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00008184
glennrpecab7d72013-05-14 22:50:32 +00008185 data=GetStringInfoDatum(profile);
8186 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00008187
glennrpecab7d72013-05-14 22:50:32 +00008188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8189 " with crc=%8x",(unsigned int) profile_crc);
8190 got_crc++;
8191 }
glennrpee7b4c02011-10-04 01:21:09 +00008192
glennrpecab7d72013-05-14 22:50:32 +00008193 if (profile_crc == sRGB_info[icheck].crc)
glennrpee7b4c02011-10-04 01:21:09 +00008194 {
8195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpecab7d72013-05-14 22:50:32 +00008196 " It is sRGB with rendering intent = %s",
8197 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8198 sRGB_info[icheck].intent));
glennrpee7b4c02011-10-04 01:21:09 +00008199 if (image->rendering_intent==UndefinedIntent)
glennrpecab7d72013-05-14 22:50:32 +00008200 {
8201 image->rendering_intent=
8202 Magick_RenderingIntent_from_PNG_RenderingIntent(
8203 sRGB_info[icheck].intent);
8204 }
8205 ping_exclude_iCCP = MagickTrue;
8206 ping_exclude_zCCP = MagickTrue;
8207 ping_have_sRGB = MagickTrue;
glennrpee7b4c02011-10-04 01:21:09 +00008208 break;
8209 }
glennrp29a106e2011-09-06 17:11:42 +00008210 }
glennrp29a106e2011-09-06 17:11:42 +00008211 }
glennrpecab7d72013-05-14 22:50:32 +00008212 if (sRGB_info[icheck].len == 0)
glennrp29a106e2011-09-06 17:11:42 +00008213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpecab7d72013-05-14 22:50:32 +00008214 " Got a %lu-byte ICC profile not recognized as sRGB",
glennrp29a106e2011-09-06 17:11:42 +00008215 (unsigned long) length);
8216 }
cristy0d57eec2011-09-04 22:13:56 +00008217 }
8218 name=GetNextImageProfile(image);
8219 }
8220 }
8221
glennrp8bb3a022010-12-13 20:40:04 +00008222 number_opaque = 0;
8223 number_semitransparent = 0;
8224 number_transparent = 0;
8225
glennrpfd05d622011-02-25 04:10:33 +00008226 if (logging != MagickFalse)
8227 {
8228 if (image->storage_class == UndefinedClass)
8229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpf3794ae2014-10-26 21:16:10 +00008230 " image->storage_class=UndefinedClass");
glennrpfd05d622011-02-25 04:10:33 +00008231 if (image->storage_class == DirectClass)
8232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpf3794ae2014-10-26 21:16:10 +00008233 " image->storage_class=DirectClass");
glennrpfd05d622011-02-25 04:10:33 +00008234 if (image->storage_class == PseudoClass)
8235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpf3794ae2014-10-26 21:16:10 +00008236 " image->storage_class=PseudoClass");
8237 (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
glennrpcc96f2d2014-10-26 21:30:02 +00008238 " image->taint=MagickTrue":
8239 " image->taint=MagickFalse");
glennrpfd05d622011-02-25 04:10:33 +00008240 }
glennrp28af3712011-04-06 18:07:30 +00008241
glennrp750105b2012-04-25 16:20:45 +00008242 if (image->storage_class == PseudoClass &&
glennrp7e65e932011-08-19 02:31:16 +00008243 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
glennrpfd164d22013-01-26 21:10:22 +00008244 mng_info->write_png48 || mng_info->write_png64 ||
8245 (mng_info->write_png_colortype != 1 &&
8246 mng_info->write_png_colortype != 5)))
glennrp7e65e932011-08-19 02:31:16 +00008247 {
cristy16ea1392012-03-21 20:38:41 +00008248 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00008249 image->storage_class = DirectClass;
8250 }
8251
glennrpc6c391a2011-04-27 02:23:56 +00008252 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00008253 {
glennrpc6c391a2011-04-27 02:23:56 +00008254 if (image->storage_class != PseudoClass && image->colormap != NULL)
8255 {
8256 /* Free the bogus colormap; it can cause trouble later */
8257 if (logging != MagickFalse)
8258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8259 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00008260 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00008261 image->colormap=NULL;
8262 }
glennrp28af3712011-04-06 18:07:30 +00008263 }
glennrpbb4f99d2011-05-22 11:13:17 +00008264
glennrpc28acd62013-12-05 20:21:45 +00008265 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00008266 (void) TransformImageColorspace(image,sRGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00008267
glennrp3241bd02010-12-12 04:36:28 +00008268 /*
8269 Sometimes we get PseudoClass images whose RGB values don't match
8270 the colors in the colormap. This code syncs the RGB values.
8271 */
8272 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristy16ea1392012-03-21 20:38:41 +00008273 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00008274
glennrpa6a06632011-01-19 15:15:34 +00008275#if (MAGICKCORE_QUANTUM_DEPTH == 8)
8276 if (image->depth > 8)
8277 {
8278 if (logging != MagickFalse)
8279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8280 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8281
8282 image->depth=8;
8283 }
8284#endif
8285
glennrp8e58efd2011-05-20 12:16:29 +00008286 /* Respect the -depth option */
dirkcd979952014-10-24 20:46:54 +00008287 if (image->depth < 4)
glennrpcc95c3f2011-04-18 16:46:48 +00008288 {
cristy16ea1392012-03-21 20:38:41 +00008289 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00008290 *r;
8291
dirkaac49632014-10-24 20:29:33 +00008292 if (image->depth > 2)
glennrp8e58efd2011-05-20 12:16:29 +00008293 {
8294 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00008295 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008296
8297 for (y=0; y < (ssize_t) image->rows; y++)
8298 {
cristy16ea1392012-03-21 20:38:41 +00008299 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008300
cristy16ea1392012-03-21 20:38:41 +00008301 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008302 break;
8303
8304 for (x=0; x < (ssize_t) image->columns; x++)
8305 {
cristy16ea1392012-03-21 20:38:41 +00008306 LBR04PixelRGBA(r);
8307 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008308 }
glennrpbb4f99d2011-05-22 11:13:17 +00008309
glennrp8e58efd2011-05-20 12:16:29 +00008310 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8311 break;
8312 }
8313
8314 if (image->storage_class == PseudoClass && image->colormap != NULL)
8315 {
cristy3e08f112011-05-24 13:19:30 +00008316 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008317 {
glennrp91d99252011-06-25 14:30:13 +00008318 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008319 }
8320 }
8321 }
glennrp8e58efd2011-05-20 12:16:29 +00008322 else if (image->depth > 1)
8323 {
8324 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00008325 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008326
8327 for (y=0; y < (ssize_t) image->rows; y++)
8328 {
cristy16ea1392012-03-21 20:38:41 +00008329 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008330
cristy16ea1392012-03-21 20:38:41 +00008331 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008332 break;
8333
8334 for (x=0; x < (ssize_t) image->columns; x++)
8335 {
cristy16ea1392012-03-21 20:38:41 +00008336 LBR02PixelRGBA(r);
8337 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008338 }
glennrpbb4f99d2011-05-22 11:13:17 +00008339
glennrp8e58efd2011-05-20 12:16:29 +00008340 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8341 break;
8342 }
8343
8344 if (image->storage_class == PseudoClass && image->colormap != NULL)
8345 {
cristy3e08f112011-05-24 13:19:30 +00008346 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008347 {
glennrp91d99252011-06-25 14:30:13 +00008348 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008349 }
8350 }
8351 }
8352 else
8353 {
8354 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00008355 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008356
8357 for (y=0; y < (ssize_t) image->rows; y++)
8358 {
cristy16ea1392012-03-21 20:38:41 +00008359 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008360
cristy16ea1392012-03-21 20:38:41 +00008361 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008362 break;
8363
8364 for (x=0; x < (ssize_t) image->columns; x++)
8365 {
cristy16ea1392012-03-21 20:38:41 +00008366 LBR01PixelRGBA(r);
8367 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008368 }
glennrpbb4f99d2011-05-22 11:13:17 +00008369
glennrp8e58efd2011-05-20 12:16:29 +00008370 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8371 break;
8372 }
8373
8374 if (image->storage_class == PseudoClass && image->colormap != NULL)
8375 {
cristy3e08f112011-05-24 13:19:30 +00008376 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008377 {
glennrp91d99252011-06-25 14:30:13 +00008378 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008379 }
8380 }
8381 }
glennrp9d0ea4d2011-04-22 18:35:57 +00008382 }
8383
glennrp67b9c1a2011-04-22 18:47:36 +00008384 /* To do: set to next higher multiple of 8 */
8385 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00008386 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00008387
glennrp2b013e42010-11-24 16:55:50 +00008388#if (MAGICKCORE_QUANTUM_DEPTH > 16)
8389 /* PNG does not handle depths greater than 16 so reduce it even
8390 * if lossy
8391 */
glennrp8e58efd2011-05-20 12:16:29 +00008392 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00008393 image->depth=16;
8394#endif
8395
glennrp3faa9a32011-04-23 14:00:25 +00008396#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00008397 if (image->depth > 8)
8398 {
8399 /* To do: fill low byte properly */
8400 image->depth=16;
8401 }
8402
glennrpc722dd82011-02-24 05:13:21 +00008403 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristy16ea1392012-03-21 20:38:41 +00008404 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00008405 image->depth = 8;
8406#endif
8407
glennrpd0ee5a22014-03-28 14:28:47 +00008408 image_colors = (int) image->colors;
8409 number_opaque = (int) image->colors;
8410 number_transparent = 0;
8411 number_semitransparent = 0;
8412
glennrp197c8e62014-04-22 23:45:20 +00008413 if (mng_info->write_png_colortype &&
glennrpa8036d62012-11-04 01:46:06 +00008414 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8415 mng_info->write_png_colortype < 4 &&
cristy17f11b02014-12-20 19:37:04 +00008416 image->alpha_trait == UndefinedPixelTrait)))
glennrpa8036d62012-11-04 01:46:06 +00008417 {
8418 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8419 * are not going to need the result.
8420 */
glennrpa8036d62012-11-04 01:46:06 +00008421 if (mng_info->write_png_colortype == 1 ||
8422 mng_info->write_png_colortype == 5)
8423 ping_have_color=MagickFalse;
glennrpa8036d62012-11-04 01:46:06 +00008424
cristy17f11b02014-12-20 19:37:04 +00008425 if (image->alpha_trait != UndefinedPixelTrait)
glennrpa8036d62012-11-04 01:46:06 +00008426 {
8427 number_transparent = 2;
8428 number_semitransparent = 1;
8429 }
glennrpa8036d62012-11-04 01:46:06 +00008430 }
8431
glennrpeb5cb8d2015-06-13 14:01:01 +00008432 if (mng_info->write_png_colortype < 7)
glennrpa8036d62012-11-04 01:46:06 +00008433 {
8434 /* BUILD_PALETTE
8435 *
8436 * Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00008437 * we reduce the transparency to binary and run again, then if there
8438 * 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 +00008439 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8440 * palette. Then (To do) we take care of a final reduction that is only
8441 * needed if there are still 256 colors present and one of them has both
8442 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00008443 */
glennrp82b3c532011-03-22 19:20:54 +00008444
glennrp8ca51ad2011-05-12 21:22:32 +00008445 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008446 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00008447 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008448
glennrp8ca51ad2011-05-12 21:22:32 +00008449 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00008450 {
glennrpa8036d62012-11-04 01:46:06 +00008451 /*
glennrpd71e86a2011-02-24 01:28:37 +00008452 * Sometimes we get DirectClass images that have 256 colors or fewer.
8453 * This code will build a colormap.
8454 *
8455 * Also, sometimes we get PseudoClass images with an out-of-date
8456 * colormap. This code will replace the colormap with a new one.
8457 * Sometimes we get PseudoClass images that have more than 256 colors.
8458 * This code will delete the colormap and change the image to
8459 * DirectClass.
8460 *
cristy8a46d822012-08-28 23:32:39 +00008461 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008462 * even though it sometimes contains left-over non-opaque values.
8463 *
8464 * Also we gather some information (number of opaque, transparent,
8465 * and semitransparent pixels, and whether the image has any non-gray
8466 * pixels or only black-and-white pixels) that we might need later.
8467 *
8468 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8469 * we need to check for bogus non-opaque values, at least.
8470 */
glennrp3c218112010-11-27 15:31:26 +00008471
glennrpd71e86a2011-02-24 01:28:37 +00008472 int
8473 n;
glennrp3c218112010-11-27 15:31:26 +00008474
cristy16ea1392012-03-21 20:38:41 +00008475 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008476 opaque[260],
8477 semitransparent[260],
8478 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008479
cristy16ea1392012-03-21 20:38:41 +00008480 register const Quantum
8481 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008482
cristy16ea1392012-03-21 20:38:41 +00008483 register Quantum
8484 *q,
glennrpfd05d622011-02-25 04:10:33 +00008485 *r;
8486
glennrpd71e86a2011-02-24 01:28:37 +00008487 if (logging != MagickFalse)
8488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489 " Enter BUILD_PALETTE:");
8490
8491 if (logging != MagickFalse)
8492 {
glennrp03812ae2010-12-24 01:31:34 +00008493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008494 " image->columns=%.20g",(double) image->columns);
8495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8496 " image->rows=%.20g",(double) image->rows);
8497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00008498 " image->alpha_trait=%.20g",(double) image->alpha_trait);
glennrpd71e86a2011-02-24 01:28:37 +00008499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8500 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008501
glennrpfd05d622011-02-25 04:10:33 +00008502 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008503 {
8504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008505 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008507 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008508
glennrpd71e86a2011-02-24 01:28:37 +00008509 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008510 {
glennrpd71e86a2011-02-24 01:28:37 +00008511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8512 " %d (%d,%d,%d,%d)",
8513 (int) i,
8514 (int) image->colormap[i].red,
8515 (int) image->colormap[i].green,
8516 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008517 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008518 }
glennrp2cc891a2010-12-24 13:44:32 +00008519
glennrpd71e86a2011-02-24 01:28:37 +00008520 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8521 {
8522 if (i > 255)
8523 {
8524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8525 " %d (%d,%d,%d,%d)",
8526 (int) i,
8527 (int) image->colormap[i].red,
8528 (int) image->colormap[i].green,
8529 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008530 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008531 }
8532 }
glennrp03812ae2010-12-24 01:31:34 +00008533 }
glennrp7ddcc222010-12-11 05:01:05 +00008534
glennrpd71e86a2011-02-24 01:28:37 +00008535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8536 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008537
glennrpd71e86a2011-02-24 01:28:37 +00008538 if (image->colors == 0)
cristy16ea1392012-03-21 20:38:41 +00008539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8540 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008541
glennrp8d3d6e52011-04-19 04:39:51 +00008542 if (ping_preserve_colormap == MagickFalse)
8543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8544 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008545 }
8546
glennrpd71e86a2011-02-24 01:28:37 +00008547 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008548 number_opaque = 0;
8549 number_semitransparent = 0;
8550 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008551
8552 for (y=0; y < (ssize_t) image->rows; y++)
8553 {
8554 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8555
cristy16ea1392012-03-21 20:38:41 +00008556 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008557 break;
8558
8559 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008560 {
cristy17f11b02014-12-20 19:37:04 +00008561 if (image->alpha_trait == UndefinedPixelTrait ||
cristy16ea1392012-03-21 20:38:41 +00008562 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008563 {
8564 if (number_opaque < 259)
8565 {
8566 if (number_opaque == 0)
8567 {
cristy16ea1392012-03-21 20:38:41 +00008568 GetPixelInfoPixel(image, q, opaque);
8569 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008570 number_opaque=1;
8571 }
glennrp2cc891a2010-12-24 13:44:32 +00008572
glennrpd71e86a2011-02-24 01:28:37 +00008573 for (i=0; i< (ssize_t) number_opaque; i++)
8574 {
dirk68a6b502016-01-02 17:47:58 +01008575 if (Magick_png_color_equal(image,q,opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008576 break;
8577 }
glennrp7ddcc222010-12-11 05:01:05 +00008578
cristy16ea1392012-03-21 20:38:41 +00008579 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008580 {
8581 number_opaque++;
cristy16ea1392012-03-21 20:38:41 +00008582 GetPixelInfoPixel(image, q, opaque+i);
8583 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008584 }
8585 }
8586 }
cristy16ea1392012-03-21 20:38:41 +00008587 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008588 {
8589 if (number_transparent < 259)
8590 {
8591 if (number_transparent == 0)
8592 {
cristy16ea1392012-03-21 20:38:41 +00008593 GetPixelInfoPixel(image, q, transparent);
8594 ping_trans_color.red=(unsigned short)
8595 GetPixelRed(image,q);
8596 ping_trans_color.green=(unsigned short)
8597 GetPixelGreen(image,q);
8598 ping_trans_color.blue=(unsigned short)
8599 GetPixelBlue(image,q);
8600 ping_trans_color.gray=(unsigned short)
cristy972d1c42012-07-14 23:29:14 +00008601 GetPixelGray(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008602 number_transparent = 1;
8603 }
8604
8605 for (i=0; i< (ssize_t) number_transparent; i++)
8606 {
dirk68a6b502016-01-02 17:47:58 +01008607 if (Magick_png_color_equal(image,q,transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008608 break;
8609 }
8610
8611 if (i == (ssize_t) number_transparent &&
8612 number_transparent < 259)
8613 {
8614 number_transparent++;
cristy16ea1392012-03-21 20:38:41 +00008615 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008616 }
8617 }
8618 }
8619 else
8620 {
8621 if (number_semitransparent < 259)
8622 {
8623 if (number_semitransparent == 0)
8624 {
cristy16ea1392012-03-21 20:38:41 +00008625 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008626 number_semitransparent = 1;
8627 }
8628
8629 for (i=0; i< (ssize_t) number_semitransparent; i++)
8630 {
dirk68a6b502016-01-02 17:47:58 +01008631 if (Magick_png_color_equal(image,q,semitransparent+i)
cristy16ea1392012-03-21 20:38:41 +00008632 && GetPixelAlpha(image,q) ==
8633 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008634 break;
8635 }
8636
8637 if (i == (ssize_t) number_semitransparent &&
8638 number_semitransparent < 259)
8639 {
8640 number_semitransparent++;
cristy16ea1392012-03-21 20:38:41 +00008641 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008642 }
8643 }
8644 }
cristy16ea1392012-03-21 20:38:41 +00008645 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008646 }
8647 }
8648
cristy4054bfb2011-08-29 23:41:39 +00008649 if (mng_info->write_png8 == MagickFalse &&
8650 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008651 {
8652 /* Add the background color to the palette, if it
8653 * isn't already there.
8654 */
glennrpc6c391a2011-04-27 02:23:56 +00008655 if (logging != MagickFalse)
8656 {
8657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8658 " Check colormap for background (%d,%d,%d)",
8659 (int) image->background_color.red,
8660 (int) image->background_color.green,
8661 (int) image->background_color.blue);
8662 }
glennrpd71e86a2011-02-24 01:28:37 +00008663 for (i=0; i<number_opaque; i++)
8664 {
glennrpca7ad3a2011-04-26 04:44:54 +00008665 if (opaque[i].red == image->background_color.red &&
8666 opaque[i].green == image->background_color.green &&
8667 opaque[i].blue == image->background_color.blue)
8668 break;
glennrpd71e86a2011-02-24 01:28:37 +00008669 }
glennrpd71e86a2011-02-24 01:28:37 +00008670 if (number_opaque < 259 && i == number_opaque)
8671 {
glennrp8e045c82011-04-27 16:40:27 +00008672 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008673 ping_background.index = i;
glennrp388a8c82012-06-27 13:41:51 +00008674 number_opaque++;
glennrpc6c391a2011-04-27 02:23:56 +00008675 if (logging != MagickFalse)
8676 {
8677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8678 " background_color index is %d",(int) i);
8679 }
8680
glennrpd71e86a2011-02-24 01:28:37 +00008681 }
glennrpa080bc32011-03-11 18:03:44 +00008682 else if (logging != MagickFalse)
8683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8684 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008685 }
8686
8687 image_colors=number_opaque+number_transparent+number_semitransparent;
8688
8689 if (logging != MagickFalse)
8690 {
8691 if (image_colors > 256)
8692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8693 " image has more than 256 colors");
8694
8695 else
8696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8697 " image has %d colors",image_colors);
8698 }
8699
glennrp8d3d6e52011-04-19 04:39:51 +00008700 if (ping_preserve_colormap != MagickFalse)
8701 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008702
glennrpfd05d622011-02-25 04:10:33 +00008703 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008704 {
8705 ping_have_color=MagickFalse;
8706 ping_have_non_bw=MagickFalse;
8707
glennrp45d4c342013-12-05 21:24:54 +00008708 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
glennrp0fa25802012-07-20 14:01:06 +00008709 {
glennrp96bc6202013-12-03 19:21:04 +00008710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8711 "incompatible colorspace");
glennrp0fa25802012-07-20 14:01:06 +00008712 ping_have_color=MagickTrue;
glennrp98b95772012-11-29 01:32:00 +00008713 ping_have_non_bw=MagickTrue;
glennrp0fa25802012-07-20 14:01:06 +00008714 }
8715
glennrpd71e86a2011-02-24 01:28:37 +00008716 if(image_colors > 256)
8717 {
8718 for (y=0; y < (ssize_t) image->rows; y++)
8719 {
8720 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8721
cristy16ea1392012-03-21 20:38:41 +00008722 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008723 break;
8724
glennrpe5e6b802011-07-20 14:44:40 +00008725 s=q;
8726 for (x=0; x < (ssize_t) image->columns; x++)
8727 {
cristy16ea1392012-03-21 20:38:41 +00008728 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8729 GetPixelRed(image,s) != GetPixelBlue(image,s))
glennrpe5e6b802011-07-20 14:44:40 +00008730 {
8731 ping_have_color=MagickTrue;
8732 ping_have_non_bw=MagickTrue;
8733 break;
8734 }
cristy16ea1392012-03-21 20:38:41 +00008735 s+=GetPixelChannels(image);
glennrpe5e6b802011-07-20 14:44:40 +00008736 }
8737
8738 if (ping_have_color != MagickFalse)
8739 break;
8740
glennrpd71e86a2011-02-24 01:28:37 +00008741 /* Worst case is black-and-white; we are looking at every
8742 * pixel twice.
8743 */
8744
glennrpd71e86a2011-02-24 01:28:37 +00008745 if (ping_have_non_bw == MagickFalse)
8746 {
8747 s=q;
8748 for (x=0; x < (ssize_t) image->columns; x++)
8749 {
cristy16ea1392012-03-21 20:38:41 +00008750 if (GetPixelRed(image,s) != 0 &&
8751 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008752 {
8753 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008754 break;
glennrpd71e86a2011-02-24 01:28:37 +00008755 }
cristy16ea1392012-03-21 20:38:41 +00008756 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008757 }
glennrpe5e6b802011-07-20 14:44:40 +00008758 }
glennrpd71e86a2011-02-24 01:28:37 +00008759 }
glennrpbb4f99d2011-05-22 11:13:17 +00008760 }
8761 }
glennrpd71e86a2011-02-24 01:28:37 +00008762
8763 if (image_colors < 257)
8764 {
cristy16ea1392012-03-21 20:38:41 +00008765 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008766 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008767
glennrpd71e86a2011-02-24 01:28:37 +00008768 /*
8769 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008770 */
8771
glennrpd71e86a2011-02-24 01:28:37 +00008772 if (logging != MagickFalse)
8773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8774 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008775
glennrpd71e86a2011-02-24 01:28:37 +00008776 /* Sort palette, transparent first */;
8777
8778 n = 0;
8779
8780 for (i=0; i<number_transparent; i++)
8781 colormap[n++] = transparent[i];
8782
8783 for (i=0; i<number_semitransparent; i++)
8784 colormap[n++] = semitransparent[i];
8785
8786 for (i=0; i<number_opaque; i++)
8787 colormap[n++] = opaque[i];
8788
glennrpc6c391a2011-04-27 02:23:56 +00008789 ping_background.index +=
8790 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008791
glennrpd71e86a2011-02-24 01:28:37 +00008792 /* image_colors < 257; search the colormap instead of the pixels
8793 * to get ping_have_color and ping_have_non_bw
8794 */
8795 for (i=0; i<n; i++)
8796 {
8797 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008798 {
glennrpd71e86a2011-02-24 01:28:37 +00008799 if (colormap[i].red != colormap[i].green ||
8800 colormap[i].red != colormap[i].blue)
8801 {
8802 ping_have_color=MagickTrue;
8803 ping_have_non_bw=MagickTrue;
8804 break;
8805 }
8806 }
8807
8808 if (ping_have_non_bw == MagickFalse)
8809 {
8810 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008811 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008812 }
glennrp8bb3a022010-12-13 20:40:04 +00008813 }
8814
glennrpd71e86a2011-02-24 01:28:37 +00008815 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8816 (number_transparent == 0 && number_semitransparent == 0)) &&
8817 (((mng_info->write_png_colortype-1) ==
8818 PNG_COLOR_TYPE_PALETTE) ||
8819 (mng_info->write_png_colortype == 0)))
8820 {
glennrp6185c532011-01-14 17:58:40 +00008821 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008822 {
glennrpd71e86a2011-02-24 01:28:37 +00008823 if (n != (ssize_t) image_colors)
8824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8825 " image_colors (%d) and n (%d) don't match",
8826 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008827
glennrpd71e86a2011-02-24 01:28:37 +00008828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8829 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008830 }
glennrp03812ae2010-12-24 01:31:34 +00008831
glennrpd71e86a2011-02-24 01:28:37 +00008832 image->colors = image_colors;
8833
cristy16ea1392012-03-21 20:38:41 +00008834 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008835 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008836 ThrowWriterException(ResourceLimitError,
8837 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008838
8839 for (i=0; i< (ssize_t) image_colors; i++)
8840 image->colormap[i] = colormap[i];
8841
8842 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008843 {
glennrpd71e86a2011-02-24 01:28:37 +00008844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8845 " image->colors=%d (%d)",
8846 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008847
glennrpd71e86a2011-02-24 01:28:37 +00008848 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8849 " Update the pixel indexes");
8850 }
glennrp6185c532011-01-14 17:58:40 +00008851
glennrpfd05d622011-02-25 04:10:33 +00008852 /* Sync the pixel indices with the new colormap */
8853
glennrpd71e86a2011-02-24 01:28:37 +00008854 for (y=0; y < (ssize_t) image->rows; y++)
8855 {
cristy16ea1392012-03-21 20:38:41 +00008856 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008857
cristy16ea1392012-03-21 20:38:41 +00008858 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008859 break;
glennrp6185c532011-01-14 17:58:40 +00008860
glennrpd71e86a2011-02-24 01:28:37 +00008861 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008862 {
glennrpd71e86a2011-02-24 01:28:37 +00008863 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008864 {
cristy17f11b02014-12-20 19:37:04 +00008865 if ((image->alpha_trait == UndefinedPixelTrait ||
cristy16ea1392012-03-21 20:38:41 +00008866 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8867 image->colormap[i].red == GetPixelRed(image,q) &&
8868 image->colormap[i].green == GetPixelGreen(image,q) &&
8869 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008870 {
cristy16ea1392012-03-21 20:38:41 +00008871 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008872 break;
glennrp6185c532011-01-14 17:58:40 +00008873 }
glennrp6185c532011-01-14 17:58:40 +00008874 }
cristy16ea1392012-03-21 20:38:41 +00008875 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008876 }
glennrp6185c532011-01-14 17:58:40 +00008877
glennrpd71e86a2011-02-24 01:28:37 +00008878 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8879 break;
8880 }
8881 }
8882 }
8883
8884 if (logging != MagickFalse)
8885 {
8886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8887 " image->colors=%d", (int) image->colors);
8888
8889 if (image->colormap != NULL)
8890 {
8891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008892 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008893
8894 for (i=0; i < (ssize_t) image->colors; i++)
8895 {
cristy72988482011-03-29 16:34:38 +00008896 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008897 {
8898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8899 " %d (%d,%d,%d,%d)",
8900 (int) i,
8901 (int) image->colormap[i].red,
8902 (int) image->colormap[i].green,
8903 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008904 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008905 }
glennrp6185c532011-01-14 17:58:40 +00008906 }
8907 }
glennrp03812ae2010-12-24 01:31:34 +00008908
glennrpd71e86a2011-02-24 01:28:37 +00008909 if (number_transparent < 257)
8910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8911 " number_transparent = %d",
8912 number_transparent);
8913 else
glennrp03812ae2010-12-24 01:31:34 +00008914
glennrpd71e86a2011-02-24 01:28:37 +00008915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8916 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008917
glennrpd71e86a2011-02-24 01:28:37 +00008918 if (number_opaque < 257)
8919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8920 " number_opaque = %d",
8921 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008922
glennrpd71e86a2011-02-24 01:28:37 +00008923 else
8924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8925 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008926
glennrpd71e86a2011-02-24 01:28:37 +00008927 if (number_semitransparent < 257)
8928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8929 " number_semitransparent = %d",
8930 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008931
glennrpd71e86a2011-02-24 01:28:37 +00008932 else
8933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8934 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008935
glennrpd71e86a2011-02-24 01:28:37 +00008936 if (ping_have_non_bw == MagickFalse)
8937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8938 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008939
glennrpd71e86a2011-02-24 01:28:37 +00008940 else if (ping_have_color == MagickFalse)
8941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8942 " All pixels and the background are gray");
8943
8944 else
8945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8946 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008947
glennrp03812ae2010-12-24 01:31:34 +00008948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8949 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008950 }
glennrpfd05d622011-02-25 04:10:33 +00008951
glennrpc8c2f062011-02-25 19:00:33 +00008952 if (mng_info->write_png8 == MagickFalse)
8953 break;
glennrpfd05d622011-02-25 04:10:33 +00008954
glennrpc8c2f062011-02-25 19:00:33 +00008955 /* Make any reductions necessary for the PNG8 format */
8956 if (image_colors <= 256 &&
8957 image_colors != 0 && image->colormap != NULL &&
8958 number_semitransparent == 0 &&
8959 number_transparent <= 1)
8960 break;
8961
8962 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008963 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8964 * transparent color so if more than one is transparent we merge
8965 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008966 */
glennrp130fc452011-08-20 03:43:18 +00008967 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008968 {
8969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8970 " Thresholding the alpha channel to binary");
8971
8972 for (y=0; y < (ssize_t) image->rows; y++)
8973 {
cristy16ea1392012-03-21 20:38:41 +00008974 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008975
cristy16ea1392012-03-21 20:38:41 +00008976 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008977 break;
8978
8979 for (x=0; x < (ssize_t) image->columns; x++)
8980 {
cristy16ea1392012-03-21 20:38:41 +00008981 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008982 {
cristy11a06d32015-01-04 12:03:27 +00008983 SetPixelViaPixelInfo(image,&image->background_color,r);
cristy16ea1392012-03-21 20:38:41 +00008984 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008985 }
8986 else
cristy16ea1392012-03-21 20:38:41 +00008987 SetPixelAlpha(image,OpaqueAlpha,r);
8988 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008989 }
glennrpbb4f99d2011-05-22 11:13:17 +00008990
glennrpc8c2f062011-02-25 19:00:33 +00008991 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8992 break;
8993
8994 if (image_colors != 0 && image_colors <= 256 &&
8995 image->colormap != NULL)
8996 for (i=0; i<image_colors; i++)
cristy16ea1392012-03-21 20:38:41 +00008997 image->colormap[i].alpha =
8998 (image->colormap[i].alpha > TransparentAlpha/2 ?
8999 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00009000 }
9001 continue;
9002 }
9003
9004 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00009005 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9006 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9007 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00009008 */
glennrpd3371642011-03-22 19:42:23 +00009009 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9010 {
9011 if (logging != MagickFalse)
9012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9013 " Quantizing the background color to 4-4-4");
9014
9015 tried_444 = MagickTrue;
9016
glennrp91d99252011-06-25 14:30:13 +00009017 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00009018
9019 if (logging != MagickFalse)
9020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9021 " Quantizing the pixel colors to 4-4-4");
9022
9023 if (image->colormap == NULL)
9024 {
9025 for (y=0; y < (ssize_t) image->rows; y++)
9026 {
cristy16ea1392012-03-21 20:38:41 +00009027 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00009028
cristy16ea1392012-03-21 20:38:41 +00009029 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00009030 break;
9031
9032 for (x=0; x < (ssize_t) image->columns; x++)
9033 {
cristy16ea1392012-03-21 20:38:41 +00009034 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00009035 LBR04PixelRGB(r);
cristy16ea1392012-03-21 20:38:41 +00009036 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00009037 }
glennrpbb4f99d2011-05-22 11:13:17 +00009038
glennrpd3371642011-03-22 19:42:23 +00009039 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9040 break;
9041 }
9042 }
9043
9044 else /* Should not reach this; colormap already exists and
9045 must be <= 256 */
9046 {
9047 if (logging != MagickFalse)
9048 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9049 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00009050
glennrpd3371642011-03-22 19:42:23 +00009051 for (i=0; i<image_colors; i++)
9052 {
glennrp91d99252011-06-25 14:30:13 +00009053 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00009054 }
9055 }
9056 continue;
9057 }
9058
glennrp82b3c532011-03-22 19:20:54 +00009059 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9060 {
9061 if (logging != MagickFalse)
9062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9063 " Quantizing the background color to 3-3-3");
9064
9065 tried_333 = MagickTrue;
9066
glennrp91d99252011-06-25 14:30:13 +00009067 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00009068
9069 if (logging != MagickFalse)
9070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009071 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00009072
9073 if (image->colormap == NULL)
9074 {
9075 for (y=0; y < (ssize_t) image->rows; y++)
9076 {
cristy16ea1392012-03-21 20:38:41 +00009077 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00009078
cristy16ea1392012-03-21 20:38:41 +00009079 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00009080 break;
9081
9082 for (x=0; x < (ssize_t) image->columns; x++)
9083 {
cristy16ea1392012-03-21 20:38:41 +00009084 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9085 LBR03RGB(r);
9086 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00009087 }
glennrpbb4f99d2011-05-22 11:13:17 +00009088
glennrp82b3c532011-03-22 19:20:54 +00009089 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9090 break;
9091 }
9092 }
9093
9094 else /* Should not reach this; colormap already exists and
9095 must be <= 256 */
9096 {
9097 if (logging != MagickFalse)
9098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009099 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00009100 for (i=0; i<image_colors; i++)
9101 {
glennrp91d99252011-06-25 14:30:13 +00009102 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00009103 }
glennrpd3371642011-03-22 19:42:23 +00009104 }
9105 continue;
glennrp82b3c532011-03-22 19:20:54 +00009106 }
glennrpc8c2f062011-02-25 19:00:33 +00009107
glennrp8ca51ad2011-05-12 21:22:32 +00009108 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00009109 {
9110 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00009111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00009112 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00009113
glennrp8ca51ad2011-05-12 21:22:32 +00009114 tried_332 = MagickTrue;
9115
glennrp3faa9a32011-04-23 14:00:25 +00009116 /* Red and green were already done so we only quantize the blue
9117 * channel
9118 */
9119
glennrp91d99252011-06-25 14:30:13 +00009120 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00009121
glennrpc8c2f062011-02-25 19:00:33 +00009122 if (logging != MagickFalse)
9123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009124 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00009125
glennrpc8c2f062011-02-25 19:00:33 +00009126 if (image->colormap == NULL)
9127 {
9128 for (y=0; y < (ssize_t) image->rows; y++)
9129 {
cristy16ea1392012-03-21 20:38:41 +00009130 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00009131
cristy16ea1392012-03-21 20:38:41 +00009132 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00009133 break;
9134
9135 for (x=0; x < (ssize_t) image->columns; x++)
9136 {
cristy16ea1392012-03-21 20:38:41 +00009137 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00009138 LBR02PixelBlue(r);
cristy16ea1392012-03-21 20:38:41 +00009139 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00009140 }
glennrpbb4f99d2011-05-22 11:13:17 +00009141
glennrpc8c2f062011-02-25 19:00:33 +00009142 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9143 break;
9144 }
9145 }
glennrpfd05d622011-02-25 04:10:33 +00009146
glennrpc8c2f062011-02-25 19:00:33 +00009147 else /* Should not reach this; colormap already exists and
9148 must be <= 256 */
9149 {
9150 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00009151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009152 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00009153 for (i=0; i<image_colors; i++)
9154 {
glennrp91d99252011-06-25 14:30:13 +00009155 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00009156 }
9157 }
9158 continue;
9159 }
glennrp8ca51ad2011-05-12 21:22:32 +00009160
9161 if (image_colors == 0 || image_colors > 256)
9162 {
glennrp34ef7202013-09-27 17:36:53 +00009163 /* Take care of special case with 256 opaque colors + 1 transparent
glennrp8ca51ad2011-05-12 21:22:32 +00009164 * color. We don't need to quantize to 2-3-2-1; we only need to
9165 * eliminate one color, so we'll merge the two darkest red
9166 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9167 */
glennrp34ef7202013-09-27 17:36:53 +00009168 if (logging != MagickFalse)
9169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9170 " Merging two dark red background colors to 3-3-2-1");
9171
glennrp8ca51ad2011-05-12 21:22:32 +00009172 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9173 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9174 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9175 {
9176 image->background_color.red=ScaleCharToQuantum(0x24);
9177 }
glennrpbb4f99d2011-05-22 11:13:17 +00009178
glennrp34ef7202013-09-27 17:36:53 +00009179 if (logging != MagickFalse)
9180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9181 " Merging two dark red pixel colors to 3-3-2-1");
9182
glennrp8ca51ad2011-05-12 21:22:32 +00009183 if (image->colormap == NULL)
9184 {
9185 for (y=0; y < (ssize_t) image->rows; y++)
9186 {
cristy16ea1392012-03-21 20:38:41 +00009187 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00009188
cristy16ea1392012-03-21 20:38:41 +00009189 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00009190 break;
glennrpbb4f99d2011-05-22 11:13:17 +00009191
glennrp8ca51ad2011-05-12 21:22:32 +00009192 for (x=0; x < (ssize_t) image->columns; x++)
9193 {
cristy16ea1392012-03-21 20:38:41 +00009194 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9195 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9196 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9197 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00009198 {
cristy16ea1392012-03-21 20:38:41 +00009199 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00009200 }
cristy16ea1392012-03-21 20:38:41 +00009201 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00009202 }
glennrpbb4f99d2011-05-22 11:13:17 +00009203
glennrp8ca51ad2011-05-12 21:22:32 +00009204 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9205 break;
glennrpbb4f99d2011-05-22 11:13:17 +00009206
glennrp8ca51ad2011-05-12 21:22:32 +00009207 }
9208 }
9209
9210 else
9211 {
9212 for (i=0; i<image_colors; i++)
9213 {
9214 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9215 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9216 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9217 {
9218 image->colormap[i].red=ScaleCharToQuantum(0x24);
9219 }
9220 }
9221 }
9222 }
glennrpd71e86a2011-02-24 01:28:37 +00009223 }
glennrpa8036d62012-11-04 01:46:06 +00009224 }
glennrpfd05d622011-02-25 04:10:33 +00009225 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00009226
glennrpfd05d622011-02-25 04:10:33 +00009227 /* If we are excluding the tRNS chunk and there is transparency,
9228 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9229 * PNG.
glennrp8d579662011-02-23 02:05:02 +00009230 */
glennrp0e8ea192010-12-24 18:00:33 +00009231 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9232 (number_transparent != 0 || number_semitransparent != 0))
9233 {
glennrpd17915c2011-04-29 14:24:22 +00009234 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00009235
9236 if (ping_have_color == MagickFalse)
9237 mng_info->write_png_colortype = 5;
9238
9239 else
9240 mng_info->write_png_colortype = 7;
9241
glennrp8d579662011-02-23 02:05:02 +00009242 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00009243 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00009244 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00009245
glennrp0e8ea192010-12-24 18:00:33 +00009246 }
9247
glennrpfd05d622011-02-25 04:10:33 +00009248 /* See if cheap transparency is possible. It is only possible
9249 * when there is a single transparent color, no semitransparent
9250 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00009251 * as the transparent color. We only need this information if
9252 * we are writing a PNG with colortype 0 or 2, and we have not
9253 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00009254 */
glennrp5a39f372011-02-25 04:52:16 +00009255 if (number_transparent == 1 &&
9256 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00009257 {
9258 ping_have_cheap_transparency = MagickTrue;
9259
9260 if (number_semitransparent != 0)
9261 ping_have_cheap_transparency = MagickFalse;
9262
9263 else if (image_colors == 0 || image_colors > 256 ||
9264 image->colormap == NULL)
9265 {
cristy16ea1392012-03-21 20:38:41 +00009266 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00009267 *q;
9268
glennrpfd05d622011-02-25 04:10:33 +00009269 for (y=0; y < (ssize_t) image->rows; y++)
9270 {
9271 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9272
cristy16ea1392012-03-21 20:38:41 +00009273 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00009274 break;
9275
9276 for (x=0; x < (ssize_t) image->columns; x++)
9277 {
cristy16ea1392012-03-21 20:38:41 +00009278 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9279 (unsigned short) GetPixelRed(image,q) ==
9280 ping_trans_color.red &&
9281 (unsigned short) GetPixelGreen(image,q) ==
9282 ping_trans_color.green &&
9283 (unsigned short) GetPixelBlue(image,q) ==
9284 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00009285 {
9286 ping_have_cheap_transparency = MagickFalse;
9287 break;
9288 }
9289
cristy16ea1392012-03-21 20:38:41 +00009290 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00009291 }
glennrpbb4f99d2011-05-22 11:13:17 +00009292
glennrpfd05d622011-02-25 04:10:33 +00009293 if (ping_have_cheap_transparency == MagickFalse)
9294 break;
9295 }
9296 }
9297 else
9298 {
glennrp67b9c1a2011-04-22 18:47:36 +00009299 /* Assuming that image->colormap[0] is the one transparent color
9300 * and that all others are opaque.
9301 */
glennrpfd05d622011-02-25 04:10:33 +00009302 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00009303 for (i=1; i<image_colors; i++)
9304 if (image->colormap[i].red == image->colormap[0].red &&
9305 image->colormap[i].green == image->colormap[0].green &&
9306 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00009307 {
glennrp67b9c1a2011-04-22 18:47:36 +00009308 ping_have_cheap_transparency = MagickFalse;
9309 break;
glennrpfd05d622011-02-25 04:10:33 +00009310 }
9311 }
glennrpbb4f99d2011-05-22 11:13:17 +00009312
glennrpfd05d622011-02-25 04:10:33 +00009313 if (logging != MagickFalse)
9314 {
9315 if (ping_have_cheap_transparency == MagickFalse)
9316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9317 " Cheap transparency is not possible.");
9318
9319 else
9320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9321 " Cheap transparency is possible.");
9322 }
9323 }
9324 else
9325 ping_have_cheap_transparency = MagickFalse;
9326
glennrp8640fb52010-11-23 15:48:26 +00009327 image_depth=image->depth;
9328
glennrp26c990a2010-11-23 02:23:20 +00009329 quantum_info = (QuantumInfo *) NULL;
9330 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00009331 image_colors=(int) image->colors;
cristy17f11b02014-12-20 19:37:04 +00009332 image_matte=image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse;
glennrp26c990a2010-11-23 02:23:20 +00009333
glennrp48c20622014-04-23 01:00:37 +00009334 if (mng_info->write_png_colortype < 5)
glennrp197c8e62014-04-22 23:45:20 +00009335 mng_info->IsPalette=image->storage_class == PseudoClass &&
9336 image_colors <= 256 && image->colormap != NULL;
9337 else
9338 mng_info->IsPalette = MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00009339
glennrp52a479c2011-02-26 21:14:38 +00009340 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9341 (image->colors == 0 || image->colormap == NULL))
9342 {
cristy16ea1392012-03-21 20:38:41 +00009343 image_info=DestroyImageInfo(image_info);
9344 image=DestroyImage(image);
9345 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00009346 "Cannot write PNG8 or color-type 3; colormap is NULL",
cristy16ea1392012-03-21 20:38:41 +00009347 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00009348 return(MagickFalse);
9349 }
9350
cristy3ed852e2009-09-05 21:47:34 +00009351 /*
9352 Allocate the PNG structures
9353 */
9354#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00009355 error_info.image=image;
9356 error_info.exception=exception;
glennrp3e0971d2014-11-06 12:59:43 +00009357 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009358 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9359 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00009360
cristy3ed852e2009-09-05 21:47:34 +00009361#else
glennrp3e0971d2014-11-06 12:59:43 +00009362 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009363 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00009364
cristy3ed852e2009-09-05 21:47:34 +00009365#endif
9366 if (ping == (png_struct *) NULL)
9367 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009368
cristy3ed852e2009-09-05 21:47:34 +00009369 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00009370
cristy3ed852e2009-09-05 21:47:34 +00009371 if (ping_info == (png_info *) NULL)
9372 {
9373 png_destroy_write_struct(&ping,(png_info **) NULL);
9374 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9375 }
glennrp0fe50b42010-11-16 03:52:51 +00009376
cristy3ed852e2009-09-05 21:47:34 +00009377 png_set_write_fn(ping,image,png_put_data,png_flush_data);
cristy09973322013-06-30 21:06:30 +00009378 pixel_info=(MemoryInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00009379
glennrp5af765f2010-03-30 11:12:18 +00009380 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009381 {
9382 /*
9383 PNG write failed.
9384 */
9385#ifdef PNG_DEBUG
9386 if (image_info->verbose)
9387 (void) printf("PNG write has failed.\n");
9388#endif
9389 png_destroy_write_struct(&ping,&ping_info);
glennrp868fff32014-03-16 22:09:06 +00009390#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00009391 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009392#endif
glennrpedaa0382012-04-12 14:16:21 +00009393
cristy09973322013-06-30 21:06:30 +00009394 if (pixel_info != (MemoryInfo *) NULL)
9395 pixel_info=RelinquishVirtualMemory(pixel_info);
glennrpedaa0382012-04-12 14:16:21 +00009396
9397 if (quantum_info != (QuantumInfo *) NULL)
9398 quantum_info=DestroyQuantumInfo(quantum_info);
9399
cristy16ea1392012-03-21 20:38:41 +00009400 if (ping_have_blob != MagickFalse)
9401 (void) CloseBlob(image);
9402 image_info=DestroyImageInfo(image_info);
9403 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009404 return(MagickFalse);
9405 }
glennrpedaa0382012-04-12 14:16:21 +00009406
9407 /* { For navigation to end of SETJMP-protected block. Within this
9408 * block, use png_error() instead of Throwing an Exception, to ensure
9409 * that libpng is able to clean up, and that the semaphore is unlocked.
9410 */
9411
glennrp868fff32014-03-16 22:09:06 +00009412#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpedaa0382012-04-12 14:16:21 +00009413 LockSemaphoreInfo(ping_semaphore);
9414#endif
9415
glennrp943b7d32013-04-21 00:40:38 +00009416#ifdef PNG_BENIGN_ERRORS_SUPPORTED
glennrpa3a5f952013-04-17 12:58:15 +00009417 /* Allow benign errors */
9418 png_set_benign_errors(ping, 1);
9419#endif
9420
dirkf9eb2042015-10-22 13:22:42 +02009421#ifdef PNG_SET_USER_LIMITS_SUPPORTED
9422 /* Reject images with too many rows or columns */
9423 png_set_user_limits(ping,
9424 (png_uint_32) MagickMin(0x7fffffffL,
9425 GetMagickResourceLimit(WidthResource)),
9426 (png_uint_32) MagickMin(0x7fffffffL,
9427 GetMagickResourceLimit(HeightResource)));
9428#endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9429
cristy3ed852e2009-09-05 21:47:34 +00009430 /*
9431 Prepare PNG for writing.
9432 */
glennrp9bf97b62012-06-06 21:03:14 +00009433
cristy3ed852e2009-09-05 21:47:34 +00009434#if defined(PNG_MNG_FEATURES_SUPPORTED)
9435 if (mng_info->write_mng)
glennrp25024a62012-06-07 11:38:34 +00009436 {
cristy3ed852e2009-09-05 21:47:34 +00009437 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp25024a62012-06-07 11:38:34 +00009438# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9439 /* Disable new libpng-1.5.10 feature when writing a MNG because
9440 * zero-length PLTE is OK
9441 */
9442 png_set_check_for_invalid_index (ping, 0);
9443# endif
9444 }
glennrp2b013e42010-11-24 16:55:50 +00009445
cristy3ed852e2009-09-05 21:47:34 +00009446#else
9447# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9448 if (mng_info->write_mng)
9449 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00009450
cristy3ed852e2009-09-05 21:47:34 +00009451# endif
9452#endif
glennrp2b013e42010-11-24 16:55:50 +00009453
cristy3ed852e2009-09-05 21:47:34 +00009454 x=0;
glennrp2b013e42010-11-24 16:55:50 +00009455
cristy4e5bc842010-06-09 13:56:01 +00009456 ping_width=(png_uint_32) image->columns;
9457 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00009458
cristy3ed852e2009-09-05 21:47:34 +00009459 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9460 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009461
glennrpfd164d22013-01-26 21:10:22 +00009462 if (mng_info->write_png48 || mng_info->write_png64)
9463 image_depth=16;
9464
cristy3ed852e2009-09-05 21:47:34 +00009465 if (mng_info->write_png_depth != 0)
9466 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009467
cristy3ed852e2009-09-05 21:47:34 +00009468 /* Adjust requested depth to next higher valid depth if necessary */
9469 if (image_depth > 8)
9470 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009471
cristy3ed852e2009-09-05 21:47:34 +00009472 if ((image_depth > 4) && (image_depth < 8))
9473 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009474
cristy3ed852e2009-09-05 21:47:34 +00009475 if (image_depth == 3)
9476 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00009477
cristy3ed852e2009-09-05 21:47:34 +00009478 if (logging != MagickFalse)
9479 {
9480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009481 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00009482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009483 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00009484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00009485 " image_matte=%.20g",(double) image->alpha_trait);
cristy3ed852e2009-09-05 21:47:34 +00009486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009487 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00009488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009489 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00009490 }
glennrp8640fb52010-11-23 15:48:26 +00009491
cristy3ed852e2009-09-05 21:47:34 +00009492 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00009493 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00009494
glennrp26f37912010-12-23 16:22:42 +00009495
cristy3ed852e2009-09-05 21:47:34 +00009496#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00009497 if (ping_exclude_pHYs == MagickFalse)
9498 {
cristy16ea1392012-03-21 20:38:41 +00009499 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00009500 (!mng_info->write_mng || !mng_info->equal_physs))
9501 {
glennrp0fe50b42010-11-16 03:52:51 +00009502 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009503 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9504 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00009505
9506 if (image->units == PixelsPerInchResolution)
9507 {
glennrpdfd70802010-11-14 01:23:35 +00009508 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009509 ping_pHYs_x_resolution=
cristy16ea1392012-03-21 20:38:41 +00009510 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00009511 ping_pHYs_y_resolution=
cristy16ea1392012-03-21 20:38:41 +00009512 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009513 }
glennrpdfd70802010-11-14 01:23:35 +00009514
cristy3ed852e2009-09-05 21:47:34 +00009515 else if (image->units == PixelsPerCentimeterResolution)
9516 {
glennrpdfd70802010-11-14 01:23:35 +00009517 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy16ea1392012-03-21 20:38:41 +00009518 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9519 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009520 }
glennrp991d11d2010-11-12 21:55:28 +00009521
cristy3ed852e2009-09-05 21:47:34 +00009522 else
9523 {
glennrpdfd70802010-11-14 01:23:35 +00009524 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy16ea1392012-03-21 20:38:41 +00009525 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9526 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009527 }
glennrp991d11d2010-11-12 21:55:28 +00009528
glennrp823b55c2011-03-14 18:46:46 +00009529 if (logging != MagickFalse)
9530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9531 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9532 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9533 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009534 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009535 }
glennrp26f37912010-12-23 16:22:42 +00009536 }
cristy3ed852e2009-09-05 21:47:34 +00009537#endif
glennrpa521b2f2010-10-29 04:11:03 +00009538
glennrp26f37912010-12-23 16:22:42 +00009539 if (ping_exclude_bKGD == MagickFalse)
9540 {
glennrpa521b2f2010-10-29 04:11:03 +00009541 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009542 {
glennrpa521b2f2010-10-29 04:11:03 +00009543 unsigned int
9544 mask;
cristy3ed852e2009-09-05 21:47:34 +00009545
glennrpa521b2f2010-10-29 04:11:03 +00009546 mask=0xffff;
9547 if (ping_bit_depth == 8)
9548 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009549
glennrpa521b2f2010-10-29 04:11:03 +00009550 if (ping_bit_depth == 4)
9551 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009552
glennrpa521b2f2010-10-29 04:11:03 +00009553 if (ping_bit_depth == 2)
9554 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009555
glennrpa521b2f2010-10-29 04:11:03 +00009556 if (ping_bit_depth == 1)
9557 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009558
glennrpa521b2f2010-10-29 04:11:03 +00009559 ping_background.red=(png_uint_16)
9560 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009561
glennrpa521b2f2010-10-29 04:11:03 +00009562 ping_background.green=(png_uint_16)
9563 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009564
glennrpa521b2f2010-10-29 04:11:03 +00009565 ping_background.blue=(png_uint_16)
9566 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009567
9568 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009569 }
cristy3ed852e2009-09-05 21:47:34 +00009570
glennrp0fe50b42010-11-16 03:52:51 +00009571 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009572 {
9573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9574 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9576 " background_color index is %d",
9577 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009578
9579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9580 " ping_bit_depth=%d",ping_bit_depth);
9581 }
glennrp0fe50b42010-11-16 03:52:51 +00009582
9583 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009584 }
glennrp0fe50b42010-11-16 03:52:51 +00009585
cristy3ed852e2009-09-05 21:47:34 +00009586 /*
9587 Select the color type.
9588 */
9589 matte=image_matte;
9590 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009591
glennrp1273f7b2011-02-24 03:20:30 +00009592 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009593 {
glennrpfd05d622011-02-25 04:10:33 +00009594 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009595 for reducing the sample depth from 8. */
9596
glennrp0fe50b42010-11-16 03:52:51 +00009597 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009598
glennrp8bb3a022010-12-13 20:40:04 +00009599 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009600
9601 /*
9602 Set image palette.
9603 */
9604 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9605
glennrp0fe50b42010-11-16 03:52:51 +00009606 if (logging != MagickFalse)
9607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9608 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009609 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009610
9611 for (i=0; i < (ssize_t) number_colors; i++)
9612 {
9613 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9614 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9615 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9616 if (logging != MagickFalse)
9617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009618#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009619 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009620#else
9621 " %5ld (%5d,%5d,%5d)",
9622#endif
glennrp0fe50b42010-11-16 03:52:51 +00009623 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9624
9625 }
glennrp2b013e42010-11-24 16:55:50 +00009626
glennrp8bb3a022010-12-13 20:40:04 +00009627 ping_have_PLTE=MagickTrue;
9628 image_depth=ping_bit_depth;
9629 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009630
glennrp58e01762011-01-07 15:28:54 +00009631 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009632 {
glennrp0fe50b42010-11-16 03:52:51 +00009633 /*
9634 Identify which colormap entry is transparent.
9635 */
9636 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009637 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009638
glennrp8bb3a022010-12-13 20:40:04 +00009639 for (i=0; i < (ssize_t) number_transparent; i++)
9640 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009641
glennrp0fe50b42010-11-16 03:52:51 +00009642
glennrp2cc891a2010-12-24 13:44:32 +00009643 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009644 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009645
9646 if (ping_num_trans == 0)
9647 ping_have_tRNS=MagickFalse;
9648
glennrp8bb3a022010-12-13 20:40:04 +00009649 else
9650 ping_have_tRNS=MagickTrue;
9651 }
glennrp0fe50b42010-11-16 03:52:51 +00009652
glennrp1273f7b2011-02-24 03:20:30 +00009653 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009654 {
glennrp1273f7b2011-02-24 03:20:30 +00009655 /*
9656 * Identify which colormap entry is the background color.
9657 */
9658
glennrp4f25bd02011-01-01 18:51:28 +00009659 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9660 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9661 break;
glennrp0fe50b42010-11-16 03:52:51 +00009662
glennrp4f25bd02011-01-01 18:51:28 +00009663 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009664
9665 if (logging != MagickFalse)
9666 {
9667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9668 " background_color index is %d",
9669 (int) ping_background.index);
9670 }
glennrp4f25bd02011-01-01 18:51:28 +00009671 }
cristy3ed852e2009-09-05 21:47:34 +00009672 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009673
glennrpfd164d22013-01-26 21:10:22 +00009674 else if (mng_info->write_png_colortype == 1)
9675 {
9676 image_matte=MagickFalse;
9677 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9678 }
9679
9680 else if (mng_info->write_png24 || mng_info->write_png48 ||
9681 mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009682 {
9683 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009684 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009685 }
glennrp0fe50b42010-11-16 03:52:51 +00009686
glennrpfd164d22013-01-26 21:10:22 +00009687 else if (mng_info->write_png32 || mng_info->write_png64 ||
9688 mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009689 {
9690 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009691 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009692 }
glennrp0fe50b42010-11-16 03:52:51 +00009693
glennrp8bb3a022010-12-13 20:40:04 +00009694 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009695 {
glennrp5af765f2010-03-30 11:12:18 +00009696 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009697
glennrp8bb3a022010-12-13 20:40:04 +00009698 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009699 {
glennrp5af765f2010-03-30 11:12:18 +00009700 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009701
glennrp5af765f2010-03-30 11:12:18 +00009702 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9703 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009704 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009705
glennrp8bb3a022010-12-13 20:40:04 +00009706 else
9707 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009708
9709 if (logging != MagickFalse)
9710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9711 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009712 }
glennrp0fe50b42010-11-16 03:52:51 +00009713
glennrp7c4c9e62011-03-21 20:23:32 +00009714 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009715 {
9716 if (logging != MagickFalse)
9717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009718 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009719
glennrpd6bf1612010-12-17 17:28:54 +00009720 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009721 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009722
glennrpd6bf1612010-12-17 17:28:54 +00009723 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009724 {
glennrp5af765f2010-03-30 11:12:18 +00009725 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009726 image_matte=MagickFalse;
9727 }
glennrp0fe50b42010-11-16 03:52:51 +00009728
cristydef23e52015-01-22 11:52:01 +00009729 if (image_info->type == TrueColorAlphaType)
cristy3ed852e2009-09-05 21:47:34 +00009730 {
glennrp5af765f2010-03-30 11:12:18 +00009731 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009732 image_matte=MagickTrue;
9733 }
glennrp0fe50b42010-11-16 03:52:51 +00009734
glennrp5aa37f62011-01-02 03:07:57 +00009735 if (image_info->type == PaletteType ||
cristydef23e52015-01-22 11:52:01 +00009736 image_info->type == PaletteAlphaType)
glennrp5aa37f62011-01-02 03:07:57 +00009737 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9738
glennrp7c4c9e62011-03-21 20:23:32 +00009739 if (mng_info->write_png_colortype == 0 &&
glennrp261f64e2014-08-09 15:44:51 +00009740 image_info->type == UndefinedType)
cristy3ed852e2009-09-05 21:47:34 +00009741 {
glennrp5aa37f62011-01-02 03:07:57 +00009742 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009743 {
glennrp5aa37f62011-01-02 03:07:57 +00009744 if (image_matte == MagickFalse)
9745 {
9746 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9747 image_matte=MagickFalse;
9748 }
glennrp0fe50b42010-11-16 03:52:51 +00009749
glennrp0b206f52011-01-07 04:55:32 +00009750 else
glennrp5aa37f62011-01-02 03:07:57 +00009751 {
9752 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9753 image_matte=MagickTrue;
9754 }
9755 }
9756 else
glennrp8bb3a022010-12-13 20:40:04 +00009757 {
glennrp5aa37f62011-01-02 03:07:57 +00009758 if (image_matte == MagickFalse)
9759 {
9760 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9761 image_matte=MagickFalse;
9762 }
glennrp8bb3a022010-12-13 20:40:04 +00009763
glennrp0b206f52011-01-07 04:55:32 +00009764 else
glennrp5aa37f62011-01-02 03:07:57 +00009765 {
9766 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9767 image_matte=MagickTrue;
9768 }
9769 }
glennrp0fe50b42010-11-16 03:52:51 +00009770 }
glennrp5aa37f62011-01-02 03:07:57 +00009771
cristy3ed852e2009-09-05 21:47:34 +00009772 }
glennrp0fe50b42010-11-16 03:52:51 +00009773
cristy3ed852e2009-09-05 21:47:34 +00009774 if (logging != MagickFalse)
9775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009776 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009777
glennrp5af765f2010-03-30 11:12:18 +00009778 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009779 {
9780 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9781 ping_color_type == PNG_COLOR_TYPE_RGB ||
9782 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9783 ping_bit_depth=8;
9784 }
cristy3ed852e2009-09-05 21:47:34 +00009785
glennrpd6bf1612010-12-17 17:28:54 +00009786 old_bit_depth=ping_bit_depth;
9787
glennrp5af765f2010-03-30 11:12:18 +00009788 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009789 {
cristy17f11b02014-12-20 19:37:04 +00009790 if (image->alpha_trait == UndefinedPixelTrait && ping_have_non_bw == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00009791 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009792 }
glennrp8640fb52010-11-23 15:48:26 +00009793
glennrp5af765f2010-03-30 11:12:18 +00009794 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009795 {
cristy35ef8242010-06-03 16:24:13 +00009796 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009797 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009798
9799 if (image->colors == 0)
9800 {
glennrp0fe50b42010-11-16 03:52:51 +00009801 /* DO SOMETHING */
glennrpedaa0382012-04-12 14:16:21 +00009802 png_error(ping,"image has 0 colors");
glennrp0f111982010-07-07 20:18:33 +00009803 }
9804
cristy35ef8242010-06-03 16:24:13 +00009805 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009806 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009807 }
glennrp2b013e42010-11-24 16:55:50 +00009808
glennrpd6bf1612010-12-17 17:28:54 +00009809 if (logging != MagickFalse)
9810 {
9811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9812 " Number of colors: %.20g",(double) image_colors);
9813
9814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9815 " Tentative PNG bit depth: %d",ping_bit_depth);
9816 }
9817
9818 if (ping_bit_depth < (int) mng_info->write_png_depth)
9819 ping_bit_depth = mng_info->write_png_depth;
9820 }
glennrp2cc891a2010-12-24 13:44:32 +00009821
glennrp5af765f2010-03-30 11:12:18 +00009822 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009823
cristy3ed852e2009-09-05 21:47:34 +00009824 if (logging != MagickFalse)
9825 {
9826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +00009827 " Tentative PNG color type: %s (%.20g)",
9828 PngColorTypeToString(ping_color_type),
9829 (double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009830
cristy3ed852e2009-09-05 21:47:34 +00009831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009832 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009833
cristy3ed852e2009-09-05 21:47:34 +00009834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009835 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009836
cristy3ed852e2009-09-05 21:47:34 +00009837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009838
glennrp8640fb52010-11-23 15:48:26 +00009839 " image->depth: %.20g",(double) image->depth);
9840
9841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009842 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009843 }
9844
glennrp58e01762011-01-07 15:28:54 +00009845 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009846 {
glennrp4f25bd02011-01-01 18:51:28 +00009847 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009848 {
glennrp7c4c9e62011-03-21 20:23:32 +00009849 if (mng_info->write_png_colortype == 0)
9850 {
9851 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009852
glennrp7c4c9e62011-03-21 20:23:32 +00009853 if (ping_have_color != MagickFalse)
9854 ping_color_type=PNG_COLOR_TYPE_RGBA;
9855 }
glennrp4f25bd02011-01-01 18:51:28 +00009856
9857 /*
9858 * Determine if there is any transparent color.
9859 */
9860 if (number_transparent + number_semitransparent == 0)
9861 {
9862 /*
9863 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9864 */
glennrpa6a06632011-01-19 15:15:34 +00009865
glennrp4f25bd02011-01-01 18:51:28 +00009866 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009867
9868 if (mng_info->write_png_colortype == 0)
9869 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009870 }
9871
9872 else
9873 {
9874 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009875 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009876
9877 mask=0xffff;
9878
9879 if (ping_bit_depth == 8)
9880 mask=0x00ff;
9881
9882 if (ping_bit_depth == 4)
9883 mask=0x000f;
9884
9885 if (ping_bit_depth == 2)
9886 mask=0x0003;
9887
9888 if (ping_bit_depth == 1)
9889 mask=0x0001;
9890
9891 ping_trans_color.red=(png_uint_16)
9892 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9893
9894 ping_trans_color.green=(png_uint_16)
9895 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9896
9897 ping_trans_color.blue=(png_uint_16)
9898 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9899
9900 ping_trans_color.gray=(png_uint_16)
cristy11a06d32015-01-04 12:03:27 +00009901 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
glennrp4f25bd02011-01-01 18:51:28 +00009902 image->colormap)) & mask);
9903
9904 ping_trans_color.index=(png_byte) 0;
9905
9906 ping_have_tRNS=MagickTrue;
9907 }
9908
9909 if (ping_have_tRNS != MagickFalse)
9910 {
9911 /*
glennrpfd05d622011-02-25 04:10:33 +00009912 * Determine if there is one and only one transparent color
9913 * and if so if it is fully transparent.
9914 */
9915 if (ping_have_cheap_transparency == MagickFalse)
9916 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009917 }
9918
9919 if (ping_have_tRNS != MagickFalse)
9920 {
glennrp7c4c9e62011-03-21 20:23:32 +00009921 if (mng_info->write_png_colortype == 0)
9922 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009923
9924 if (image_depth == 8)
9925 {
9926 ping_trans_color.red&=0xff;
9927 ping_trans_color.green&=0xff;
9928 ping_trans_color.blue&=0xff;
9929 ping_trans_color.gray&=0xff;
9930 }
9931 }
9932 }
cristy3ed852e2009-09-05 21:47:34 +00009933 else
9934 {
cristy3ed852e2009-09-05 21:47:34 +00009935 if (image_depth == 8)
9936 {
glennrp5af765f2010-03-30 11:12:18 +00009937 ping_trans_color.red&=0xff;
9938 ping_trans_color.green&=0xff;
9939 ping_trans_color.blue&=0xff;
9940 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009941 }
9942 }
9943 }
glennrp8640fb52010-11-23 15:48:26 +00009944
cristy3ed852e2009-09-05 21:47:34 +00009945 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009946
glennrp2e09f552010-11-14 00:38:48 +00009947 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009948 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009949
glennrp39992b42010-11-14 00:03:43 +00009950 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009951 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009952 ping_have_color == MagickFalse &&
9953 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009954 {
cristy35ef8242010-06-03 16:24:13 +00009955 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009956
cristy3ed852e2009-09-05 21:47:34 +00009957 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009958 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009959
glennrp7c4c9e62011-03-21 20:23:32 +00009960 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009961 {
glennrp5af765f2010-03-30 11:12:18 +00009962 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009963
cristy3ed852e2009-09-05 21:47:34 +00009964 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009965 {
9966 if (logging != MagickFalse)
9967 {
9968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9969 " Scaling ping_trans_color (0)");
9970 }
9971 ping_trans_color.gray*=0x0101;
9972 }
cristy3ed852e2009-09-05 21:47:34 +00009973 }
glennrp0fe50b42010-11-16 03:52:51 +00009974
cristy3ed852e2009-09-05 21:47:34 +00009975 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9976 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009977
glennrp136ee3a2011-04-27 15:47:45 +00009978 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009979 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009980 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009981
cristy3ed852e2009-09-05 21:47:34 +00009982 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009983 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009984
cristy3ed852e2009-09-05 21:47:34 +00009985 else
9986 {
glennrp5af765f2010-03-30 11:12:18 +00009987 ping_bit_depth=8;
9988 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009989 {
9990 if(!mng_info->write_png_depth)
9991 {
glennrp5af765f2010-03-30 11:12:18 +00009992 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009993
cristy35ef8242010-06-03 16:24:13 +00009994 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009995 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009996 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009997 }
9998 }
glennrp2b013e42010-11-24 16:55:50 +00009999
glennrp0fe50b42010-11-16 03:52:51 +000010000 else if (ping_color_type ==
10001 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +000010002 mng_info->IsPalette)
10003 {
cristy3ed852e2009-09-05 21:47:34 +000010004 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +000010005
cristy3ed852e2009-09-05 21:47:34 +000010006 int
10007 depth_4_ok=MagickTrue,
10008 depth_2_ok=MagickTrue,
10009 depth_1_ok=MagickTrue;
10010
cristybb503372010-05-27 20:51:26 +000010011 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000010012 {
10013 unsigned char
10014 intensity;
10015
10016 intensity=ScaleQuantumToChar(image->colormap[i].red);
10017
10018 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10019 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10020 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10021 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +000010022 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +000010023 depth_1_ok=MagickFalse;
10024 }
glennrp2b013e42010-11-24 16:55:50 +000010025
cristy3ed852e2009-09-05 21:47:34 +000010026 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +000010027 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +000010028
cristy3ed852e2009-09-05 21:47:34 +000010029 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +000010030 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +000010031
cristy3ed852e2009-09-05 21:47:34 +000010032 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +000010033 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +000010034 }
10035 }
glennrp2b013e42010-11-24 16:55:50 +000010036
glennrp5af765f2010-03-30 11:12:18 +000010037 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +000010038 }
glennrp0fe50b42010-11-16 03:52:51 +000010039
cristy3ed852e2009-09-05 21:47:34 +000010040 else
glennrp0fe50b42010-11-16 03:52:51 +000010041
cristy3ed852e2009-09-05 21:47:34 +000010042 if (mng_info->IsPalette)
10043 {
glennrp17a14852010-05-10 03:01:59 +000010044 number_colors=image_colors;
10045
cristy3ed852e2009-09-05 21:47:34 +000010046 if (image_depth <= 8)
10047 {
cristy3ed852e2009-09-05 21:47:34 +000010048 /*
10049 Set image palette.
10050 */
glennrp5af765f2010-03-30 11:12:18 +000010051 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +000010052
glennrp3d627862013-02-26 00:19:34 +000010053 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +000010054 {
cristybb503372010-05-27 20:51:26 +000010055 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000010056 {
10057 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10058 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10059 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10060 }
glennrp0fe50b42010-11-16 03:52:51 +000010061
glennrp3b51f0e2010-11-27 18:14:08 +000010062 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +000010064 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +000010065 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010066
glennrp39992b42010-11-14 00:03:43 +000010067 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010068 }
glennrp0fe50b42010-11-16 03:52:51 +000010069
cristy3ed852e2009-09-05 21:47:34 +000010070 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +000010071 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +000010072 {
cristybefe4d22010-06-07 01:18:58 +000010073 size_t
10074 one;
10075
glennrp5af765f2010-03-30 11:12:18 +000010076 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +000010077 one=1;
glennrp0fe50b42010-11-16 03:52:51 +000010078
cristy16ea1392012-03-21 20:38:41 +000010079 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +000010080 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +000010081 }
glennrp0fe50b42010-11-16 03:52:51 +000010082
glennrp5af765f2010-03-30 11:12:18 +000010083 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +000010084
glennrp58e01762011-01-07 15:28:54 +000010085 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010086 {
glennrp0fe50b42010-11-16 03:52:51 +000010087 /*
glennrpd6bf1612010-12-17 17:28:54 +000010088 * Set up trans_colors array.
10089 */
glennrp0fe50b42010-11-16 03:52:51 +000010090 assert(number_colors <= 256);
10091
glennrpd6bf1612010-12-17 17:28:54 +000010092 ping_num_trans=(unsigned short) (number_transparent +
10093 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +000010094
10095 if (ping_num_trans == 0)
10096 ping_have_tRNS=MagickFalse;
10097
glennrpd6bf1612010-12-17 17:28:54 +000010098 else
glennrp0fe50b42010-11-16 03:52:51 +000010099 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010100 if (logging != MagickFalse)
10101 {
10102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10103 " Scaling ping_trans_color (1)");
10104 }
glennrpd6bf1612010-12-17 17:28:54 +000010105 ping_have_tRNS=MagickTrue;
10106
10107 for (i=0; i < ping_num_trans; i++)
10108 {
glennrp750105b2012-04-25 16:20:45 +000010109 ping_trans_alpha[i]= (png_byte)
cristy16ea1392012-03-21 20:38:41 +000010110 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +000010111 }
glennrp0fe50b42010-11-16 03:52:51 +000010112 }
10113 }
cristy3ed852e2009-09-05 21:47:34 +000010114 }
10115 }
glennrp0fe50b42010-11-16 03:52:51 +000010116
cristy3ed852e2009-09-05 21:47:34 +000010117 else
10118 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010119
cristy3ed852e2009-09-05 21:47:34 +000010120 if (image_depth < 8)
10121 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010122
cristy3ed852e2009-09-05 21:47:34 +000010123 if ((save_image_depth == 16) && (image_depth == 8))
10124 {
glennrp4f25bd02011-01-01 18:51:28 +000010125 if (logging != MagickFalse)
10126 {
10127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10128 " Scaling ping_trans_color from (%d,%d,%d)",
10129 (int) ping_trans_color.red,
10130 (int) ping_trans_color.green,
10131 (int) ping_trans_color.blue);
10132 }
10133
glennrp5af765f2010-03-30 11:12:18 +000010134 ping_trans_color.red*=0x0101;
10135 ping_trans_color.green*=0x0101;
10136 ping_trans_color.blue*=0x0101;
10137 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +000010138
10139 if (logging != MagickFalse)
10140 {
10141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10142 " to (%d,%d,%d)",
10143 (int) ping_trans_color.red,
10144 (int) ping_trans_color.green,
10145 (int) ping_trans_color.blue);
10146 }
cristy3ed852e2009-09-05 21:47:34 +000010147 }
10148 }
10149
cristy4383ec82011-01-05 15:42:32 +000010150 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10151 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +000010152
cristy3ed852e2009-09-05 21:47:34 +000010153 /*
10154 Adjust background and transparency samples in sub-8-bit grayscale files.
10155 */
glennrp5af765f2010-03-30 11:12:18 +000010156 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +000010157 PNG_COLOR_TYPE_GRAY)
10158 {
10159 png_uint_16
10160 maxval;
10161
cristy35ef8242010-06-03 16:24:13 +000010162 size_t
10163 one=1;
10164
cristy22ffd972010-06-03 16:51:47 +000010165 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +000010166
glennrp4f25bd02011-01-01 18:51:28 +000010167 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010168 {
cristy3ed852e2009-09-05 21:47:34 +000010169
cristy16ea1392012-03-21 20:38:41 +000010170 ping_background.gray=(png_uint_16) ((maxval/65535.)*
cristy11a06d32015-01-04 12:03:27 +000010171 (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
cristy16ea1392012-03-21 20:38:41 +000010172 &image->background_color))) +.5)));
10173
cristy3ed852e2009-09-05 21:47:34 +000010174 if (logging != MagickFalse)
10175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +000010176 " Setting up bKGD chunk (2)");
10177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10178 " background_color index is %d",
10179 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +000010180
glennrp991d11d2010-11-12 21:55:28 +000010181 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010182 }
cristy3ed852e2009-09-05 21:47:34 +000010183
glennrp3e3e20f2011-06-09 04:21:43 +000010184 if (logging != MagickFalse)
10185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10186 " Scaling ping_trans_color.gray from %d",
10187 (int)ping_trans_color.gray);
10188
glennrp9be9b1c2011-06-09 12:21:45 +000010189 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +000010190 ping_trans_color.gray)+.5);
10191
10192 if (logging != MagickFalse)
10193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10194 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +000010195 }
glennrp17a14852010-05-10 03:01:59 +000010196
glennrp26f37912010-12-23 16:22:42 +000010197 if (ping_exclude_bKGD == MagickFalse)
10198 {
glennrp1273f7b2011-02-24 03:20:30 +000010199 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +000010200 {
10201 /*
10202 Identify which colormap entry is the background color.
10203 */
10204
glennrp17a14852010-05-10 03:01:59 +000010205 number_colors=image_colors;
10206
glennrpa521b2f2010-10-29 04:11:03 +000010207 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10208 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +000010209 break;
10210
10211 ping_background.index=(png_byte) i;
10212
glennrp3b51f0e2010-11-27 18:14:08 +000010213 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +000010214 {
10215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +000010216 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +000010217 }
glennrp0fe50b42010-11-16 03:52:51 +000010218
cristy13d07042010-11-21 20:56:18 +000010219 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +000010220 {
10221 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +000010222
10223 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010224 {
10225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10226 " background =(%d,%d,%d)",
10227 (int) ping_background.red,
10228 (int) ping_background.green,
10229 (int) ping_background.blue);
10230 }
10231 }
glennrpa521b2f2010-10-29 04:11:03 +000010232
glennrpd6bf1612010-12-17 17:28:54 +000010233 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +000010234 {
glennrp3b51f0e2010-11-27 18:14:08 +000010235 if (logging != MagickFalse)
10236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10237 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +000010238 ping_have_bKGD = MagickFalse;
10239 }
glennrp17a14852010-05-10 03:01:59 +000010240 }
glennrp26f37912010-12-23 16:22:42 +000010241 }
glennrp17a14852010-05-10 03:01:59 +000010242
cristy3ed852e2009-09-05 21:47:34 +000010243 if (logging != MagickFalse)
10244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +000010245 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10246 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010247 /*
10248 Initialize compression level and filtering.
10249 */
10250 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010251 {
10252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10253 " Setting up deflate compression");
10254
10255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10256 " Compression buffer size: 32768");
10257 }
10258
cristy3ed852e2009-09-05 21:47:34 +000010259 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +000010260
cristy3ed852e2009-09-05 21:47:34 +000010261 if (logging != MagickFalse)
10262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10263 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +000010264
cristy4054bfb2011-08-29 23:41:39 +000010265 png_set_compression_mem_level(ping, 9);
10266
glennrp10d739e2011-06-29 18:00:52 +000010267 /* Untangle the "-quality" setting:
10268
10269 Undefined is 0; the default is used.
10270 Default is 75
10271
10272 10's digit:
10273
glennrpef804f52013-09-24 00:38:27 +000010274 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
glennrp10d739e2011-06-29 18:00:52 +000010275 zlib default compression level
10276
10277 1-9: the zlib compression level
10278
10279 1's digit:
10280
10281 0-4: the PNG filter method
10282
10283 5: libpng adaptive filtering if compression level > 5
10284 libpng filter type "none" if compression level <= 5
10285 or if image is grayscale or palette
glennrp750105b2012-04-25 16:20:45 +000010286
glennrp10d739e2011-06-29 18:00:52 +000010287 6: libpng adaptive filtering
10288
10289 7: "LOCO" filtering (intrapixel differing) if writing
glennrp85dfe1a2013-09-24 17:30:39 +000010290 a MNG, otherwise "none". Did not work in IM-6.7.0-9
glennrp10d739e2011-06-29 18:00:52 +000010291 and earlier because of a missing "else".
10292
glennrp85dfe1a2013-09-24 17:30:39 +000010293 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10294 filtering. Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +000010295
glennrpef804f52013-09-24 00:38:27 +000010296 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
glennrp18682582011-06-30 18:11:47 +000010297 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +000010298
10299 Note that using the -quality option, not all combinations of
10300 PNG filter type, zlib compression level, and zlib compression
cristy16ea1392012-03-21 20:38:41 +000010301 strategy are possible. This will be addressed soon in a
10302 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +000010303
10304 */
10305
dirk29dd80e2013-10-31 23:11:11 +000010306 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10307 image_info->quality;
glennrp0fe50b42010-11-16 03:52:51 +000010308
glennrp18682582011-06-30 18:11:47 +000010309 if (quality <= 9)
10310 {
10311 if (mng_info->write_png_compression_strategy == 0)
10312 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10313 }
glennrp750105b2012-04-25 16:20:45 +000010314
glennrp18682582011-06-30 18:11:47 +000010315 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +000010316 {
10317 int
10318 level;
10319
cristybb503372010-05-27 20:51:26 +000010320 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +000010321
glennrp18682582011-06-30 18:11:47 +000010322 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +000010323 }
glennrp0fe50b42010-11-16 03:52:51 +000010324
glennrp18682582011-06-30 18:11:47 +000010325 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +000010326 {
glennrp18682582011-06-30 18:11:47 +000010327 if ((quality %10) == 8 || (quality %10) == 9)
glennrpa24b2452012-06-27 11:38:38 +000010328#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10329 mng_info->write_png_compression_strategy=Z_RLE+1;
10330#else
10331 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10332#endif
cristy3ed852e2009-09-05 21:47:34 +000010333 }
glennrp0fe50b42010-11-16 03:52:51 +000010334
glennrp18682582011-06-30 18:11:47 +000010335 if (mng_info->write_png_compression_filter == 0)
10336 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10337
cristy3ed852e2009-09-05 21:47:34 +000010338 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010339 {
glennrp18682582011-06-30 18:11:47 +000010340 if (mng_info->write_png_compression_level)
10341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10342 " Compression level: %d",
10343 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +000010344
glennrp18682582011-06-30 18:11:47 +000010345 if (mng_info->write_png_compression_strategy)
10346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10347 " Compression strategy: %d",
10348 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +000010349
glennrp18682582011-06-30 18:11:47 +000010350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10351 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +000010352
cristy4054bfb2011-08-29 23:41:39 +000010353 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +000010354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10355 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +000010356 else if (mng_info->write_png_compression_filter == 0 ||
10357 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +000010358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10359 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +000010360 else
10361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10362 " Base filter method: %d",
10363 (int) mng_info->write_png_compression_filter-1);
10364 }
glennrp2b013e42010-11-24 16:55:50 +000010365
glennrp18682582011-06-30 18:11:47 +000010366 if (mng_info->write_png_compression_level != 0)
10367 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10368
10369 if (mng_info->write_png_compression_filter == 6)
10370 {
10371 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10372 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10373 (quality < 50))
10374 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10375 else
10376 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10377 }
cristy4054bfb2011-08-29 23:41:39 +000010378 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +000010379 mng_info->write_png_compression_filter == 10)
10380 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10381
10382 else if (mng_info->write_png_compression_filter == 8)
10383 {
10384#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10385 if (mng_info->write_mng)
10386 {
10387 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10388 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10389 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10390 }
10391#endif
cristy4054bfb2011-08-29 23:41:39 +000010392 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +000010393 }
10394
10395 else if (mng_info->write_png_compression_filter == 9)
10396 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10397
10398 else if (mng_info->write_png_compression_filter != 0)
10399 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10400 mng_info->write_png_compression_filter-1);
10401
10402 if (mng_info->write_png_compression_strategy != 0)
10403 png_set_compression_strategy(ping,
10404 mng_info->write_png_compression_strategy-1);
10405
glennrpdec72c92013-02-26 17:42:47 +000010406 ping_interlace_method=image_info->interlace != NoInterlace;
10407
10408 if (mng_info->write_mng)
10409 png_set_sig_bytes(ping,8);
10410
10411 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10412
10413 if (mng_info->write_png_colortype != 0)
10414 {
10415 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10416 if (ping_have_color != MagickFalse)
10417 {
10418 ping_color_type = PNG_COLOR_TYPE_RGB;
10419
10420 if (ping_bit_depth < 8)
10421 ping_bit_depth=8;
10422 }
10423
10424 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10425 if (ping_have_color != MagickFalse)
10426 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10427 }
10428
10429 if (ping_need_colortype_warning != MagickFalse ||
10430 ((mng_info->write_png_depth &&
10431 (int) mng_info->write_png_depth != ping_bit_depth) ||
10432 (mng_info->write_png_colortype &&
10433 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10434 mng_info->write_png_colortype != 7 &&
10435 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10436 {
10437 if (logging != MagickFalse)
10438 {
10439 if (ping_need_colortype_warning != MagickFalse)
10440 {
10441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10442 " Image has transparency but tRNS chunk was excluded");
10443 }
10444
10445 if (mng_info->write_png_depth)
10446 {
10447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10448 " Defined png:bit-depth=%u, Computed depth=%u",
10449 mng_info->write_png_depth,
10450 ping_bit_depth);
10451 }
10452
10453 if (mng_info->write_png_colortype)
10454 {
10455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10456 " Defined png:color-type=%u, Computed color type=%u",
10457 mng_info->write_png_colortype-1,
10458 ping_color_type);
10459 }
10460 }
10461
10462 png_warning(ping,
10463 "Cannot write image with defined png:bit-depth or png:color-type.");
10464 }
10465
cristy17f11b02014-12-20 19:37:04 +000010466 if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
glennrpdec72c92013-02-26 17:42:47 +000010467 {
10468 /* Add an opaque matte channel */
10469 image->alpha_trait = BlendPixelTrait;
10470 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10471
10472 if (logging != MagickFalse)
10473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10474 " Added an opaque matte channel");
10475 }
10476
10477 if (number_transparent != 0 || number_semitransparent != 0)
10478 {
10479 if (ping_color_type < 4)
10480 {
10481 ping_have_tRNS=MagickTrue;
10482 if (logging != MagickFalse)
10483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10484 " Setting ping_have_tRNS=MagickTrue.");
10485 }
10486 }
10487
10488 if (logging != MagickFalse)
10489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10490 " Writing PNG header chunks");
10491
10492 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10493 ping_bit_depth,ping_color_type,
10494 ping_interlace_method,ping_compression_method,
10495 ping_filter_method);
10496
10497 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10498 {
10499 png_set_PLTE(ping,ping_info,palette,number_colors);
10500
10501 if (logging != MagickFalse)
10502 {
10503 for (i=0; i< (ssize_t) number_colors; i++)
10504 {
10505 if (i < ping_num_trans)
10506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10507 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10508 (int) i,
10509 (int) palette[i].red,
10510 (int) palette[i].green,
10511 (int) palette[i].blue,
10512 (int) i,
10513 (int) ping_trans_alpha[i]);
10514 else
10515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10516 " PLTE[%d] = (%d,%d,%d)",
10517 (int) i,
10518 (int) palette[i].red,
10519 (int) palette[i].green,
10520 (int) palette[i].blue);
10521 }
10522 }
10523 }
10524
cristy0d57eec2011-09-04 22:13:56 +000010525 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10526 if (ping_exclude_sRGB != MagickFalse ||
glennrp3d627862013-02-26 00:19:34 +000010527 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy0d57eec2011-09-04 22:13:56 +000010528 {
10529 if ((ping_exclude_tEXt == MagickFalse ||
10530 ping_exclude_zTXt == MagickFalse) &&
10531 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +000010532 {
10533 ResetImageProfileIterator(image);
10534 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +000010535 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010536 profile=GetImageProfile(image,name);
10537
10538 if (profile != (StringInfo *) NULL)
10539 {
glennrp5af765f2010-03-30 11:12:18 +000010540#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +000010541 if ((LocaleCompare(name,"ICC") == 0) ||
10542 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +000010543 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010544
10545 if (ping_exclude_iCCP == MagickFalse)
10546 {
glennrpecab7d72013-05-14 22:50:32 +000010547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10548 " Setting up iCCP chunk");
glennrp6647b972015-01-12 04:39:14 +000010549
cristy16ea1392012-03-21 20:38:41 +000010550 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +000010551#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +000010552 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +000010553#else
Cristy120b5812015-09-14 17:33:11 -040010554 (const png_byte *) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +000010555#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010556 (png_uint_32) GetStringInfoLength(profile));
glennrp918b9dc2013-04-03 13:41:41 +000010557 ping_have_iCCP = MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010558 }
glennrp26f37912010-12-23 16:22:42 +000010559 }
glennrp0fe50b42010-11-16 03:52:51 +000010560
glennrpc8cbc5d2011-01-01 00:12:34 +000010561 else
cristy3ed852e2009-09-05 21:47:34 +000010562#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010563 if (ping_exclude_zCCP == MagickFalse)
10564 {
glennrpecab7d72013-05-14 22:50:32 +000010565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10566 " Setting up zTXT chunk with uuencoded ICC");
glennrpcf002022011-01-30 02:38:15 +000010567 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +000010568 (unsigned char *) name,(unsigned char *) name,
10569 GetStringInfoDatum(profile),
10570 (png_uint_32) GetStringInfoLength(profile));
glennrpecab7d72013-05-14 22:50:32 +000010571 ping_have_iCCP = MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010572 }
10573 }
glennrp0b206f52011-01-07 04:55:32 +000010574
glennrpc8cbc5d2011-01-01 00:12:34 +000010575 if (logging != MagickFalse)
10576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10577 " Setting up text chunk with %s profile",name);
10578
10579 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +000010580 }
cristy0d57eec2011-09-04 22:13:56 +000010581 }
cristy3ed852e2009-09-05 21:47:34 +000010582 }
10583
10584#if defined(PNG_WRITE_sRGB_SUPPORTED)
10585 if ((mng_info->have_write_global_srgb == 0) &&
glennrpecab7d72013-05-14 22:50:32 +000010586 ping_have_iCCP != MagickTrue &&
10587 (ping_have_sRGB != MagickFalse ||
10588 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +000010589 {
glennrp26f37912010-12-23 16:22:42 +000010590 if (ping_exclude_sRGB == MagickFalse)
10591 {
10592 /*
10593 Note image rendering intent.
10594 */
10595 if (logging != MagickFalse)
10596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10597 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +000010598
glennrp26f37912010-12-23 16:22:42 +000010599 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +000010600 Magick_RenderingIntent_to_PNG_RenderingIntent(
10601 image->rendering_intent)));
glennrp918b9dc2013-04-03 13:41:41 +000010602
10603 ping_have_sRGB = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010604 }
cristy3ed852e2009-09-05 21:47:34 +000010605 }
glennrp26f37912010-12-23 16:22:42 +000010606
glennrp5af765f2010-03-30 11:12:18 +000010607 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +000010608#endif
10609 {
glennrp2cc891a2010-12-24 13:44:32 +000010610 if (ping_exclude_gAMA == MagickFalse &&
glennrp918b9dc2013-04-03 13:41:41 +000010611 ping_have_iCCP == MagickFalse &&
10612 ping_have_sRGB == MagickFalse &&
glennrp2cc891a2010-12-24 13:44:32 +000010613 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +000010614 (image->gamma < .45 || image->gamma > .46)))
10615 {
cristy3ed852e2009-09-05 21:47:34 +000010616 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10617 {
10618 /*
10619 Note image gamma.
10620 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10621 */
10622 if (logging != MagickFalse)
10623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10624 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010625
cristy3ed852e2009-09-05 21:47:34 +000010626 png_set_gAMA(ping,ping_info,image->gamma);
10627 }
glennrp26f37912010-12-23 16:22:42 +000010628 }
glennrp2b013e42010-11-24 16:55:50 +000010629
glennrp918b9dc2013-04-03 13:41:41 +000010630 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010631 {
glennrp26f37912010-12-23 16:22:42 +000010632 if ((mng_info->have_write_global_chrm == 0) &&
10633 (image->chromaticity.red_primary.x != 0.0))
10634 {
10635 /*
10636 Note image chromaticity.
glennrp918b9dc2013-04-03 13:41:41 +000010637 Note: if cHRM+gAMA == sRGB write sRGB instead.
glennrp26f37912010-12-23 16:22:42 +000010638 */
10639 PrimaryInfo
10640 bp,
10641 gp,
10642 rp,
10643 wp;
cristy3ed852e2009-09-05 21:47:34 +000010644
glennrp26f37912010-12-23 16:22:42 +000010645 wp=image->chromaticity.white_point;
10646 rp=image->chromaticity.red_primary;
10647 gp=image->chromaticity.green_primary;
10648 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010649
glennrp26f37912010-12-23 16:22:42 +000010650 if (logging != MagickFalse)
10651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10652 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010653
glennrp26f37912010-12-23 16:22:42 +000010654 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10655 bp.x,bp.y);
10656 }
10657 }
cristy3ed852e2009-09-05 21:47:34 +000010658 }
glennrpdfd70802010-11-14 01:23:35 +000010659
glennrp26f37912010-12-23 16:22:42 +000010660 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010661 {
glennrp26f37912010-12-23 16:22:42 +000010662 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010663 {
glennrp26f37912010-12-23 16:22:42 +000010664 png_set_bKGD(ping,ping_info,&ping_background);
glennrp8fe91592014-10-25 13:57:25 +000010665 if (logging != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010666 {
10667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10668 " Setting up bKGD chunk");
10669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10670 " background color = (%d,%d,%d)",
10671 (int) ping_background.red,
10672 (int) ping_background.green,
10673 (int) ping_background.blue);
10674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10675 " index = %d, gray=%d",
10676 (int) ping_background.index,
10677 (int) ping_background.gray);
10678 }
10679 }
glennrp26f37912010-12-23 16:22:42 +000010680 }
10681
10682 if (ping_exclude_pHYs == MagickFalse)
10683 {
10684 if (ping_have_pHYs != MagickFalse)
10685 {
10686 png_set_pHYs(ping,ping_info,
10687 ping_pHYs_x_resolution,
10688 ping_pHYs_y_resolution,
10689 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010690
glennrp8fe91592014-10-25 13:57:25 +000010691 if (logging != MagickFalse)
glennrp823b55c2011-03-14 18:46:46 +000010692 {
10693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10694 " Setting up pHYs chunk");
10695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10696 " x_resolution=%lu",
10697 (unsigned long) ping_pHYs_x_resolution);
10698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10699 " y_resolution=%lu",
10700 (unsigned long) ping_pHYs_y_resolution);
10701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10702 " unit_type=%lu",
10703 (unsigned long) ping_pHYs_unit_type);
10704 }
glennrp26f37912010-12-23 16:22:42 +000010705 }
glennrpdfd70802010-11-14 01:23:35 +000010706 }
10707
10708#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010709 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010710 {
glennrp26f37912010-12-23 16:22:42 +000010711 if (image->page.x || image->page.y)
10712 {
10713 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10714 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010715
glennrp26f37912010-12-23 16:22:42 +000010716 if (logging != MagickFalse)
10717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10718 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10719 (int) image->page.x, (int) image->page.y);
10720 }
glennrpdfd70802010-11-14 01:23:35 +000010721 }
10722#endif
10723
dirkfd6fd072014-10-24 21:10:08 +000010724#if defined(PNG_tIME_SUPPORTED)
10725 if (ping_exclude_tIME == MagickFalse)
10726 {
10727 const char
10728 *timestamp;
10729
glennrp39d99e12014-10-26 20:57:13 +000010730 if (image->taint == MagickFalse)
10731 {
10732 timestamp=GetImageOption(image_info,"png:tIME");
10733
10734 if (timestamp == (const char *) NULL)
10735 timestamp=GetImageProperty(image,"png:tIME",exception);
10736 }
10737
dirkfd6fd072014-10-24 21:10:08 +000010738 else
10739 {
glennrp39d99e12014-10-26 20:57:13 +000010740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10741 " Reset tIME in tainted image");
10742
10743 timestamp=GetImageProperty(image,"date:modify",exception);
dirkfd6fd072014-10-24 21:10:08 +000010744 }
glennrp39d99e12014-10-26 20:57:13 +000010745
10746 if (timestamp != (const char *) NULL)
10747 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
dirkfd6fd072014-10-24 21:10:08 +000010748 }
10749#endif
glennrp6647b972015-01-12 04:39:14 +000010750
glennrpda8f3a72011-02-27 23:54:12 +000010751 if (mng_info->need_blob != MagickFalse)
10752 {
cristy16ea1392012-03-21 20:38:41 +000010753 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010754 MagickFalse)
10755 png_error(ping,"WriteBlob Failed");
10756
10757 ping_have_blob=MagickTrue;
10758 }
10759
cristy3ed852e2009-09-05 21:47:34 +000010760 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010761
glennrp39992b42010-11-14 00:03:43 +000010762 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010763 {
glennrp3b51f0e2010-11-27 18:14:08 +000010764 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010765 {
10766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10767 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10768 }
10769
10770 if (ping_color_type == 3)
10771 (void) png_set_tRNS(ping, ping_info,
10772 ping_trans_alpha,
10773 ping_num_trans,
10774 NULL);
10775
10776 else
10777 {
10778 (void) png_set_tRNS(ping, ping_info,
10779 NULL,
10780 0,
10781 &ping_trans_color);
10782
glennrp3b51f0e2010-11-27 18:14:08 +000010783 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010784 {
10785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010786 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010787 (int) ping_trans_color.red,
10788 (int) ping_trans_color.green,
10789 (int) ping_trans_color.blue);
10790 }
10791 }
glennrp991d11d2010-11-12 21:55:28 +000010792 }
10793
cristy3ed852e2009-09-05 21:47:34 +000010794 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010795 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010796
cristy3ed852e2009-09-05 21:47:34 +000010797 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010798
cristy3ed852e2009-09-05 21:47:34 +000010799 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010800 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010801
glennrp26f37912010-12-23 16:22:42 +000010802 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010803 {
glennrp4f25bd02011-01-01 18:51:28 +000010804 if ((image->page.width != 0 && image->page.width != image->columns) ||
10805 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010806 {
10807 unsigned char
10808 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010809
glennrp26f37912010-12-23 16:22:42 +000010810 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10811 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010812 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010813 PNGLong(chunk+4,(png_uint_32) image->page.width);
10814 PNGLong(chunk+8,(png_uint_32) image->page.height);
10815 chunk[12]=0; /* unit = pixels */
10816 (void) WriteBlob(image,13,chunk);
10817 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10818 }
cristy3ed852e2009-09-05 21:47:34 +000010819 }
10820
10821#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010822 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010823#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010824 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010825#undef PNG_HAVE_IDAT
10826#endif
10827
10828 png_set_packing(ping);
10829 /*
10830 Allocate memory.
10831 */
10832 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010833 if (image_depth > 8)
10834 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010835 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010836 {
glennrpb4a13412010-05-05 12:47:19 +000010837 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010838 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010839 break;
glennrp0fe50b42010-11-16 03:52:51 +000010840
glennrpb4a13412010-05-05 12:47:19 +000010841 case PNG_COLOR_TYPE_GRAY_ALPHA:
10842 rowbytes*=2;
10843 break;
glennrp0fe50b42010-11-16 03:52:51 +000010844
glennrpb4a13412010-05-05 12:47:19 +000010845 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010846 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010847 break;
glennrp0fe50b42010-11-16 03:52:51 +000010848
glennrpb4a13412010-05-05 12:47:19 +000010849 default:
10850 break;
cristy3ed852e2009-09-05 21:47:34 +000010851 }
glennrp3b51f0e2010-11-27 18:14:08 +000010852
10853 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010854 {
10855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10856 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010857
glennrpb4a13412010-05-05 12:47:19 +000010858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010859 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010860 }
cristy09973322013-06-30 21:06:30 +000010861 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10862 if (pixel_info == (MemoryInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010863 png_error(ping,"Allocation of memory for pixels failed");
cristy09973322013-06-30 21:06:30 +000010864 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
glennrp0fe50b42010-11-16 03:52:51 +000010865
cristy3ed852e2009-09-05 21:47:34 +000010866 /*
10867 Initialize image scanlines.
10868 */
cristy5f766ef2014-12-14 21:12:47 +000010869 quantum_info=AcquireQuantumInfo(image_info,image);
cristyed552522009-10-16 14:04:35 +000010870 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010871 png_error(ping,"Memory allocation for quantum_info failed");
cristy3ed852e2009-09-05 21:47:34 +000010872 quantum_info->format=UndefinedQuantumFormat;
10873 quantum_info->depth=image_depth;
glennrp4b840d72012-11-22 16:01:16 +000010874 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
cristy3ed852e2009-09-05 21:47:34 +000010875 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010876
cristy3ed852e2009-09-05 21:47:34 +000010877 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrpfd164d22013-01-26 21:10:22 +000010878 !mng_info->write_png48 && !mng_info->write_png64 &&
glennrp8bb3a022010-12-13 20:40:04 +000010879 !mng_info->write_png32) &&
10880 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010881 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010882 image_matte == MagickFalse &&
10883 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010884 {
glennrp8bb3a022010-12-13 20:40:04 +000010885 /* Palette, Bilevel, or Opaque Monochrome */
cristy16ea1392012-03-21 20:38:41 +000010886 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010887 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010888
cristy3ed852e2009-09-05 21:47:34 +000010889 quantum_info->depth=8;
10890 for (pass=0; pass < num_passes; pass++)
10891 {
10892 /*
10893 Convert PseudoClass image to a PNG monochrome image.
10894 */
cristybb503372010-05-27 20:51:26 +000010895 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010896 {
glennrpd71e86a2011-02-24 01:28:37 +000010897 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10899 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010900
cristy16ea1392012-03-21 20:38:41 +000010901 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010902
cristy16ea1392012-03-21 20:38:41 +000010903 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010904 break;
glennrp0fe50b42010-11-16 03:52:51 +000010905
cristy3ed852e2009-09-05 21:47:34 +000010906 if (mng_info->IsPalette)
10907 {
cristy16ea1392012-03-21 20:38:41 +000010908 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10909 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010910 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10911 mng_info->write_png_depth &&
10912 mng_info->write_png_depth != old_bit_depth)
10913 {
10914 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010915 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010916 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010917 >> (8-old_bit_depth));
10918 }
10919 }
glennrp0fe50b42010-11-16 03:52:51 +000010920
cristy3ed852e2009-09-05 21:47:34 +000010921 else
10922 {
cristy16ea1392012-03-21 20:38:41 +000010923 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10924 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010925 }
glennrp0fe50b42010-11-16 03:52:51 +000010926
cristy3ed852e2009-09-05 21:47:34 +000010927 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010928 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010929 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010930 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010931
glennrp3b51f0e2010-11-27 18:14:08 +000010932 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10934 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010935
glennrpcf002022011-01-30 02:38:15 +000010936 png_write_row(ping,ping_pixels);
glennrpaf932042014-11-29 20:44:25 +000010937
10938 status=SetImageProgress(image,LoadImageTag,
10939 (MagickOffsetType) (pass * image->rows + y),
10940 num_passes * image->rows);
glennrp44c22972015-01-25 03:10:16 +000010941
glennrpaf932042014-11-29 20:44:25 +000010942 if (status == MagickFalse)
10943 break;
cristy3ed852e2009-09-05 21:47:34 +000010944 }
cristy3ed852e2009-09-05 21:47:34 +000010945 }
10946 }
glennrp0fe50b42010-11-16 03:52:51 +000010947
glennrp8bb3a022010-12-13 20:40:04 +000010948 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010949 {
glennrp0fe50b42010-11-16 03:52:51 +000010950 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrpfd164d22013-01-26 21:10:22 +000010951 !mng_info->write_png48 && !mng_info->write_png64 &&
10952 !mng_info->write_png32) && (image_matte != MagickFalse ||
10953 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10954 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010955 {
cristy16ea1392012-03-21 20:38:41 +000010956 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010957 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010958
glennrp8bb3a022010-12-13 20:40:04 +000010959 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010960 {
glennrp8bb3a022010-12-13 20:40:04 +000010961
cristybb503372010-05-27 20:51:26 +000010962 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010963 {
cristy16ea1392012-03-21 20:38:41 +000010964 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010965
cristy16ea1392012-03-21 20:38:41 +000010966 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010967 break;
glennrp2cc891a2010-12-24 13:44:32 +000010968
glennrp5af765f2010-03-30 11:12:18 +000010969 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010970 {
glennrp8bb3a022010-12-13 20:40:04 +000010971 if (mng_info->IsPalette)
cristy16ea1392012-03-21 20:38:41 +000010972 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10973 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010974
glennrp8bb3a022010-12-13 20:40:04 +000010975 else
cristy16ea1392012-03-21 20:38:41 +000010976 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10977 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010978
glennrp3b51f0e2010-11-27 18:14:08 +000010979 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010981 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010982 }
glennrp2cc891a2010-12-24 13:44:32 +000010983
glennrp8bb3a022010-12-13 20:40:04 +000010984 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10985 {
10986 if (logging != MagickFalse && y == 0)
10987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10988 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010989
cristy16ea1392012-03-21 20:38:41 +000010990 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10991 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010992 }
glennrp2cc891a2010-12-24 13:44:32 +000010993
glennrp3b51f0e2010-11-27 18:14:08 +000010994 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010996 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010997
glennrpcf002022011-01-30 02:38:15 +000010998 png_write_row(ping,ping_pixels);
glennrp2cc891a2010-12-24 13:44:32 +000010999
glennrpaf932042014-11-29 20:44:25 +000011000 status=SetImageProgress(image,LoadImageTag,
11001 (MagickOffsetType) (pass * image->rows + y),
11002 num_passes * image->rows);
glennrp44c22972015-01-25 03:10:16 +000011003
glennrpaf932042014-11-29 20:44:25 +000011004 if (status == MagickFalse)
11005 break;
cristy3ed852e2009-09-05 21:47:34 +000011006 }
cristy3ed852e2009-09-05 21:47:34 +000011007 }
11008 }
glennrp8bb3a022010-12-13 20:40:04 +000011009
11010 else
11011 {
cristy16ea1392012-03-21 20:38:41 +000011012 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000011013 *p;
11014
11015 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000011016 {
glennrpfd164d22013-01-26 21:10:22 +000011017 if ((image_depth > 8) ||
11018 mng_info->write_png24 ||
glennrp8bb3a022010-12-13 20:40:04 +000011019 mng_info->write_png32 ||
glennrpfd164d22013-01-26 21:10:22 +000011020 mng_info->write_png48 ||
11021 mng_info->write_png64 ||
11022 (!mng_info->write_png8 && !mng_info->IsPalette))
glennrp8bb3a022010-12-13 20:40:04 +000011023 {
11024 for (y=0; y < (ssize_t) image->rows; y++)
11025 {
cristy862a33c2012-05-17 22:49:37 +000011026 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000011027
cristy16ea1392012-03-21 20:38:41 +000011028 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000011029 break;
glennrp2cc891a2010-12-24 13:44:32 +000011030
glennrp8bb3a022010-12-13 20:40:04 +000011031 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11032 {
11033 if (image->storage_class == DirectClass)
cristy16ea1392012-03-21 20:38:41 +000011034 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11035 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011036
glennrp8bb3a022010-12-13 20:40:04 +000011037 else
cristy16ea1392012-03-21 20:38:41 +000011038 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11039 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000011040 }
glennrp2cc891a2010-12-24 13:44:32 +000011041
glennrp8bb3a022010-12-13 20:40:04 +000011042 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11043 {
cristy16ea1392012-03-21 20:38:41 +000011044 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000011045 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000011046 exception);
glennrp2cc891a2010-12-24 13:44:32 +000011047
glennrp8bb3a022010-12-13 20:40:04 +000011048 if (logging != MagickFalse && y == 0)
11049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11050 " Writing GRAY_ALPHA PNG pixels (3)");
11051 }
glennrp2cc891a2010-12-24 13:44:32 +000011052
glennrp8bb3a022010-12-13 20:40:04 +000011053 else if (image_matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +000011054 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11055 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011056
glennrp8bb3a022010-12-13 20:40:04 +000011057 else
cristy16ea1392012-03-21 20:38:41 +000011058 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11059 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011060
glennrp8bb3a022010-12-13 20:40:04 +000011061 if (logging != MagickFalse && y == 0)
11062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11063 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000011064
glennrpcf002022011-01-30 02:38:15 +000011065 png_write_row(ping,ping_pixels);
glennrpaf932042014-11-29 20:44:25 +000011066
11067 status=SetImageProgress(image,LoadImageTag,
11068 (MagickOffsetType) (pass * image->rows + y),
11069 num_passes * image->rows);
glennrp44c22972015-01-25 03:10:16 +000011070
glennrpaf932042014-11-29 20:44:25 +000011071 if (status == MagickFalse)
11072 break;
glennrp8bb3a022010-12-13 20:40:04 +000011073 }
11074 }
glennrp2cc891a2010-12-24 13:44:32 +000011075
glennrp8bb3a022010-12-13 20:40:04 +000011076 else
glennrpfd164d22013-01-26 21:10:22 +000011077 /* not ((image_depth > 8) ||
11078 mng_info->write_png24 || mng_info->write_png32 ||
11079 mng_info->write_png48 || mng_info->write_png64 ||
11080 (!mng_info->write_png8 && !mng_info->IsPalette))
11081 */
glennrp8bb3a022010-12-13 20:40:04 +000011082 {
11083 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11084 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11085 {
11086 if (logging != MagickFalse)
11087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11088 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000011089
glennrp8bb3a022010-12-13 20:40:04 +000011090 quantum_info->depth=8;
11091 image_depth=8;
11092 }
glennrp2cc891a2010-12-24 13:44:32 +000011093
glennrp8bb3a022010-12-13 20:40:04 +000011094 for (y=0; y < (ssize_t) image->rows; y++)
11095 {
11096 if (logging != MagickFalse && y == 0)
11097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11098 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000011099
cristy16ea1392012-03-21 20:38:41 +000011100 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000011101
cristy16ea1392012-03-21 20:38:41 +000011102 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000011103 break;
glennrp2cc891a2010-12-24 13:44:32 +000011104
glennrp8bb3a022010-12-13 20:40:04 +000011105 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000011106 {
glennrp4bf89732011-03-21 13:48:28 +000011107 quantum_info->depth=image->depth;
11108
cristy16ea1392012-03-21 20:38:41 +000011109 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11110 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000011111 }
glennrp2cc891a2010-12-24 13:44:32 +000011112
glennrp8bb3a022010-12-13 20:40:04 +000011113 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11114 {
11115 if (logging != MagickFalse && y == 0)
11116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11117 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000011118
cristy16ea1392012-03-21 20:38:41 +000011119 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000011120 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000011121 exception);
glennrp8bb3a022010-12-13 20:40:04 +000011122 }
glennrp2cc891a2010-12-24 13:44:32 +000011123
glennrp8bb3a022010-12-13 20:40:04 +000011124 else
glennrp8bb3a022010-12-13 20:40:04 +000011125 {
cristy16ea1392012-03-21 20:38:41 +000011126 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11127 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000011128
11129 if (logging != MagickFalse && y <= 2)
11130 {
11131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000011132 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000011133
11134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11135 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11136 (int)ping_pixels[0],(int)ping_pixels[1]);
11137 }
glennrp8bb3a022010-12-13 20:40:04 +000011138 }
glennrpcf002022011-01-30 02:38:15 +000011139 png_write_row(ping,ping_pixels);
glennrp2cc891a2010-12-24 13:44:32 +000011140
glennrpaf932042014-11-29 20:44:25 +000011141 status=SetImageProgress(image,LoadImageTag,
11142 (MagickOffsetType) (pass * image->rows + y),
11143 num_passes * image->rows);
glennrp44c22972015-01-25 03:10:16 +000011144
glennrp8bb3a022010-12-13 20:40:04 +000011145 if (status == MagickFalse)
11146 break;
11147 }
glennrpaf932042014-11-29 20:44:25 +000011148 }
cristy3ed852e2009-09-05 21:47:34 +000011149 }
glennrp8bb3a022010-12-13 20:40:04 +000011150 }
11151 }
11152
cristyb32b90a2009-09-07 21:45:48 +000011153 if (quantum_info != (QuantumInfo *) NULL)
11154 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000011155
11156 if (logging != MagickFalse)
11157 {
11158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000011159 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000011160
cristy3ed852e2009-09-05 21:47:34 +000011161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011162 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000011163
cristy3ed852e2009-09-05 21:47:34 +000011164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011165 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000011166
cristy3ed852e2009-09-05 21:47:34 +000011167 if (mng_info->write_png_depth)
11168 {
11169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011170 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011171 }
glennrp0fe50b42010-11-16 03:52:51 +000011172
cristy3ed852e2009-09-05 21:47:34 +000011173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000011174 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011175
cristy3ed852e2009-09-05 21:47:34 +000011176 if (mng_info->write_png_colortype)
11177 {
11178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011179 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011180 }
glennrp0fe50b42010-11-16 03:52:51 +000011181
cristy3ed852e2009-09-05 21:47:34 +000011182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000011183 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011184
cristy3ed852e2009-09-05 21:47:34 +000011185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000011186 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000011187 }
11188 /*
glennrpa0ed0092011-04-18 16:36:29 +000011189 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000011190 */
glennrp823b55c2011-03-14 18:46:46 +000011191 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000011192 {
glennrp26f37912010-12-23 16:22:42 +000011193 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000011194 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000011195 while (property != (const char *) NULL)
11196 {
11197 png_textp
11198 text;
glennrp2cc891a2010-12-24 13:44:32 +000011199
cristy16ea1392012-03-21 20:38:41 +000011200 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000011201
glennrpe4d5faf2013-07-22 00:11:04 +000011202 /* Don't write any "png:" or "jpeg:" properties; those are just for
11203 * "identify" or for passing through to another JPEG
11204 */
11205 if ((LocaleNCompare(property,"png:",4) != 0 &&
glennrpa3d5f0e2014-01-30 18:58:51 +000011206 LocaleNCompare(property,"jpeg:",5) != 0) &&
glennrpe4d5faf2013-07-22 00:11:04 +000011207
glennrpa0ed0092011-04-18 16:36:29 +000011208
11209 /* Suppress density and units if we wrote a pHYs chunk */
11210 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000011211 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000011212 LocaleCompare(property,"units") != 0) &&
11213
11214 /* Suppress the IM-generated Date:create and Date:modify */
11215 (ping_exclude_date == MagickFalse ||
11216 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000011217 {
glennrpc70af4a2011-03-07 00:08:23 +000011218 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000011219 {
cristya865ccd2012-07-28 00:33:10 +000011220
glennrpecab7d72013-05-14 22:50:32 +000011221#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +000011222 text=(png_textp) png_malloc(ping,
11223 (png_alloc_size_t) sizeof(png_text));
11224#else
11225 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11226#endif
glennrpc70af4a2011-03-07 00:08:23 +000011227 text[0].key=(char *) property;
11228 text[0].text=(char *) value;
11229 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000011230
glennrpc70af4a2011-03-07 00:08:23 +000011231 if (ping_exclude_tEXt != MagickFalse)
11232 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11233
11234 else if (ping_exclude_zTXt != MagickFalse)
11235 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11236
11237 else
glennrp26f37912010-12-23 16:22:42 +000011238 {
glennrpc70af4a2011-03-07 00:08:23 +000011239 text[0].compression=image_info->compression == NoCompression ||
11240 (image_info->compression == UndefinedCompression &&
11241 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11242 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000011243 }
glennrp2cc891a2010-12-24 13:44:32 +000011244
glennrpc70af4a2011-03-07 00:08:23 +000011245 if (logging != MagickFalse)
11246 {
11247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11248 " Setting up text chunk");
11249
11250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpcbc92152013-02-04 15:46:22 +000011251 " keyword: '%s'",text[0].key);
glennrpc70af4a2011-03-07 00:08:23 +000011252 }
11253
11254 png_set_text(ping,ping_info,text,1);
11255 png_free(ping,text);
11256 }
glennrp26f37912010-12-23 16:22:42 +000011257 }
11258 property=GetNextImageProperty(image);
11259 }
cristy3ed852e2009-09-05 21:47:34 +000011260 }
11261
11262 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011263 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011264
11265 if (logging != MagickFalse)
11266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11267 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000011268
cristy3ed852e2009-09-05 21:47:34 +000011269 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000011270
cristy3ed852e2009-09-05 21:47:34 +000011271 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11272 {
11273 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000011274 (ping_width != mng_info->page.width) ||
11275 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000011276 {
11277 unsigned char
11278 chunk[32];
11279
11280 /*
11281 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11282 */
11283 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11284 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000011285 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000011286 chunk[4]=4;
11287 chunk[5]=0; /* frame name separator (no name) */
11288 chunk[6]=1; /* flag for changing delay, for next frame only */
11289 chunk[7]=0; /* flag for changing frame timeout */
11290 chunk[8]=1; /* flag for changing frame clipping for next frame */
11291 chunk[9]=0; /* flag for changing frame sync_id */
11292 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11293 chunk[14]=0; /* clipping boundaries delta type */
11294 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11295 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000011296 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000011297 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11298 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000011299 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000011300 (void) WriteBlob(image,31,chunk);
11301 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11302 mng_info->old_framing_mode=4;
11303 mng_info->framing_mode=1;
11304 }
glennrp0fe50b42010-11-16 03:52:51 +000011305
cristy3ed852e2009-09-05 21:47:34 +000011306 else
11307 mng_info->framing_mode=3;
11308 }
11309 if (mng_info->write_mng && !mng_info->need_fram &&
11310 ((int) image->dispose == 3))
glennrpedaa0382012-04-12 14:16:21 +000011311 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
glennrp0fe50b42010-11-16 03:52:51 +000011312
cristy3ed852e2009-09-05 21:47:34 +000011313 /*
11314 Free PNG resources.
11315 */
glennrp5af765f2010-03-30 11:12:18 +000011316
cristy3ed852e2009-09-05 21:47:34 +000011317 png_destroy_write_struct(&ping,&ping_info);
11318
cristy09973322013-06-30 21:06:30 +000011319 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +000011320
cristy16ea1392012-03-21 20:38:41 +000011321 if (ping_have_blob != MagickFalse)
11322 (void) CloseBlob(image);
11323
11324 image_info=DestroyImageInfo(image_info);
11325 image=DestroyImage(image);
11326
glennrpb9cfe272010-12-21 15:08:06 +000011327 /* Store bit depth actually written */
11328 s[0]=(char) ping_bit_depth;
11329 s[1]='\0';
11330
cristy16ea1392012-03-21 20:38:41 +000011331 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000011332
cristy3ed852e2009-09-05 21:47:34 +000011333 if (logging != MagickFalse)
11334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11335 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011336
glennrp868fff32014-03-16 22:09:06 +000011337#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpedaa0382012-04-12 14:16:21 +000011338 UnlockSemaphoreInfo(ping_semaphore);
11339#endif
11340
11341 /* } for navigation to beginning of SETJMP-protected block. Revert to
11342 * Throwing an Exception when an error occurs.
11343 */
11344
cristy3ed852e2009-09-05 21:47:34 +000011345 return(MagickTrue);
11346/* End write one PNG image */
glennrpedaa0382012-04-12 14:16:21 +000011347
cristy3ed852e2009-09-05 21:47:34 +000011348}
11349
11350/*
11351%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11352% %
11353% %
11354% %
11355% W r i t e P N G I m a g e %
11356% %
11357% %
11358% %
11359%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11360%
11361% WritePNGImage() writes a Portable Network Graphics (PNG) or
11362% Multiple-image Network Graphics (MNG) image file.
11363%
11364% MNG support written by Glenn Randers-Pehrson, glennrp@image...
11365%
11366% The format of the WritePNGImage method is:
11367%
cristy16ea1392012-03-21 20:38:41 +000011368% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11369% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011370%
11371% A description of each parameter follows:
11372%
11373% o image_info: the image info.
11374%
11375% o image: The image.
11376%
cristy16ea1392012-03-21 20:38:41 +000011377% o exception: return any errors or warnings in this structure.
11378%
cristy3ed852e2009-09-05 21:47:34 +000011379% Returns MagickTrue on success, MagickFalse on failure.
11380%
11381% Communicating with the PNG encoder:
11382%
11383% While the datastream written is always in PNG format and normally would
11384% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000011385% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000011386%
glennrp5a39f372011-02-25 04:52:16 +000011387% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11388% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000011389% is present, the tRNS chunk must only have values 0 and 255
11390% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000011391% transparent). If other values are present they will be
11392% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000011393% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000011394% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11395% of any resulting fully-transparent pixels is changed to
11396% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000011397%
11398% If you want better quantization or dithering of the colors
11399% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000011400% PNG encoder. The pixels contain 8-bit indices even if
11401% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000011402% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000011403% PNG grayscale type might be slightly more efficient. Please
11404% note that writing to the PNG8 format may result in loss
11405% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000011406%
11407% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11408% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000011409% one of the colors as transparent. The only loss incurred
11410% is reduction of sample depth to 8. If the image has more
11411% than one transparent color, has semitransparent pixels, or
11412% has an opaque pixel with the same RGB components as the
11413% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000011414%
11415% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11416% transparency is permitted, i.e., the alpha sample for
11417% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000011418% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000011419% The only loss in data is the reduction of the sample depth
11420% to 8.
cristy3ed852e2009-09-05 21:47:34 +000011421%
glennrpfd164d22013-01-26 21:10:22 +000011422% o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11423% chunk can be present to convey binary transparency by naming
11424% one of the colors as transparent. If the image has more
11425% than one transparent color, has semitransparent pixels, or
11426% has an opaque pixel with the same RGB components as the
11427% transparent color, an image is not written.
11428%
11429% o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11430% transparency is permitted, i.e., the alpha sample for
11431% each pixel can have any value from 0 to 65535. The alpha
11432% channel is present even if the image is fully opaque.
11433%
glennrp5830fbc2013-01-27 06:11:45 +000011434% o PNG00: A PNG that inherits its colortype and bit-depth from the input
11435% image, if the input was a PNG, is written. If these values
glennrpc1e07702015-05-13 02:12:57 +000011436% cannot be found, or if the pixels have been changed in a way
11437% that makes this impossible, then "PNG00" falls back to the
11438% regular "PNG" format.
glennrp5830fbc2013-01-27 06:11:45 +000011439%
cristy3ed852e2009-09-05 21:47:34 +000011440% o -define: For more precise control of the PNG output, you can use the
11441% Image options "png:bit-depth" and "png:color-type". These
11442% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000011443% from the application programming interfaces. The options
11444% are case-independent and are converted to lowercase before
11445% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000011446%
11447% png:color-type can be 0, 2, 3, 4, or 6.
11448%
11449% When png:color-type is 0 (Grayscale), png:bit-depth can
11450% be 1, 2, 4, 8, or 16.
11451%
11452% When png:color-type is 2 (RGB), png:bit-depth can
11453% be 8 or 16.
11454%
11455% When png:color-type is 3 (Indexed), png:bit-depth can
11456% be 1, 2, 4, or 8. This refers to the number of bits
11457% used to store the index. The color samples always have
11458% bit-depth 8 in indexed PNG files.
11459%
11460% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11461% png:bit-depth can be 8 or 16.
11462%
glennrpfd164d22013-01-26 21:10:22 +000011463% If the image cannot be written without loss with the
11464% requested bit-depth and color-type, a PNG file will not
11465% be written, a warning will be issued, and the encoder will
11466% return MagickFalse.
glennrp5a39f372011-02-25 04:52:16 +000011467%
cristy3ed852e2009-09-05 21:47:34 +000011468% Since image encoders should not be responsible for the "heavy lifting",
11469% the user should make sure that ImageMagick has already reduced the
11470% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000011471% transparency prior to attempting to write the image with depth, color,
cristy16ea1392012-03-21 20:38:41 +000011472% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000011473%
cristy3ed852e2009-09-05 21:47:34 +000011474% Note that another definition, "png:bit-depth-written" exists, but it
11475% is not intended for external use. It is only used internally by the
11476% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11477%
11478% It is possible to request that the PNG encoder write previously-formatted
11479% ancillary chunks in the output PNG file, using the "-profile" commandline
11480% option as shown below or by setting the profile via a programming
11481% interface:
11482%
11483% -profile PNG-chunk-x:<file>
11484%
11485% where x is a location flag and <file> is a file containing the chunk
11486% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000011487% This encoder will compute the chunk length and CRC, so those must not
11488% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000011489%
11490% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11491% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11492% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000011493% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000011494%
glennrpbb8a7332010-11-13 15:17:35 +000011495% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000011496%
glennrp3241bd02010-12-12 04:36:28 +000011497% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000011498%
glennrpd6afd542010-11-19 01:53:05 +000011499% o 32-bit depth is reduced to 16.
11500% o 16-bit depth is reduced to 8 if all pixels contain samples whose
11501% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000011502% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000011503% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000011504% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000011505% o Grayscale images are reduced to 1, 2, or 4 bit depth if
11506% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000011507% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000011508% o If matte channel is present but only one transparent color is
11509% present, RGB+tRNS is written instead of RGBA
11510% o Opaque matte channel is removed (or added, if color-type 4 or 6
11511% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000011512%
cristy3ed852e2009-09-05 21:47:34 +000011513%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11514*/
11515static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy16ea1392012-03-21 20:38:41 +000011516 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011517{
11518 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011519 excluding,
11520 logging,
11521 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011522 status;
11523
11524 MngInfo
11525 *mng_info;
11526
11527 const char
11528 *value;
11529
11530 int
glennrp5c7cf4e2010-12-24 00:30:00 +000011531 source;
11532
cristy3ed852e2009-09-05 21:47:34 +000011533 /*
11534 Open image file.
11535 */
11536 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +000011537 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +000011538 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +000011539 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +000011540 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011541 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011542 /*
11543 Allocate a MngInfo structure.
11544 */
11545 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011546 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000011547
cristy3ed852e2009-09-05 21:47:34 +000011548 if (mng_info == (MngInfo *) NULL)
11549 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011550
cristy3ed852e2009-09-05 21:47:34 +000011551 /*
11552 Initialize members of the MngInfo structure.
11553 */
11554 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11555 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000011556 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011557 have_mng_structure=MagickTrue;
11558
11559 /* See if user has requested a specific PNG subformat */
11560
11561 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11562 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11563 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
glennrpfd164d22013-01-26 21:10:22 +000011564 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11565 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
cristy3ed852e2009-09-05 21:47:34 +000011566
cristy092ec8d2013-04-26 13:46:22 +000011567 value=GetImageOption(image_info,"png:format");
glennrpb381a262012-02-11 17:49:49 +000011568
glennrp5a4989d2014-04-23 00:34:09 +000011569 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
glennrpb381a262012-02-11 17:49:49 +000011570 {
glennrpf70c4d22013-03-19 15:26:48 +000011571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11572 " Format=%s",value);
11573
glennrpfd164d22013-01-26 21:10:22 +000011574 mng_info->write_png8 = MagickFalse;
11575 mng_info->write_png24 = MagickFalse;
11576 mng_info->write_png32 = MagickFalse;
11577 mng_info->write_png48 = MagickFalse;
11578 mng_info->write_png64 = MagickFalse;
11579
glennrpb381a262012-02-11 17:49:49 +000011580 if (LocaleCompare(value,"png8") == 0)
glennrpb381a262012-02-11 17:49:49 +000011581 mng_info->write_png8 = MagickTrue;
glennrpb381a262012-02-11 17:49:49 +000011582
11583 else if (LocaleCompare(value,"png24") == 0)
glennrpb381a262012-02-11 17:49:49 +000011584 mng_info->write_png24 = MagickTrue;
glennrpb381a262012-02-11 17:49:49 +000011585
11586 else if (LocaleCompare(value,"png32") == 0)
glennrpb381a262012-02-11 17:49:49 +000011587 mng_info->write_png32 = MagickTrue;
glennrpfd164d22013-01-26 21:10:22 +000011588
11589 else if (LocaleCompare(value,"png48") == 0)
11590 mng_info->write_png48 = MagickTrue;
11591
11592 else if (LocaleCompare(value,"png64") == 0)
11593 mng_info->write_png64 = MagickTrue;
glennrp5830fbc2013-01-27 06:11:45 +000011594
glennrp5a4989d2014-04-23 00:34:09 +000011595 else if ((LocaleCompare(value,"png00") == 0) ||
11596 LocaleCompare(image_info->magick,"PNG00") == 0)
glennrp5830fbc2013-01-27 06:11:45 +000011597 {
glennrp3398b5b2013-05-03 23:10:31 +000011598 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11599 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
glennrp6647b972015-01-12 04:39:14 +000011600
glennrpf70c4d22013-03-19 15:26:48 +000011601 if (value != (char *) NULL)
11602 {
11603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11604 " png00 inherited bit depth=%s",value);
glennrp6647b972015-01-12 04:39:14 +000011605
glennrpf70c4d22013-03-19 15:26:48 +000011606 if (LocaleCompare(value,"1") == 0)
11607 mng_info->write_png_depth = 1;
glennrp6647b972015-01-12 04:39:14 +000011608
glennrp5a4989d2014-04-23 00:34:09 +000011609 else if (LocaleCompare(value,"2") == 0)
glennrpf70c4d22013-03-19 15:26:48 +000011610 mng_info->write_png_depth = 2;
glennrp6647b972015-01-12 04:39:14 +000011611
glennrp5a4989d2014-04-23 00:34:09 +000011612 else if (LocaleCompare(value,"4") == 0)
glennrpf70c4d22013-03-19 15:26:48 +000011613 mng_info->write_png_depth = 4;
glennrp6647b972015-01-12 04:39:14 +000011614
glennrpf70c4d22013-03-19 15:26:48 +000011615 else if (LocaleCompare(value,"8") == 0)
11616 mng_info->write_png_depth = 8;
glennrp6647b972015-01-12 04:39:14 +000011617
glennrpf70c4d22013-03-19 15:26:48 +000011618 else if (LocaleCompare(value,"16") == 0)
11619 mng_info->write_png_depth = 16;
11620 }
glennrp6647b972015-01-12 04:39:14 +000011621
glennrp3398b5b2013-05-03 23:10:31 +000011622 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
glennrp6647b972015-01-12 04:39:14 +000011623
glennrpf70c4d22013-03-19 15:26:48 +000011624 if (value != (char *) NULL)
11625 {
11626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11627 " png00 inherited color type=%s",value);
glennrp6647b972015-01-12 04:39:14 +000011628
glennrpf70c4d22013-03-19 15:26:48 +000011629 if (LocaleCompare(value,"0") == 0)
11630 mng_info->write_png_colortype = 1;
glennrp6647b972015-01-12 04:39:14 +000011631
glennrpf70c4d22013-03-19 15:26:48 +000011632 else if (LocaleCompare(value,"2") == 0)
11633 mng_info->write_png_colortype = 3;
glennrp6647b972015-01-12 04:39:14 +000011634
glennrpf70c4d22013-03-19 15:26:48 +000011635 else if (LocaleCompare(value,"3") == 0)
11636 mng_info->write_png_colortype = 4;
glennrp6647b972015-01-12 04:39:14 +000011637
glennrpf70c4d22013-03-19 15:26:48 +000011638 else if (LocaleCompare(value,"4") == 0)
11639 mng_info->write_png_colortype = 5;
glennrp6647b972015-01-12 04:39:14 +000011640
glennrpf70c4d22013-03-19 15:26:48 +000011641 else if (LocaleCompare(value,"6") == 0)
11642 mng_info->write_png_colortype = 7;
11643 }
glennrp5830fbc2013-01-27 06:11:45 +000011644 }
11645 }
11646
cristy3ed852e2009-09-05 21:47:34 +000011647 if (mng_info->write_png8)
11648 {
glennrp9c1eb072010-06-06 22:19:15 +000011649 mng_info->write_png_colortype = /* 3 */ 4;
11650 mng_info->write_png_depth = 8;
11651 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011652 }
11653
11654 if (mng_info->write_png24)
11655 {
glennrp9c1eb072010-06-06 22:19:15 +000011656 mng_info->write_png_colortype = /* 2 */ 3;
11657 mng_info->write_png_depth = 8;
11658 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011659
cristy17f11b02014-12-20 19:37:04 +000011660 if (image->alpha_trait != UndefinedPixelTrait)
cristydef23e52015-01-22 11:52:01 +000011661 (void) SetImageType(image,TrueColorAlphaType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011662
glennrp9c1eb072010-06-06 22:19:15 +000011663 else
cristy16ea1392012-03-21 20:38:41 +000011664 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011665
cristy16ea1392012-03-21 20:38:41 +000011666 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011667 }
11668
11669 if (mng_info->write_png32)
11670 {
glennrp9c1eb072010-06-06 22:19:15 +000011671 mng_info->write_png_colortype = /* 6 */ 7;
11672 mng_info->write_png_depth = 8;
11673 image->depth = 8;
dirk47da46d2014-04-24 18:48:54 +000011674 image->alpha_trait = BlendPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +000011675
cristydef23e52015-01-22 11:52:01 +000011676 (void) SetImageType(image,TrueColorAlphaType,exception);
cristy16ea1392012-03-21 20:38:41 +000011677 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011678 }
11679
glennrpfd164d22013-01-26 21:10:22 +000011680 if (mng_info->write_png48)
11681 {
11682 mng_info->write_png_colortype = /* 2 */ 3;
11683 mng_info->write_png_depth = 16;
11684 image->depth = 16;
11685
cristy17f11b02014-12-20 19:37:04 +000011686 if (image->alpha_trait != UndefinedPixelTrait)
cristydef23e52015-01-22 11:52:01 +000011687 (void) SetImageType(image,TrueColorAlphaType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011688
11689 else
glennrp4dda64f2013-01-26 21:20:24 +000011690 (void) SetImageType(image,TrueColorType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011691
glennrp4dda64f2013-01-26 21:20:24 +000011692 (void) SyncImage(image,exception);
glennrpfd164d22013-01-26 21:10:22 +000011693 }
11694
11695 if (mng_info->write_png64)
11696 {
11697 mng_info->write_png_colortype = /* 6 */ 7;
11698 mng_info->write_png_depth = 16;
11699 image->depth = 16;
dirk47da46d2014-04-24 18:48:54 +000011700 image->alpha_trait = BlendPixelTrait;
glennrpfd164d22013-01-26 21:10:22 +000011701
cristydef23e52015-01-22 11:52:01 +000011702 (void) SetImageType(image,TrueColorAlphaType,exception);
glennrp4dda64f2013-01-26 21:20:24 +000011703 (void) SyncImage(image,exception);
glennrpfd164d22013-01-26 21:10:22 +000011704 }
11705
cristy092ec8d2013-04-26 13:46:22 +000011706 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011707
cristy3ed852e2009-09-05 21:47:34 +000011708 if (value != (char *) NULL)
11709 {
11710 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011711 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011712
cristy3ed852e2009-09-05 21:47:34 +000011713 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011714 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011715
cristy3ed852e2009-09-05 21:47:34 +000011716 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011717 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011718
cristy3ed852e2009-09-05 21:47:34 +000011719 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011720 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011721
cristy3ed852e2009-09-05 21:47:34 +000011722 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011723 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011724
glennrpbb8a7332010-11-13 15:17:35 +000011725 else
cristy16ea1392012-03-21 20:38:41 +000011726 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011727 GetMagickModule(),CoderWarning,
11728 "ignoring invalid defined png:bit-depth",
11729 "=%s",value);
11730
cristy3ed852e2009-09-05 21:47:34 +000011731 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011733 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011734 }
glennrp0fe50b42010-11-16 03:52:51 +000011735
cristy092ec8d2013-04-26 13:46:22 +000011736 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011737
cristy3ed852e2009-09-05 21:47:34 +000011738 if (value != (char *) NULL)
11739 {
11740 /* We must store colortype+1 because 0 is a valid colortype */
11741 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011742 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011743
cristy16ea1392012-03-21 20:38:41 +000011744 else if (LocaleCompare(value,"1") == 0)
11745 mng_info->write_png_colortype = 2;
11746
cristy3ed852e2009-09-05 21:47:34 +000011747 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011748 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011749
cristy3ed852e2009-09-05 21:47:34 +000011750 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011751 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011752
cristy3ed852e2009-09-05 21:47:34 +000011753 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011754 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011755
cristy3ed852e2009-09-05 21:47:34 +000011756 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011757 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011758
glennrpbb8a7332010-11-13 15:17:35 +000011759 else
cristy16ea1392012-03-21 20:38:41 +000011760 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011761 GetMagickModule(),CoderWarning,
11762 "ignoring invalid defined png:color-type",
11763 "=%s",value);
11764
cristy3ed852e2009-09-05 21:47:34 +000011765 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011767 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011768 }
11769
glennrp0e8ea192010-12-24 18:00:33 +000011770 /* Check for chunks to be excluded:
11771 *
glennrp0dff56c2011-01-29 19:10:02 +000011772 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011773 * listed in the "unused_chunks" array, above.
11774 *
cristy5d6fc9c2011-12-27 03:10:42 +000011775 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011776 * define (in the image properties or in the image artifacts)
11777 * or via a mng_info member. For convenience, in addition
11778 * to or instead of a comma-separated list of chunks, the
11779 * "exclude-chunk" string can be simply "all" or "none".
11780 *
11781 * The exclude-chunk define takes priority over the mng_info.
11782 *
cristy5d6fc9c2011-12-27 03:10:42 +000011783 * A "png:include-chunk" define takes priority over both the
11784 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011785 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011786 * well as a comma-separated list. Chunks that are unknown to
11787 * ImageMagick are always excluded, regardless of their "copy-safe"
11788 * status according to the PNG specification, and even if they
glennrpaa192b12012-01-17 21:35:21 +000011789 * appear in the "include-chunk" list. Such defines appearing among
11790 * the image options take priority over those found among the image
11791 * artifacts.
glennrp0e8ea192010-12-24 18:00:33 +000011792 *
11793 * Finally, all chunks listed in the "unused_chunks" array are
11794 * automatically excluded, regardless of the other instructions
11795 * or lack thereof.
11796 *
11797 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11798 * will not be written and the gAMA chunk will only be written if it
11799 * is not between .45 and .46, or approximately (1.0/2.2).
11800 *
11801 * If you exclude tRNS and the image has transparency, the colortype
11802 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11803 *
11804 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011805 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011806 */
11807
glennrp26f37912010-12-23 16:22:42 +000011808 mng_info->ping_exclude_bKGD=MagickFalse;
11809 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011810 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011811 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11812 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011813 mng_info->ping_exclude_iCCP=MagickFalse;
11814 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11815 mng_info->ping_exclude_oFFs=MagickFalse;
11816 mng_info->ping_exclude_pHYs=MagickFalse;
11817 mng_info->ping_exclude_sRGB=MagickFalse;
11818 mng_info->ping_exclude_tEXt=MagickFalse;
dirkfd6fd072014-10-24 21:10:08 +000011819 mng_info->ping_exclude_tIME=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011820 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011821 mng_info->ping_exclude_vpAg=MagickFalse;
11822 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11823 mng_info->ping_exclude_zTXt=MagickFalse;
11824
glennrp8d3d6e52011-04-19 04:39:51 +000011825 mng_info->ping_preserve_colormap=MagickFalse;
11826
cristy092ec8d2013-04-26 13:46:22 +000011827 value=GetImageOption(image_info,"png:preserve-colormap");
glennrp8d3d6e52011-04-19 04:39:51 +000011828 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011829 value=GetImageArtifact(image,"png:preserve-colormap");
glennrp8d3d6e52011-04-19 04:39:51 +000011830 if (value != NULL)
11831 mng_info->ping_preserve_colormap=MagickTrue;
11832
glennrpecab7d72013-05-14 22:50:32 +000011833 mng_info->ping_preserve_iCCP=MagickFalse;
11834
11835 value=GetImageOption(image_info,"png:preserve-iCCP");
11836 if (value == NULL)
11837 value=GetImageArtifact(image,"png:preserve-iCCP");
11838 if (value != NULL)
11839 mng_info->ping_preserve_iCCP=MagickTrue;
11840
11841 /* These compression-level, compression-strategy, and compression-filter
glennrp18682582011-06-30 18:11:47 +000011842 * defines take precedence over values from the -quality option.
11843 */
cristy092ec8d2013-04-26 13:46:22 +000011844 value=GetImageOption(image_info,"png:compression-level");
glennrp18682582011-06-30 18:11:47 +000011845 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011846 value=GetImageArtifact(image,"png:compression-level");
glennrp18682582011-06-30 18:11:47 +000011847 if (value != NULL)
11848 {
glennrp18682582011-06-30 18:11:47 +000011849 /* We have to add 1 to everything because 0 is a valid input,
11850 * and we want to use 0 (the default) to mean undefined.
11851 */
11852 if (LocaleCompare(value,"0") == 0)
11853 mng_info->write_png_compression_level = 1;
11854
glennrp0ffb95c2012-01-30 21:16:22 +000011855 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011856 mng_info->write_png_compression_level = 2;
11857
11858 else if (LocaleCompare(value,"2") == 0)
11859 mng_info->write_png_compression_level = 3;
11860
11861 else if (LocaleCompare(value,"3") == 0)
11862 mng_info->write_png_compression_level = 4;
11863
11864 else if (LocaleCompare(value,"4") == 0)
11865 mng_info->write_png_compression_level = 5;
11866
11867 else if (LocaleCompare(value,"5") == 0)
11868 mng_info->write_png_compression_level = 6;
11869
11870 else if (LocaleCompare(value,"6") == 0)
11871 mng_info->write_png_compression_level = 7;
11872
11873 else if (LocaleCompare(value,"7") == 0)
11874 mng_info->write_png_compression_level = 8;
11875
11876 else if (LocaleCompare(value,"8") == 0)
11877 mng_info->write_png_compression_level = 9;
11878
11879 else if (LocaleCompare(value,"9") == 0)
11880 mng_info->write_png_compression_level = 10;
11881
11882 else
cristy16ea1392012-03-21 20:38:41 +000011883 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011884 GetMagickModule(),CoderWarning,
11885 "ignoring invalid defined png:compression-level",
11886 "=%s",value);
11887 }
11888
cristy092ec8d2013-04-26 13:46:22 +000011889 value=GetImageOption(image_info,"png:compression-strategy");
glennrp18682582011-06-30 18:11:47 +000011890 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011891 value=GetImageArtifact(image,"png:compression-strategy");
glennrp18682582011-06-30 18:11:47 +000011892 if (value != NULL)
11893 {
glennrp18682582011-06-30 18:11:47 +000011894 if (LocaleCompare(value,"0") == 0)
11895 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11896
11897 else if (LocaleCompare(value,"1") == 0)
11898 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11899
11900 else if (LocaleCompare(value,"2") == 0)
11901 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11902
11903 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011904#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011905 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011906#else
11907 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11908#endif
glennrp18682582011-06-30 18:11:47 +000011909
11910 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011911#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011912 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011913#else
11914 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11915#endif
glennrp18682582011-06-30 18:11:47 +000011916
11917 else
cristy16ea1392012-03-21 20:38:41 +000011918 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011919 GetMagickModule(),CoderWarning,
11920 "ignoring invalid defined png:compression-strategy",
11921 "=%s",value);
11922 }
11923
cristy092ec8d2013-04-26 13:46:22 +000011924 value=GetImageOption(image_info,"png:compression-filter");
glennrp18682582011-06-30 18:11:47 +000011925 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011926 value=GetImageArtifact(image,"png:compression-filter");
glennrp18682582011-06-30 18:11:47 +000011927 if (value != NULL)
11928 {
glennrp18682582011-06-30 18:11:47 +000011929 /* To do: combinations of filters allowed by libpng
11930 * masks 0x08 through 0xf8
11931 *
11932 * Implement this as a comma-separated list of 0,1,2,3,4,5
11933 * where 5 is a special case meaning PNG_ALL_FILTERS.
11934 */
11935
11936 if (LocaleCompare(value,"0") == 0)
11937 mng_info->write_png_compression_filter = 1;
11938
cristyb19b8122012-10-22 11:03:30 +000011939 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011940 mng_info->write_png_compression_filter = 2;
11941
11942 else if (LocaleCompare(value,"2") == 0)
11943 mng_info->write_png_compression_filter = 3;
11944
11945 else if (LocaleCompare(value,"3") == 0)
11946 mng_info->write_png_compression_filter = 4;
11947
11948 else if (LocaleCompare(value,"4") == 0)
11949 mng_info->write_png_compression_filter = 5;
11950
11951 else if (LocaleCompare(value,"5") == 0)
11952 mng_info->write_png_compression_filter = 6;
11953
glennrp18682582011-06-30 18:11:47 +000011954 else
cristy16ea1392012-03-21 20:38:41 +000011955 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011956 GetMagickModule(),CoderWarning,
11957 "ignoring invalid defined png:compression-filter",
11958 "=%s",value);
glennrp1a506ac2014-07-25 19:49:10 +000011959 }
11960
11961 for (source=0; source<8; source++)
11962 {
11963 value = NULL;
11964
glennrp2dd19062014-07-28 00:22:24 +000011965 if (source == 0)
11966 value=GetImageOption(image_info,"png:exclude-chunks");
11967
11968 if (source == 1)
11969 value=GetImageArtifact(image,"png:exclude-chunks");
11970
11971 if (source == 2)
11972 value=GetImageOption(image_info,"png:exclude-chunk");
11973
11974 if (source == 3)
11975 value=GetImageArtifact(image,"png:exclude-chunk");
11976
11977 if (source == 4)
11978 value=GetImageOption(image_info,"png:include-chunks");
11979
11980 if (source == 5)
11981 value=GetImageArtifact(image,"png:include-chunks");
11982
11983 if (source == 6)
11984 value=GetImageOption(image_info,"png:include-chunk");
11985
11986 if (source == 7)
11987 value=GetImageArtifact(image,"png:include-chunk");
glennrp18682582011-06-30 18:11:47 +000011988
glennrp1a506ac2014-07-25 19:49:10 +000011989 if (value == NULL)
11990 continue;
glennrp03812ae2010-12-24 01:31:34 +000011991
glennrp1a506ac2014-07-25 19:49:10 +000011992 if (source < 4)
11993 excluding = MagickTrue;
glennrp5c7cf4e2010-12-24 00:30:00 +000011994 else
glennrp1a506ac2014-07-25 19:49:10 +000011995 excluding = MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011996
11997 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011998 {
glennrp1a506ac2014-07-25 19:49:10 +000011999 if (source == 0 || source == 2)
12000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12001 " png:exclude-chunk=%s found in image options.\n", value);
12002 else if (source == 1 || source == 3)
glennrp2cc891a2010-12-24 13:44:32 +000012003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12004 " png:exclude-chunk=%s found in image artifacts.\n", value);
glennrp1a506ac2014-07-25 19:49:10 +000012005 else if (source == 4 || source == 6)
glennrp2cc891a2010-12-24 13:44:32 +000012006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a506ac2014-07-25 19:49:10 +000012007 " png:include-chunk=%s found in image options.\n", value);
12008 else /* if (source == 5 || source == 7) */
12009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12010 " png:include-chunk=%s found in image artifacts.\n", value);
glennrp2cc891a2010-12-24 13:44:32 +000012011 }
glennrp03812ae2010-12-24 01:31:34 +000012012
glennrp689efa22014-07-25 00:00:15 +000012013 if (IsOptionMember("all",value) != MagickFalse)
glennrp97d1b572013-05-24 01:41:32 +000012014 {
glennrp1a506ac2014-07-25 19:49:10 +000012015 mng_info->ping_exclude_bKGD=excluding;
12016 mng_info->ping_exclude_cHRM=excluding;
12017 mng_info->ping_exclude_date=excluding;
12018 mng_info->ping_exclude_EXIF=excluding;
12019 mng_info->ping_exclude_gAMA=excluding;
12020 mng_info->ping_exclude_iCCP=excluding;
12021 /* mng_info->ping_exclude_iTXt=excluding; */
12022 mng_info->ping_exclude_oFFs=excluding;
12023 mng_info->ping_exclude_pHYs=excluding;
12024 mng_info->ping_exclude_sRGB=excluding;
12025 mng_info->ping_exclude_tEXt=excluding;
dirkfd6fd072014-10-24 21:10:08 +000012026 mng_info->ping_exclude_tIME=excluding;
glennrp1a506ac2014-07-25 19:49:10 +000012027 mng_info->ping_exclude_tRNS=excluding;
12028 mng_info->ping_exclude_vpAg=excluding;
12029 mng_info->ping_exclude_zCCP=excluding;
12030 mng_info->ping_exclude_zTXt=excluding;
glennrp03812ae2010-12-24 01:31:34 +000012031 }
glennrp280283d2014-08-01 15:02:04 +000012032
glennrp1a506ac2014-07-25 19:49:10 +000012033 if (IsOptionMember("none",value) != MagickFalse)
glennrpacba0042010-12-24 14:27:26 +000012034 {
cristya20c8fe2014-08-14 11:55:11 +000012035 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12036 MagickTrue;
12037 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12038 MagickTrue;
12039 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12040 MagickTrue;
12041 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12042 MagickTrue;
12043 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12044 MagickTrue;
12045 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12046 MagickTrue;
glennrp1a506ac2014-07-25 19:49:10 +000012047 /* mng_info->ping_exclude_iTXt=!excluding; */
cristya20c8fe2014-08-14 11:55:11 +000012048 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12049 MagickTrue;
12050 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12051 MagickTrue;
12052 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12053 MagickTrue;
12054 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12055 MagickTrue;
dirkfd6fd072014-10-24 21:10:08 +000012056 mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12057 MagickTrue;
cristya20c8fe2014-08-14 11:55:11 +000012058 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12059 MagickTrue;
12060 mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12061 MagickTrue;
12062 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12063 MagickTrue;
12064 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12065 MagickTrue;
glennrpacba0042010-12-24 14:27:26 +000012066 }
12067
glennrp1a506ac2014-07-25 19:49:10 +000012068 if (IsOptionMember("bkgd",value) != MagickFalse)
12069 mng_info->ping_exclude_bKGD=excluding;
glennrp26f37912010-12-23 16:22:42 +000012070
glennrp1a506ac2014-07-25 19:49:10 +000012071 if (IsOptionMember("chrm",value) != MagickFalse)
12072 mng_info->ping_exclude_cHRM=excluding;
glennrp03812ae2010-12-24 01:31:34 +000012073
glennrp1a506ac2014-07-25 19:49:10 +000012074 if (IsOptionMember("date",value) != MagickFalse)
12075 mng_info->ping_exclude_date=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012076
glennrp1a506ac2014-07-25 19:49:10 +000012077 if (IsOptionMember("exif",value) != MagickFalse)
12078 mng_info->ping_exclude_EXIF=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012079
glennrp1a506ac2014-07-25 19:49:10 +000012080 if (IsOptionMember("gama",value) != MagickFalse)
12081 mng_info->ping_exclude_gAMA=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012082
glennrp1a506ac2014-07-25 19:49:10 +000012083 if (IsOptionMember("iccp",value) != MagickFalse)
12084 mng_info->ping_exclude_iCCP=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012085
glennrp689efa22014-07-25 00:00:15 +000012086#if 0
glennrp1a506ac2014-07-25 19:49:10 +000012087 if (IsOptionMember("itxt",value) != MagickFalse)
12088 mng_info->ping_exclude_iTXt=excluding;
glennrp689efa22014-07-25 00:00:15 +000012089#endif
glennrp2cc891a2010-12-24 13:44:32 +000012090
glennrp1a506ac2014-07-25 19:49:10 +000012091 if (IsOptionMember("offs",value) != MagickFalse)
12092 mng_info->ping_exclude_oFFs=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012093
glennrp1a506ac2014-07-25 19:49:10 +000012094 if (IsOptionMember("phys",value) != MagickFalse)
12095 mng_info->ping_exclude_pHYs=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012096
glennrp1a506ac2014-07-25 19:49:10 +000012097 if (IsOptionMember("srgb",value) != MagickFalse)
12098 mng_info->ping_exclude_sRGB=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012099
glennrp1a506ac2014-07-25 19:49:10 +000012100 if (IsOptionMember("text",value) != MagickFalse)
12101 mng_info->ping_exclude_tEXt=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012102
dirkfd6fd072014-10-24 21:10:08 +000012103 if (IsOptionMember("time",value) != MagickFalse)
12104 mng_info->ping_exclude_tIME=excluding;
12105
glennrp1a506ac2014-07-25 19:49:10 +000012106 if (IsOptionMember("trns",value) != MagickFalse)
12107 mng_info->ping_exclude_tRNS=excluding;
glennrpa1e3b7b2010-12-24 16:37:33 +000012108
glennrp1a506ac2014-07-25 19:49:10 +000012109 if (IsOptionMember("vpag",value) != MagickFalse)
12110 mng_info->ping_exclude_vpAg=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012111
glennrp1a506ac2014-07-25 19:49:10 +000012112 if (IsOptionMember("zccp",value) != MagickFalse)
12113 mng_info->ping_exclude_zCCP=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012114
glennrp1a506ac2014-07-25 19:49:10 +000012115 if (IsOptionMember("ztxt",value) != MagickFalse)
12116 mng_info->ping_exclude_zTXt=excluding;
glennrp26f37912010-12-23 16:22:42 +000012117 }
12118
glennrp1a506ac2014-07-25 19:49:10 +000012119 if (logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000012120 {
12121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000012122 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000012123 if (mng_info->ping_exclude_bKGD != MagickFalse)
12124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12125 " bKGD");
12126 if (mng_info->ping_exclude_cHRM != MagickFalse)
12127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12128 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000012129 if (mng_info->ping_exclude_date != MagickFalse)
12130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12131 " date");
glennrp26f37912010-12-23 16:22:42 +000012132 if (mng_info->ping_exclude_EXIF != MagickFalse)
12133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12134 " EXIF");
12135 if (mng_info->ping_exclude_gAMA != MagickFalse)
12136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12137 " gAMA");
12138 if (mng_info->ping_exclude_iCCP != MagickFalse)
12139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12140 " iCCP");
glennrp689efa22014-07-25 00:00:15 +000012141#if 0
glennrp26f37912010-12-23 16:22:42 +000012142 if (mng_info->ping_exclude_iTXt != MagickFalse)
12143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12144 " iTXt");
glennrp689efa22014-07-25 00:00:15 +000012145#endif
12146
glennrp26f37912010-12-23 16:22:42 +000012147 if (mng_info->ping_exclude_oFFs != MagickFalse)
12148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12149 " oFFs");
12150 if (mng_info->ping_exclude_pHYs != MagickFalse)
12151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12152 " pHYs");
12153 if (mng_info->ping_exclude_sRGB != MagickFalse)
12154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12155 " sRGB");
12156 if (mng_info->ping_exclude_tEXt != MagickFalse)
12157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12158 " tEXt");
dirkfd6fd072014-10-24 21:10:08 +000012159 if (mng_info->ping_exclude_tIME != MagickFalse)
12160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12161 " tIME");
glennrpa1e3b7b2010-12-24 16:37:33 +000012162 if (mng_info->ping_exclude_tRNS != MagickFalse)
12163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12164 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000012165 if (mng_info->ping_exclude_vpAg != MagickFalse)
12166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12167 " vpAg");
12168 if (mng_info->ping_exclude_zCCP != MagickFalse)
12169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12170 " zCCP");
12171 if (mng_info->ping_exclude_zTXt != MagickFalse)
12172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12173 " zTXt");
12174 }
12175
glennrpb9cfe272010-12-21 15:08:06 +000012176 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000012177
cristy16ea1392012-03-21 20:38:41 +000012178 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012179
12180 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012181
cristy3ed852e2009-09-05 21:47:34 +000012182 if (logging != MagickFalse)
12183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012184
cristy3ed852e2009-09-05 21:47:34 +000012185 return(status);
12186}
12187
12188#if defined(JNG_SUPPORTED)
12189
12190/* Write one JNG image */
12191static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +000012192 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012193{
12194 Image
12195 *jpeg_image;
12196
12197 ImageInfo
12198 *jpeg_image_info;
12199
12200 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000012201 logging,
cristy3ed852e2009-09-05 21:47:34 +000012202 status;
12203
12204 size_t
12205 length;
12206
12207 unsigned char
12208 *blob,
12209 chunk[80],
12210 *p;
12211
12212 unsigned int
12213 jng_alpha_compression_method,
12214 jng_alpha_sample_depth,
12215 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000012216 transparent;
12217
cristybb503372010-05-27 20:51:26 +000012218 size_t
glennrp59575fa2011-12-31 21:31:39 +000012219 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000012220 jng_quality;
12221
12222 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000012223 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012224
12225 blob=(unsigned char *) NULL;
12226 jpeg_image=(Image *) NULL;
12227 jpeg_image_info=(ImageInfo *) NULL;
glennrp7e99a062015-02-07 16:30:52 +000012228 length=0;
cristy3ed852e2009-09-05 21:47:34 +000012229
12230 status=MagickTrue;
cristydef23e52015-01-22 11:52:01 +000012231 transparent=image_info->type==GrayscaleAlphaType ||
glennrp44c22972015-01-25 03:10:16 +000012232 image_info->type==TrueColorAlphaType ||
12233 image->alpha_trait != UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +000012234
glennrp4b917592013-06-20 19:53:59 +000012235 jng_alpha_sample_depth = 0;
12236
glennrp59575fa2011-12-31 21:31:39 +000012237 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12238
12239 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12240
glennrp750105b2012-04-25 16:20:45 +000012241 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
glennrp59575fa2011-12-31 21:31:39 +000012242 image_info->quality;
12243
12244 if (jng_alpha_quality >= 1000)
12245 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000012246
glennrpd0ee5a22014-03-28 14:28:47 +000012247 length=0;
12248
glennrp8fe91592014-10-25 13:57:25 +000012249 if (transparent != 0)
cristy3ed852e2009-09-05 21:47:34 +000012250 {
12251 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000012252
cristy3ed852e2009-09-05 21:47:34 +000012253 /* Create JPEG blob, image, and image_info */
12254 if (logging != MagickFalse)
12255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +000012256 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000012257
cristy3ed852e2009-09-05 21:47:34 +000012258 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000012259
cristy3ed852e2009-09-05 21:47:34 +000012260 if (jpeg_image_info == (ImageInfo *) NULL)
12261 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000012262
cristy3ed852e2009-09-05 21:47:34 +000012263 if (logging != MagickFalse)
12264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12265 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000012266
cristy16ea1392012-03-21 20:38:41 +000012267 jpeg_image=SeparateImage(image,AlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +000012268 if (jpeg_image == (Image *) NULL)
12269 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy151b66d2015-04-15 10:50:31 +000012270 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
cristy8a46d822012-08-28 23:32:39 +000012271 jpeg_image->alpha_trait=UndefinedPixelTrait;
glennrp8f77fdc2012-03-21 15:16:37 +000012272 jpeg_image->quality=jng_alpha_quality;
cristy16ea1392012-03-21 20:38:41 +000012273 jpeg_image_info->type=GrayscaleType;
12274 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000012275 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy151b66d2015-04-15 10:50:31 +000012276 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +000012277 "%s",jpeg_image->filename);
12278 }
glennrp59575fa2011-12-31 21:31:39 +000012279 else
12280 {
12281 jng_alpha_compression_method=0;
12282 jng_color_type=10;
12283 jng_alpha_sample_depth=0;
12284 }
cristy3ed852e2009-09-05 21:47:34 +000012285
12286 /* To do: check bit depth of PNG alpha channel */
12287
12288 /* Check if image is grayscale. */
cristydef23e52015-01-22 11:52:01 +000012289 if (image_info->type != TrueColorAlphaType && image_info->type !=
dirkf1d85482015-04-06 00:36:00 +000012290 TrueColorType && SetImageGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000012291 jng_color_type-=2;
12292
glennrp59575fa2011-12-31 21:31:39 +000012293 if (logging != MagickFalse)
12294 {
12295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12296 " JNG Quality = %d",(int) jng_quality);
12297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12298 " JNG Color Type = %d",jng_color_type);
glennrp8fe91592014-10-25 13:57:25 +000012299 if (transparent != 0)
glennrp59575fa2011-12-31 21:31:39 +000012300 {
12301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12302 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12304 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12306 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12307 }
12308 }
12309
glennrp8fe91592014-10-25 13:57:25 +000012310 if (transparent != 0)
cristy3ed852e2009-09-05 21:47:34 +000012311 {
12312 if (jng_alpha_compression_method==0)
12313 {
12314 const char
12315 *value;
12316
cristy16ea1392012-03-21 20:38:41 +000012317 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000012318 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012319 exception);
glennrp44c22972015-01-25 03:10:16 +000012320 if (status == MagickFalse)
12321 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12322
cristy3ed852e2009-09-05 21:47:34 +000012323 if (logging != MagickFalse)
12324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12325 " Creating PNG blob.");
cristy3ed852e2009-09-05 21:47:34 +000012326
cristy151b66d2015-04-15 10:50:31 +000012327 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MagickPathExtent);
12328 (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000012329 jpeg_image_info->interlace=NoInterlace;
12330
glennrpcc5d45b2012-01-06 04:06:10 +000012331 /* Exclude all ancillary chunks */
12332 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12333
cristyb3f97ae2015-05-18 12:29:32 +000012334 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12335 &length,exception);
cristy3ed852e2009-09-05 21:47:34 +000012336
12337 /* Retrieve sample depth used */
cristy16ea1392012-03-21 20:38:41 +000012338 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000012339 if (value != (char *) NULL)
12340 jng_alpha_sample_depth= (unsigned int) value[0];
12341 }
12342 else
12343 {
cristy16ea1392012-03-21 20:38:41 +000012344 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000012345
12346 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012347 exception);
glennrp44c22972015-01-25 03:10:16 +000012348 if (status == MagickFalse)
12349 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12350
cristy3ed852e2009-09-05 21:47:34 +000012351
cristy151b66d2015-04-15 10:50:31 +000012352 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
12353 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000012354 jpeg_image_info->interlace=NoInterlace;
12355 if (logging != MagickFalse)
12356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12357 " Creating blob.");
cristyb3f97ae2015-05-18 12:29:32 +000012358 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000012359 exception);
cristy3ed852e2009-09-05 21:47:34 +000012360 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000012361
cristy3ed852e2009-09-05 21:47:34 +000012362 if (logging != MagickFalse)
12363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012364 " Successfully read jpeg_image into a blob, length=%.20g.",
12365 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012366
12367 }
12368 /* Destroy JPEG image and image_info */
12369 jpeg_image=DestroyImage(jpeg_image);
12370 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12371 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12372 }
12373
12374 /* Write JHDR chunk */
12375 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12376 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000012377 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000012378 PNGLong(chunk+4,(png_uint_32) image->columns);
12379 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012380 chunk[12]=jng_color_type;
12381 chunk[13]=8; /* sample depth */
12382 chunk[14]=8; /*jng_image_compression_method */
12383 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12384 chunk[16]=jng_alpha_sample_depth;
12385 chunk[17]=jng_alpha_compression_method;
12386 chunk[18]=0; /*jng_alpha_filter_method */
12387 chunk[19]=0; /*jng_alpha_interlace_method */
12388 (void) WriteBlob(image,20,chunk);
12389 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12390 if (logging != MagickFalse)
12391 {
12392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000012393 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000012394
cristy3ed852e2009-09-05 21:47:34 +000012395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000012396 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000012397
cristy3ed852e2009-09-05 21:47:34 +000012398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12399 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000012400
cristy3ed852e2009-09-05 21:47:34 +000012401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12402 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000012403
cristy3ed852e2009-09-05 21:47:34 +000012404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12405 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000012406
cristy3ed852e2009-09-05 21:47:34 +000012407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12408 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000012409
cristy3ed852e2009-09-05 21:47:34 +000012410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12411 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000012412
cristy3ed852e2009-09-05 21:47:34 +000012413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12414 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000012415
cristy3ed852e2009-09-05 21:47:34 +000012416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12417 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000012418
cristy3ed852e2009-09-05 21:47:34 +000012419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12420 " JNG alpha interlace:%5d",0);
12421 }
12422
glennrp0fe50b42010-11-16 03:52:51 +000012423 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000012424 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000012425
12426 /*
12427 Write leading ancillary chunks
12428 */
12429
glennrp8fe91592014-10-25 13:57:25 +000012430 if (transparent != 0)
cristy3ed852e2009-09-05 21:47:34 +000012431 {
12432 /*
12433 Write JNG bKGD chunk
12434 */
12435
12436 unsigned char
12437 blue,
12438 green,
12439 red;
12440
cristybb503372010-05-27 20:51:26 +000012441 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012442 num_bytes;
12443
12444 if (jng_color_type == 8 || jng_color_type == 12)
12445 num_bytes=6L;
12446 else
12447 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000012448 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012449 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012450 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012451 red=ScaleQuantumToChar(image->background_color.red);
12452 green=ScaleQuantumToChar(image->background_color.green);
12453 blue=ScaleQuantumToChar(image->background_color.blue);
12454 *(chunk+4)=0;
12455 *(chunk+5)=red;
12456 *(chunk+6)=0;
12457 *(chunk+7)=green;
12458 *(chunk+8)=0;
12459 *(chunk+9)=blue;
12460 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12461 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12462 }
12463
12464 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12465 {
12466 /*
12467 Write JNG sRGB chunk
12468 */
12469 (void) WriteBlobMSBULong(image,1L);
12470 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012471 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012472
cristy3ed852e2009-09-05 21:47:34 +000012473 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012474 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012475 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012476 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012477
cristy3ed852e2009-09-05 21:47:34 +000012478 else
glennrpe610a072010-08-05 17:08:46 +000012479 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012480 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012481 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012482
cristy3ed852e2009-09-05 21:47:34 +000012483 (void) WriteBlob(image,5,chunk);
12484 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12485 }
12486 else
12487 {
12488 if (image->gamma != 0.0)
12489 {
12490 /*
12491 Write JNG gAMA chunk
12492 */
12493 (void) WriteBlobMSBULong(image,4L);
12494 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012495 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012496 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012497 (void) WriteBlob(image,8,chunk);
12498 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12499 }
glennrp0fe50b42010-11-16 03:52:51 +000012500
cristy3ed852e2009-09-05 21:47:34 +000012501 if ((mng_info->equal_chrms == MagickFalse) &&
12502 (image->chromaticity.red_primary.x != 0.0))
12503 {
12504 PrimaryInfo
12505 primary;
12506
12507 /*
12508 Write JNG cHRM chunk
12509 */
12510 (void) WriteBlobMSBULong(image,32L);
12511 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012512 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012513 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012514 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12515 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012516 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012517 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12518 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012519 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012520 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12521 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012522 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012523 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12524 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012525 (void) WriteBlob(image,36,chunk);
12526 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12527 }
12528 }
glennrp0fe50b42010-11-16 03:52:51 +000012529
cristy16ea1392012-03-21 20:38:41 +000012530 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012531 {
12532 /*
12533 Write JNG pHYs chunk
12534 */
12535 (void) WriteBlobMSBULong(image,9L);
12536 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012537 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000012538 if (image->units == PixelsPerInchResolution)
12539 {
cristy35ef8242010-06-03 16:24:13 +000012540 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012541 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012542
cristy35ef8242010-06-03 16:24:13 +000012543 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012544 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012545
cristy3ed852e2009-09-05 21:47:34 +000012546 chunk[12]=1;
12547 }
glennrp0fe50b42010-11-16 03:52:51 +000012548
cristy3ed852e2009-09-05 21:47:34 +000012549 else
12550 {
12551 if (image->units == PixelsPerCentimeterResolution)
12552 {
cristy35ef8242010-06-03 16:24:13 +000012553 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012554 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012555
cristy35ef8242010-06-03 16:24:13 +000012556 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012557 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012558
cristy3ed852e2009-09-05 21:47:34 +000012559 chunk[12]=1;
12560 }
glennrp0fe50b42010-11-16 03:52:51 +000012561
cristy3ed852e2009-09-05 21:47:34 +000012562 else
12563 {
cristy16ea1392012-03-21 20:38:41 +000012564 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12565 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012566 chunk[12]=0;
12567 }
12568 }
12569 (void) WriteBlob(image,13,chunk);
12570 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12571 }
12572
12573 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12574 {
12575 /*
12576 Write JNG oFFs chunk
12577 */
12578 (void) WriteBlobMSBULong(image,9L);
12579 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000012580 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000012581 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12582 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000012583 chunk[12]=0;
12584 (void) WriteBlob(image,13,chunk);
12585 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12586 }
12587 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12588 {
12589 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12590 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000012591 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012592 PNGLong(chunk+4,(png_uint_32) image->page.width);
12593 PNGLong(chunk+8,(png_uint_32) image->page.height);
12594 chunk[12]=0; /* unit = pixels */
12595 (void) WriteBlob(image,13,chunk);
12596 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12597 }
12598
glennrp8fe91592014-10-25 13:57:25 +000012599 if (transparent != 0)
cristy3ed852e2009-09-05 21:47:34 +000012600 {
12601 if (jng_alpha_compression_method==0)
12602 {
cristybb503372010-05-27 20:51:26 +000012603 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012604 i;
12605
cristyfa6de8c2014-05-18 16:34:44 +000012606 size_t
cristy3ed852e2009-09-05 21:47:34 +000012607 len;
12608
12609 /* Write IDAT chunk header */
12610 if (logging != MagickFalse)
12611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012612 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012613 length);
cristy3ed852e2009-09-05 21:47:34 +000012614
12615 /* Copy IDAT chunks */
12616 len=0;
12617 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012618 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012619 {
cristy5389e5a2014-05-28 14:22:43 +000012620 len=(size_t) (*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
cristy3ed852e2009-09-05 21:47:34 +000012621 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012622
cristy3ed852e2009-09-05 21:47:34 +000012623 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12624 {
12625 /* Found an IDAT chunk. */
cristyfa6de8c2014-05-18 16:34:44 +000012626 (void) WriteBlobMSBULong(image,len);
12627 LogPNGChunk(logging,mng_IDAT,len);
12628 (void) WriteBlob(image,len+4,p);
12629 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
cristy3ed852e2009-09-05 21:47:34 +000012630 }
glennrp0fe50b42010-11-16 03:52:51 +000012631
cristy3ed852e2009-09-05 21:47:34 +000012632 else
12633 {
12634 if (logging != MagickFalse)
12635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012636 " Skipping %c%c%c%c chunk, length=%.20g.",
12637 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012638 }
12639 p+=(8+len);
12640 }
12641 }
glennrp7e99a062015-02-07 16:30:52 +000012642 else if (length != 0)
cristy3ed852e2009-09-05 21:47:34 +000012643 {
12644 /* Write JDAA chunk header */
12645 if (logging != MagickFalse)
12646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012647 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012648 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012649 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012650 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012651 /* Write JDAT chunk(s) data */
12652 (void) WriteBlob(image,4,chunk);
12653 (void) WriteBlob(image,length,blob);
12654 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12655 (uInt) length));
12656 }
12657 blob=(unsigned char *) RelinquishMagickMemory(blob);
12658 }
12659
12660 /* Encode image as a JPEG blob */
12661 if (logging != MagickFalse)
12662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12663 " Creating jpeg_image_info.");
12664 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12665 if (jpeg_image_info == (ImageInfo *) NULL)
12666 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12667
12668 if (logging != MagickFalse)
12669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12670 " Creating jpeg_image.");
12671
cristy16ea1392012-03-21 20:38:41 +000012672 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012673 if (jpeg_image == (Image *) NULL)
12674 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy151b66d2015-04-15 10:50:31 +000012675 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +000012676
12677 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy151b66d2015-04-15 10:50:31 +000012678 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012679 jpeg_image->filename);
12680
12681 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012682 exception);
cristy3ed852e2009-09-05 21:47:34 +000012683
12684 if (logging != MagickFalse)
12685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012686 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12687 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012688
glennrp44c22972015-01-25 03:10:16 +000012689 if (status == MagickFalse)
12690 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12691
cristy3ed852e2009-09-05 21:47:34 +000012692 if (jng_color_type == 8 || jng_color_type == 12)
12693 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012694
glennrp59575fa2011-12-31 21:31:39 +000012695 jpeg_image_info->quality=jng_quality;
12696 jpeg_image->quality=jng_quality;
cristy151b66d2015-04-15 10:50:31 +000012697 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
12698 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012699
cristy3ed852e2009-09-05 21:47:34 +000012700 if (logging != MagickFalse)
12701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12702 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012703
cristyb3f97ae2015-05-18 12:29:32 +000012704 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
12705 exception);
glennrp0fe50b42010-11-16 03:52:51 +000012706
cristy3ed852e2009-09-05 21:47:34 +000012707 if (logging != MagickFalse)
12708 {
12709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012710 " Successfully read jpeg_image into a blob, length=%.20g.",
12711 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012712
12713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012714 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012715 }
glennrp0fe50b42010-11-16 03:52:51 +000012716
cristy3ed852e2009-09-05 21:47:34 +000012717 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012718 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012719 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012720 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012721 (void) WriteBlob(image,4,chunk);
12722 (void) WriteBlob(image,length,blob);
12723 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12724
12725 jpeg_image=DestroyImage(jpeg_image);
12726 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12727 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12728 blob=(unsigned char *) RelinquishMagickMemory(blob);
12729
12730 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012731 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012732
12733 /* Write IEND chunk */
12734 (void) WriteBlobMSBULong(image,0L);
12735 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012736 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012737 (void) WriteBlob(image,4,chunk);
12738 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12739
12740 if (logging != MagickFalse)
12741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12742 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012743
cristy3ed852e2009-09-05 21:47:34 +000012744 return(status);
12745}
12746
12747
12748/*
12749%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12750% %
12751% %
12752% %
12753% W r i t e J N G I m a g e %
12754% %
12755% %
12756% %
12757%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12758%
12759% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12760%
12761% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12762%
12763% The format of the WriteJNGImage method is:
12764%
cristy16ea1392012-03-21 20:38:41 +000012765% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12766% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012767%
12768% A description of each parameter follows:
12769%
12770% o image_info: the image info.
12771%
12772% o image: The image.
12773%
cristy16ea1392012-03-21 20:38:41 +000012774% o exception: return any errors or warnings in this structure.
12775%
cristy3ed852e2009-09-05 21:47:34 +000012776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12777*/
cristy16ea1392012-03-21 20:38:41 +000012778static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12779 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012780{
12781 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012782 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012783 logging,
cristy3ed852e2009-09-05 21:47:34 +000012784 status;
12785
12786 MngInfo
12787 *mng_info;
12788
cristy3ed852e2009-09-05 21:47:34 +000012789 /*
12790 Open image file.
12791 */
12792 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +000012793 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +000012794 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +000012795 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +000012796 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012797 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012798 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012799 if (status == MagickFalse)
12800 return(status);
12801
12802 /*
12803 Allocate a MngInfo structure.
12804 */
12805 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012806 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012807 if (mng_info == (MngInfo *) NULL)
12808 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12809 /*
12810 Initialize members of the MngInfo structure.
12811 */
12812 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12813 mng_info->image=image;
12814 have_mng_structure=MagickTrue;
12815
12816 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12817
cristy16ea1392012-03-21 20:38:41 +000012818 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012819 (void) CloseBlob(image);
12820
12821 (void) CatchImageException(image);
12822 MngInfoFreeStruct(mng_info,&have_mng_structure);
12823 if (logging != MagickFalse)
12824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12825 return(status);
12826}
12827#endif
12828
cristy16ea1392012-03-21 20:38:41 +000012829static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12830 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012831{
12832 const char
12833 *option;
12834
12835 Image
12836 *next_image;
12837
12838 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012839 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012840 status;
12841
glennrp03812ae2010-12-24 01:31:34 +000012842 volatile MagickBooleanType
12843 logging;
12844
cristy3ed852e2009-09-05 21:47:34 +000012845 MngInfo
12846 *mng_info;
12847
12848 int
cristy3ed852e2009-09-05 21:47:34 +000012849 image_count,
12850 need_iterations,
12851 need_matte;
12852
12853 volatile int
12854#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12855 defined(PNG_MNG_FEATURES_SUPPORTED)
12856 need_local_plte,
12857#endif
12858 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012859 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012860 use_global_plte;
12861
cristybb503372010-05-27 20:51:26 +000012862 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012863 i;
12864
12865 unsigned char
12866 chunk[800];
12867
12868 volatile unsigned int
12869 write_jng,
12870 write_mng;
12871
cristybb503372010-05-27 20:51:26 +000012872 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012873 scene;
12874
cristybb503372010-05-27 20:51:26 +000012875 size_t
cristy3ed852e2009-09-05 21:47:34 +000012876 final_delay=0,
12877 initial_delay;
12878
glennrpd5045b42010-03-24 12:40:35 +000012879#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012880 if (image_info->verbose)
12881 printf("Your PNG library (libpng-%s) is rather old.\n",
12882 PNG_LIBPNG_VER_STRING);
12883#endif
12884
12885 /*
12886 Open image file.
12887 */
12888 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +000012889 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +000012890 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +000012891 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +000012892 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012893 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012894 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012895 if (status == MagickFalse)
12896 return(status);
12897
12898 /*
12899 Allocate a MngInfo structure.
12900 */
12901 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012902 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012903 if (mng_info == (MngInfo *) NULL)
12904 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12905 /*
12906 Initialize members of the MngInfo structure.
12907 */
12908 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12909 mng_info->image=image;
12910 have_mng_structure=MagickTrue;
12911 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12912
12913 /*
12914 * See if user has requested a specific PNG subformat to be used
12915 * for all of the PNGs in the MNG being written, e.g.,
12916 *
12917 * convert *.png png8:animation.mng
12918 *
12919 * To do: check -define png:bit_depth and png:color_type as well,
12920 * or perhaps use mng:bit_depth and mng:color_type instead for
12921 * global settings.
12922 */
12923
12924 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12925 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12926 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12927
12928 write_jng=MagickFalse;
12929 if (image_info->compression == JPEGCompression)
12930 write_jng=MagickTrue;
12931
12932 mng_info->adjoin=image_info->adjoin &&
12933 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12934
cristy3ed852e2009-09-05 21:47:34 +000012935 if (logging != MagickFalse)
12936 {
12937 /* Log some info about the input */
12938 Image
12939 *p;
12940
12941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +000012942 " Checking input image(s)\n"
12943 " Image_info depth: %.20g, Type: %d",
12944 (double) image_info->depth, image_info->type);
cristy3ed852e2009-09-05 21:47:34 +000012945
12946 scene=0;
12947 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12948 {
glennrp0fe50b42010-11-16 03:52:51 +000012949
cristy3ed852e2009-09-05 21:47:34 +000012950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp280283d2014-08-01 15:02:04 +000012951 " Scene: %.20g\n, Image depth: %.20g",
12952 (double) scene++, (double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012953
cristy17f11b02014-12-20 19:37:04 +000012954 if (p->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000012955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12956 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012957
cristy3ed852e2009-09-05 21:47:34 +000012958 else
12959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12960 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012961
cristy3ed852e2009-09-05 21:47:34 +000012962 if (p->storage_class == PseudoClass)
12963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12964 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012965
cristy3ed852e2009-09-05 21:47:34 +000012966 else
12967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12968 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012969
cristy3ed852e2009-09-05 21:47:34 +000012970 if (p->colors)
12971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012972 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012973
cristy3ed852e2009-09-05 21:47:34 +000012974 else
12975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12976 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012977
cristy3ed852e2009-09-05 21:47:34 +000012978 if (mng_info->adjoin == MagickFalse)
12979 break;
12980 }
12981 }
12982
cristy3ed852e2009-09-05 21:47:34 +000012983 use_global_plte=MagickFalse;
12984 all_images_are_gray=MagickFalse;
12985#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12986 need_local_plte=MagickTrue;
12987#endif
12988 need_defi=MagickFalse;
12989 need_matte=MagickFalse;
12990 mng_info->framing_mode=1;
12991 mng_info->old_framing_mode=1;
12992
12993 if (write_mng)
12994 if (image_info->page != (char *) NULL)
12995 {
12996 /*
12997 Determine image bounding box.
12998 */
12999 SetGeometry(image,&mng_info->page);
13000 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13001 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13002 }
13003 if (write_mng)
13004 {
13005 unsigned int
13006 need_geom;
13007
13008 unsigned short
13009 red,
13010 green,
13011 blue;
13012
13013 mng_info->page=image->page;
13014 need_geom=MagickTrue;
13015 if (mng_info->page.width || mng_info->page.height)
13016 need_geom=MagickFalse;
13017 /*
13018 Check all the scenes.
13019 */
13020 initial_delay=image->delay;
13021 need_iterations=MagickFalse;
13022 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13023 mng_info->equal_physs=MagickTrue,
13024 mng_info->equal_gammas=MagickTrue;
13025 mng_info->equal_srgbs=MagickTrue;
13026 mng_info->equal_backgrounds=MagickTrue;
13027 image_count=0;
13028#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13029 defined(PNG_MNG_FEATURES_SUPPORTED)
13030 all_images_are_gray=MagickTrue;
13031 mng_info->equal_palettes=MagickFalse;
13032 need_local_plte=MagickFalse;
13033#endif
13034 for (next_image=image; next_image != (Image *) NULL; )
13035 {
13036 if (need_geom)
13037 {
13038 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13039 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000013040
cristy3ed852e2009-09-05 21:47:34 +000013041 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13042 mng_info->page.height=next_image->rows+next_image->page.y;
13043 }
glennrp0fe50b42010-11-16 03:52:51 +000013044
cristy3ed852e2009-09-05 21:47:34 +000013045 if (next_image->page.x || next_image->page.y)
13046 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013047
cristy17f11b02014-12-20 19:37:04 +000013048 if (next_image->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000013049 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013050
cristy3ed852e2009-09-05 21:47:34 +000013051 if ((int) next_image->dispose >= BackgroundDispose)
cristy17f11b02014-12-20 19:37:04 +000013052 if ((next_image->alpha_trait != UndefinedPixelTrait) ||
cristydc2d3272013-02-12 14:00:44 +000013053 next_image->page.x || next_image->page.y ||
cristy3ed852e2009-09-05 21:47:34 +000013054 ((next_image->columns < mng_info->page.width) &&
13055 (next_image->rows < mng_info->page.height)))
13056 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013057
cristy3ed852e2009-09-05 21:47:34 +000013058 if (next_image->iterations)
13059 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013060
cristy3ed852e2009-09-05 21:47:34 +000013061 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000013062
cristy3ed852e2009-09-05 21:47:34 +000013063 if (final_delay != initial_delay || final_delay > 1UL*
13064 next_image->ticks_per_second)
13065 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000013066
cristy3ed852e2009-09-05 21:47:34 +000013067#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13068 defined(PNG_MNG_FEATURES_SUPPORTED)
13069 /*
13070 check for global palette possibility.
13071 */
cristy17f11b02014-12-20 19:37:04 +000013072 if (image->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000013073 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013074
cristy3ed852e2009-09-05 21:47:34 +000013075 if (need_local_plte == 0)
13076 {
dirkf1d85482015-04-06 00:36:00 +000013077 if (SetImageGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013078 all_images_are_gray=MagickFalse;
13079 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13080 if (use_global_plte == 0)
13081 use_global_plte=mng_info->equal_palettes;
13082 need_local_plte=!mng_info->equal_palettes;
13083 }
13084#endif
13085 if (GetNextImageInList(next_image) != (Image *) NULL)
13086 {
13087 if (next_image->background_color.red !=
13088 next_image->next->background_color.red ||
13089 next_image->background_color.green !=
13090 next_image->next->background_color.green ||
13091 next_image->background_color.blue !=
13092 next_image->next->background_color.blue)
13093 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013094
cristy3ed852e2009-09-05 21:47:34 +000013095 if (next_image->gamma != next_image->next->gamma)
13096 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013097
cristy3ed852e2009-09-05 21:47:34 +000013098 if (next_image->rendering_intent !=
13099 next_image->next->rendering_intent)
13100 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013101
cristy3ed852e2009-09-05 21:47:34 +000013102 if ((next_image->units != next_image->next->units) ||
cristy16ea1392012-03-21 20:38:41 +000013103 (next_image->resolution.x != next_image->next->resolution.x) ||
13104 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000013105 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013106
cristy3ed852e2009-09-05 21:47:34 +000013107 if (mng_info->equal_chrms)
13108 {
13109 if (next_image->chromaticity.red_primary.x !=
13110 next_image->next->chromaticity.red_primary.x ||
13111 next_image->chromaticity.red_primary.y !=
13112 next_image->next->chromaticity.red_primary.y ||
13113 next_image->chromaticity.green_primary.x !=
13114 next_image->next->chromaticity.green_primary.x ||
13115 next_image->chromaticity.green_primary.y !=
13116 next_image->next->chromaticity.green_primary.y ||
13117 next_image->chromaticity.blue_primary.x !=
13118 next_image->next->chromaticity.blue_primary.x ||
13119 next_image->chromaticity.blue_primary.y !=
13120 next_image->next->chromaticity.blue_primary.y ||
13121 next_image->chromaticity.white_point.x !=
13122 next_image->next->chromaticity.white_point.x ||
13123 next_image->chromaticity.white_point.y !=
13124 next_image->next->chromaticity.white_point.y)
13125 mng_info->equal_chrms=MagickFalse;
13126 }
13127 }
13128 image_count++;
13129 next_image=GetNextImageInList(next_image);
13130 }
13131 if (image_count < 2)
13132 {
13133 mng_info->equal_backgrounds=MagickFalse;
13134 mng_info->equal_chrms=MagickFalse;
13135 mng_info->equal_gammas=MagickFalse;
13136 mng_info->equal_srgbs=MagickFalse;
13137 mng_info->equal_physs=MagickFalse;
13138 use_global_plte=MagickFalse;
13139#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13140 need_local_plte=MagickTrue;
13141#endif
13142 need_iterations=MagickFalse;
13143 }
glennrp0fe50b42010-11-16 03:52:51 +000013144
cristy3ed852e2009-09-05 21:47:34 +000013145 if (mng_info->need_fram == MagickFalse)
13146 {
13147 /*
13148 Only certain framing rates 100/n are exactly representable without
13149 the FRAM chunk but we'll allow some slop in VLC files
13150 */
13151 if (final_delay == 0)
13152 {
13153 if (need_iterations != MagickFalse)
13154 {
13155 /*
13156 It's probably a GIF with loop; don't run it *too* fast.
13157 */
glennrp02617122010-07-28 13:07:35 +000013158 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000013159 {
13160 final_delay=10;
cristy16ea1392012-03-21 20:38:41 +000013161 (void) ThrowMagickException(exception,GetMagickModule(),
13162 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000013163 "input has zero delay between all frames; assuming",
13164 " 10 cs `%s'","");
13165 }
cristy3ed852e2009-09-05 21:47:34 +000013166 }
13167 else
13168 mng_info->ticks_per_second=0;
13169 }
13170 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000013171 mng_info->ticks_per_second=(png_uint_32)
13172 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000013173 if (final_delay > 50)
13174 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000013175
cristy3ed852e2009-09-05 21:47:34 +000013176 if (final_delay > 75)
13177 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000013178
cristy3ed852e2009-09-05 21:47:34 +000013179 if (final_delay > 125)
13180 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013181
cristy3ed852e2009-09-05 21:47:34 +000013182 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13183 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
cristyb9d46a12015-06-08 22:29:45 +000013184 (final_delay != 25) && (final_delay != 50) &&
dirk0bf21432015-06-22 11:13:20 +000013185 (final_delay != (size_t) image->ticks_per_second))
cristy3ed852e2009-09-05 21:47:34 +000013186 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13187 }
glennrp0fe50b42010-11-16 03:52:51 +000013188
cristy3ed852e2009-09-05 21:47:34 +000013189 if (mng_info->need_fram != MagickFalse)
cristyb9d46a12015-06-08 22:29:45 +000013190 mng_info->ticks_per_second=image->ticks_per_second;
cristy3ed852e2009-09-05 21:47:34 +000013191 /*
13192 If pseudocolor, we should also check to see if all the
13193 palettes are identical and write a global PLTE if they are.
13194 ../glennrp Feb 99.
13195 */
13196 /*
13197 Write the MNG version 1.0 signature and MHDR chunk.
13198 */
13199 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13200 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13201 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000013202 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000013203 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13204 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000013205 PNGLong(chunk+12,mng_info->ticks_per_second);
13206 PNGLong(chunk+16,0L); /* layer count=unknown */
13207 PNGLong(chunk+20,0L); /* frame count=unknown */
13208 PNGLong(chunk+24,0L); /* play time=unknown */
13209 if (write_jng)
13210 {
13211 if (need_matte)
13212 {
13213 if (need_defi || mng_info->need_fram || use_global_plte)
13214 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000013215
cristy3ed852e2009-09-05 21:47:34 +000013216 else
13217 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13218 }
glennrp0fe50b42010-11-16 03:52:51 +000013219
cristy3ed852e2009-09-05 21:47:34 +000013220 else
13221 {
13222 if (need_defi || mng_info->need_fram || use_global_plte)
13223 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000013224
cristy3ed852e2009-09-05 21:47:34 +000013225 else
13226 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13227 }
13228 }
glennrp0fe50b42010-11-16 03:52:51 +000013229
cristy3ed852e2009-09-05 21:47:34 +000013230 else
13231 {
13232 if (need_matte)
13233 {
13234 if (need_defi || mng_info->need_fram || use_global_plte)
13235 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000013236
cristy3ed852e2009-09-05 21:47:34 +000013237 else
13238 PNGLong(chunk+28,9L); /* simplicity=VLC */
13239 }
glennrp0fe50b42010-11-16 03:52:51 +000013240
cristy3ed852e2009-09-05 21:47:34 +000013241 else
13242 {
13243 if (need_defi || mng_info->need_fram || use_global_plte)
13244 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000013245
cristy3ed852e2009-09-05 21:47:34 +000013246 else
13247 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13248 }
13249 }
13250 (void) WriteBlob(image,32,chunk);
13251 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
cristy092ec8d2013-04-26 13:46:22 +000013252 option=GetImageOption(image_info,"mng:need-cacheoff");
cristy3ed852e2009-09-05 21:47:34 +000013253 if (option != (const char *) NULL)
13254 {
13255 size_t
13256 length;
13257
13258 /*
13259 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13260 */
13261 PNGType(chunk,mng_nEED);
13262 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000013263 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000013264 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000013265 length+=4;
13266 (void) WriteBlob(image,length,chunk);
13267 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13268 }
13269 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13270 (GetNextImageInList(image) != (Image *) NULL) &&
13271 (image->iterations != 1))
13272 {
13273 /*
13274 Write MNG TERM chunk
13275 */
13276 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13277 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000013278 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013279 chunk[4]=3; /* repeat animation */
13280 chunk[5]=0; /* show last frame when done */
13281 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13282 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000013283
cristy3ed852e2009-09-05 21:47:34 +000013284 if (image->iterations == 0)
13285 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000013286
cristy3ed852e2009-09-05 21:47:34 +000013287 else
13288 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000013289
cristy3ed852e2009-09-05 21:47:34 +000013290 if (logging != MagickFalse)
13291 {
13292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013293 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13294 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000013295
cristy3ed852e2009-09-05 21:47:34 +000013296 if (image->iterations == 0)
13297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013298 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000013299
cristy3ed852e2009-09-05 21:47:34 +000013300 else
13301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013302 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000013303 }
13304 (void) WriteBlob(image,14,chunk);
13305 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13306 }
13307 /*
13308 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13309 */
13310 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13311 mng_info->equal_srgbs)
13312 {
13313 /*
13314 Write MNG sRGB chunk
13315 */
13316 (void) WriteBlobMSBULong(image,1L);
13317 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000013318 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000013319
cristy3ed852e2009-09-05 21:47:34 +000013320 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000013321 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000013322 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000013323 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000013324
cristy3ed852e2009-09-05 21:47:34 +000013325 else
glennrpe610a072010-08-05 17:08:46 +000013326 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000013327 Magick_RenderingIntent_to_PNG_RenderingIntent(
13328 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000013329
cristy3ed852e2009-09-05 21:47:34 +000013330 (void) WriteBlob(image,5,chunk);
13331 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13332 mng_info->have_write_global_srgb=MagickTrue;
13333 }
glennrp0fe50b42010-11-16 03:52:51 +000013334
cristy3ed852e2009-09-05 21:47:34 +000013335 else
13336 {
13337 if (image->gamma && mng_info->equal_gammas)
13338 {
13339 /*
13340 Write MNG gAMA chunk
13341 */
13342 (void) WriteBlobMSBULong(image,4L);
13343 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000013344 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000013345 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013346 (void) WriteBlob(image,8,chunk);
13347 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13348 mng_info->have_write_global_gama=MagickTrue;
13349 }
13350 if (mng_info->equal_chrms)
13351 {
13352 PrimaryInfo
13353 primary;
13354
13355 /*
13356 Write MNG cHRM chunk
13357 */
13358 (void) WriteBlobMSBULong(image,32L);
13359 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000013360 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000013361 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000013362 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13363 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013364 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000013365 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13366 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013367 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000013368 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13369 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013370 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000013371 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13372 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013373 (void) WriteBlob(image,36,chunk);
13374 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13375 mng_info->have_write_global_chrm=MagickTrue;
13376 }
13377 }
cristy16ea1392012-03-21 20:38:41 +000013378 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000013379 {
13380 /*
13381 Write MNG pHYs chunk
13382 */
13383 (void) WriteBlobMSBULong(image,9L);
13384 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000013385 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000013386
cristy3ed852e2009-09-05 21:47:34 +000013387 if (image->units == PixelsPerInchResolution)
13388 {
cristy35ef8242010-06-03 16:24:13 +000013389 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013390 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013391
cristy35ef8242010-06-03 16:24:13 +000013392 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013393 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013394
cristy3ed852e2009-09-05 21:47:34 +000013395 chunk[12]=1;
13396 }
glennrp0fe50b42010-11-16 03:52:51 +000013397
cristy3ed852e2009-09-05 21:47:34 +000013398 else
13399 {
13400 if (image->units == PixelsPerCentimeterResolution)
13401 {
cristy35ef8242010-06-03 16:24:13 +000013402 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013403 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013404
cristy35ef8242010-06-03 16:24:13 +000013405 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013406 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013407
cristy3ed852e2009-09-05 21:47:34 +000013408 chunk[12]=1;
13409 }
glennrp0fe50b42010-11-16 03:52:51 +000013410
cristy3ed852e2009-09-05 21:47:34 +000013411 else
13412 {
cristy16ea1392012-03-21 20:38:41 +000013413 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13414 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013415 chunk[12]=0;
13416 }
13417 }
13418 (void) WriteBlob(image,13,chunk);
13419 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13420 }
13421 /*
13422 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13423 or does not cover the entire frame.
13424 */
cristy17f11b02014-12-20 19:37:04 +000013425 if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
cristydc2d3272013-02-12 14:00:44 +000013426 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
cristy3ed852e2009-09-05 21:47:34 +000013427 (image->page.width+image->page.x < mng_info->page.width))
13428 || (image->page.height && (image->page.height+image->page.y
13429 < mng_info->page.height))))
13430 {
13431 (void) WriteBlobMSBULong(image,6L);
13432 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000013433 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000013434 red=ScaleQuantumToShort(image->background_color.red);
13435 green=ScaleQuantumToShort(image->background_color.green);
13436 blue=ScaleQuantumToShort(image->background_color.blue);
13437 PNGShort(chunk+4,red);
13438 PNGShort(chunk+6,green);
13439 PNGShort(chunk+8,blue);
13440 (void) WriteBlob(image,10,chunk);
13441 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13442 if (mng_info->equal_backgrounds)
13443 {
13444 (void) WriteBlobMSBULong(image,6L);
13445 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000013446 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000013447 (void) WriteBlob(image,10,chunk);
13448 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13449 }
13450 }
13451
13452#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13453 if ((need_local_plte == MagickFalse) &&
13454 (image->storage_class == PseudoClass) &&
13455 (all_images_are_gray == MagickFalse))
13456 {
cristybb503372010-05-27 20:51:26 +000013457 size_t
cristy3ed852e2009-09-05 21:47:34 +000013458 data_length;
13459
13460 /*
13461 Write MNG PLTE chunk
13462 */
13463 data_length=3*image->colors;
13464 (void) WriteBlobMSBULong(image,data_length);
13465 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013466 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013467
cristybb503372010-05-27 20:51:26 +000013468 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013469 {
cristy16ea1392012-03-21 20:38:41 +000013470 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13471 image->colormap[i].red) & 0xff);
13472 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13473 image->colormap[i].green) & 0xff);
13474 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13475 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000013476 }
glennrp0fe50b42010-11-16 03:52:51 +000013477
cristy3ed852e2009-09-05 21:47:34 +000013478 (void) WriteBlob(image,data_length+4,chunk);
13479 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13480 mng_info->have_write_global_plte=MagickTrue;
13481 }
13482#endif
13483 }
13484 scene=0;
13485 mng_info->delay=0;
13486#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13487 defined(PNG_MNG_FEATURES_SUPPORTED)
13488 mng_info->equal_palettes=MagickFalse;
13489#endif
13490 do
13491 {
13492 if (mng_info->adjoin)
13493 {
13494#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13495 defined(PNG_MNG_FEATURES_SUPPORTED)
13496 /*
13497 If we aren't using a global palette for the entire MNG, check to
13498 see if we can use one for two or more consecutive images.
13499 */
13500 if (need_local_plte && use_global_plte && !all_images_are_gray)
13501 {
13502 if (mng_info->IsPalette)
13503 {
13504 /*
13505 When equal_palettes is true, this image has the same palette
13506 as the previous PseudoClass image
13507 */
13508 mng_info->have_write_global_plte=mng_info->equal_palettes;
13509 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13510 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13511 {
13512 /*
13513 Write MNG PLTE chunk
13514 */
cristybb503372010-05-27 20:51:26 +000013515 size_t
cristy3ed852e2009-09-05 21:47:34 +000013516 data_length;
13517
13518 data_length=3*image->colors;
13519 (void) WriteBlobMSBULong(image,data_length);
13520 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013521 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013522
cristybb503372010-05-27 20:51:26 +000013523 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013524 {
13525 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13526 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13527 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13528 }
glennrp0fe50b42010-11-16 03:52:51 +000013529
cristy3ed852e2009-09-05 21:47:34 +000013530 (void) WriteBlob(image,data_length+4,chunk);
13531 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13532 (uInt) (data_length+4)));
13533 mng_info->have_write_global_plte=MagickTrue;
13534 }
13535 }
13536 else
13537 mng_info->have_write_global_plte=MagickFalse;
13538 }
13539#endif
13540 if (need_defi)
13541 {
cristybb503372010-05-27 20:51:26 +000013542 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000013543 previous_x,
13544 previous_y;
13545
13546 if (scene)
13547 {
13548 previous_x=mng_info->page.x;
13549 previous_y=mng_info->page.y;
13550 }
13551 else
13552 {
13553 previous_x=0;
13554 previous_y=0;
13555 }
13556 mng_info->page=image->page;
13557 if ((mng_info->page.x != previous_x) ||
13558 (mng_info->page.y != previous_y))
13559 {
13560 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13561 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000013562 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000013563 chunk[4]=0; /* object 0 MSB */
13564 chunk[5]=0; /* object 0 LSB */
13565 chunk[6]=0; /* visible */
13566 chunk[7]=0; /* abstract */
13567 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13568 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13569 (void) WriteBlob(image,16,chunk);
13570 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13571 }
13572 }
13573 }
13574
13575 mng_info->write_mng=write_mng;
13576
13577 if ((int) image->dispose >= 3)
13578 mng_info->framing_mode=3;
13579
13580 if (mng_info->need_fram && mng_info->adjoin &&
13581 ((image->delay != mng_info->delay) ||
13582 (mng_info->framing_mode != mng_info->old_framing_mode)))
13583 {
13584 if (image->delay == mng_info->delay)
13585 {
13586 /*
13587 Write a MNG FRAM chunk with the new framing mode.
13588 */
13589 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13590 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013591 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013592 chunk[4]=(unsigned char) mng_info->framing_mode;
13593 (void) WriteBlob(image,5,chunk);
13594 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13595 }
13596 else
13597 {
13598 /*
13599 Write a MNG FRAM chunk with the delay.
13600 */
13601 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13602 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013603 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013604 chunk[4]=(unsigned char) mng_info->framing_mode;
13605 chunk[5]=0; /* frame name separator (no name) */
13606 chunk[6]=2; /* flag for changing default delay */
13607 chunk[7]=0; /* flag for changing frame timeout */
13608 chunk[8]=0; /* flag for changing frame clipping */
13609 chunk[9]=0; /* flag for changing frame sync_id */
13610 PNGLong(chunk+10,(png_uint_32)
13611 ((mng_info->ticks_per_second*
13612 image->delay)/MagickMax(image->ticks_per_second,1)));
13613 (void) WriteBlob(image,14,chunk);
13614 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013615 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013616 }
13617 mng_info->old_framing_mode=mng_info->framing_mode;
13618 }
13619
13620#if defined(JNG_SUPPORTED)
13621 if (image_info->compression == JPEGCompression)
13622 {
13623 ImageInfo
13624 *write_info;
13625
13626 if (logging != MagickFalse)
13627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13628 " Writing JNG object.");
13629 /* To do: specify the desired alpha compression method. */
13630 write_info=CloneImageInfo(image_info);
13631 write_info->compression=UndefinedCompression;
cristy16ea1392012-03-21 20:38:41 +000013632 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013633 write_info=DestroyImageInfo(write_info);
13634 }
13635 else
13636#endif
13637 {
13638 if (logging != MagickFalse)
13639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13640 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013641
glennrpb9cfe272010-12-21 15:08:06 +000013642 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013643 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013644
13645 /* We don't want any ancillary chunks written */
13646 mng_info->ping_exclude_bKGD=MagickTrue;
13647 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013648 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013649 mng_info->ping_exclude_EXIF=MagickTrue;
13650 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013651 mng_info->ping_exclude_iCCP=MagickTrue;
13652 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13653 mng_info->ping_exclude_oFFs=MagickTrue;
13654 mng_info->ping_exclude_pHYs=MagickTrue;
13655 mng_info->ping_exclude_sRGB=MagickTrue;
13656 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013657 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013658 mng_info->ping_exclude_vpAg=MagickTrue;
13659 mng_info->ping_exclude_zCCP=MagickTrue;
13660 mng_info->ping_exclude_zTXt=MagickTrue;
13661
cristy16ea1392012-03-21 20:38:41 +000013662 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013663 }
13664
13665 if (status == MagickFalse)
13666 {
13667 MngInfoFreeStruct(mng_info,&have_mng_structure);
13668 (void) CloseBlob(image);
13669 return(MagickFalse);
13670 }
13671 (void) CatchImageException(image);
13672 if (GetNextImageInList(image) == (Image *) NULL)
13673 break;
13674 image=SyncNextImageInList(image);
13675 status=SetImageProgress(image,SaveImagesTag,scene++,
13676 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013677
cristy3ed852e2009-09-05 21:47:34 +000013678 if (status == MagickFalse)
13679 break;
glennrp0fe50b42010-11-16 03:52:51 +000013680
cristy3ed852e2009-09-05 21:47:34 +000013681 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013682
cristy3ed852e2009-09-05 21:47:34 +000013683 if (write_mng)
13684 {
13685 while (GetPreviousImageInList(image) != (Image *) NULL)
13686 image=GetPreviousImageInList(image);
13687 /*
13688 Write the MEND chunk.
13689 */
13690 (void) WriteBlobMSBULong(image,0x00000000L);
13691 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013692 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013693 (void) WriteBlob(image,4,chunk);
13694 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13695 }
13696 /*
13697 Relinquish resources.
13698 */
13699 (void) CloseBlob(image);
13700 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013701
cristy3ed852e2009-09-05 21:47:34 +000013702 if (logging != MagickFalse)
13703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013704
cristy3ed852e2009-09-05 21:47:34 +000013705 return(MagickTrue);
13706}
glennrpd5045b42010-03-24 12:40:35 +000013707#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013708
cristy3ed852e2009-09-05 21:47:34 +000013709static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13710{
glennrp3bd393f2011-12-21 18:54:53 +000013711 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013712 printf("Your PNG library is too old: You have libpng-%s\n",
13713 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013714
cristy3ed852e2009-09-05 21:47:34 +000013715 ThrowBinaryException(CoderError,"PNG library is too old",
13716 image_info->filename);
13717}
glennrp39992b42010-11-14 00:03:43 +000013718
cristy3ed852e2009-09-05 21:47:34 +000013719static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13720{
13721 return(WritePNGImage(image_info,image));
13722}
glennrpd5045b42010-03-24 12:40:35 +000013723#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013724#endif