blob: 70d8ebda8597a7260707b6facbfe6347345a16e0 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP N N GGGG %
7% P P NN N G %
8% PPPP N N N G GG %
9% P N NN G G %
10% P N N GGG %
11% %
12% %
13% Read/Write Portable Network Graphics Image Format %
14% %
15% Software Design %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristyfe676ee2013-11-18 13:03:38 +000021% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40
41/*
42 Include declarations.
43*/
cristy16ea1392012-03-21 20:38:41 +000044#include "MagickCore/studio.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
cristyaa2c16c2012-03-25 22:21:35 +000050#include "MagickCore/channel.h"
cristy16ea1392012-03-21 20:38:41 +000051#include "MagickCore/color.h"
52#include "MagickCore/color-private.h"
53#include "MagickCore/colormap.h"
54#include "MagickCore/colorspace.h"
55#include "MagickCore/colorspace-private.h"
56#include "MagickCore/constitute.h"
57#include "MagickCore/enhance.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/geometry.h"
61#include "MagickCore/histogram.h"
62#include "MagickCore/image.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/layer.h"
65#include "MagickCore/list.h"
66#include "MagickCore/log.h"
67#include "MagickCore/MagickCore.h"
68#include "MagickCore/memory_.h"
69#include "MagickCore/module.h"
70#include "MagickCore/monitor.h"
71#include "MagickCore/monitor-private.h"
72#include "MagickCore/option.h"
73#include "MagickCore/pixel.h"
74#include "MagickCore/pixel-accessor.h"
75#include "MagickCore/profile.h"
76#include "MagickCore/property.h"
77#include "MagickCore/quantum-private.h"
78#include "MagickCore/resource_.h"
79#include "MagickCore/semaphore.h"
80#include "MagickCore/quantum-private.h"
81#include "MagickCore/static.h"
82#include "MagickCore/statistic.h"
83#include "MagickCore/string_.h"
84#include "MagickCore/string-private.h"
85#include "MagickCore/transform.h"
86#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000087#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000088
glennrp7ef138c2009-11-10 13:50:20 +000089/* Suppress libpng pedantic warnings that were added in
90 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000091 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000092 * fix any code that generates warnings.
93 */
glennrp991e92a2010-01-28 03:09:00 +000094/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000095/* #define PNG_USE_RESULT The result of this function must be checked */
96/* #define PNG_NORETURN This function does not return */
97/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000098/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000099
100/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000101#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000102
cristy3ed852e2009-09-05 21:47:34 +0000103#include "png.h"
104#include "zlib.h"
105
106/* ImageMagick differences */
107#define first_scene scene
108
glennrpd5045b42010-03-24 12:40:35 +0000109#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000110/*
111 Optional declarations. Define or undefine them as you like.
112*/
113/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
114
115/*
116 Features under construction. Define these to work on them.
117*/
118#undef MNG_OBJECT_BUFFERS
119#undef MNG_BASI_SUPPORTED
120#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000122#if defined(MAGICKCORE_JPEG_DELEGATE)
123# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
124#endif
125#if !defined(RGBColorMatchExact)
126#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000127 (((color).red == (target).red) && \
128 ((color).green == (target).green) && \
129 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000130#endif
131
glennrpecab7d72013-05-14 22:50:32 +0000132/* Table of recognized sRGB ICC profiles */
133struct sRGB_info_struct
134{
135 png_uint_32 len;
136 png_uint_32 crc;
137 png_byte intent;
138};
139
140const struct sRGB_info_struct sRGB_info[] =
141{
glennrpc241d3c2013-05-15 04:24:40 +0000142 /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
glennrpecab7d72013-05-14 22:50:32 +0000143 { 3048, 0x3b8772b9UL, 0},
144
glennrpc241d3c2013-05-15 04:24:40 +0000145 /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
glennrpecab7d72013-05-14 22:50:32 +0000146 { 3052, 0x427ebb21UL, 1},
147
glennrpc241d3c2013-05-15 04:24:40 +0000148 /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
glennrpecab7d72013-05-14 22:50:32 +0000149 {60988, 0x306fd8aeUL, 0},
150
glennrpc241d3c2013-05-15 04:24:40 +0000151 /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
glennrpecab7d72013-05-14 22:50:32 +0000152 {60960, 0xbbef7812UL, 0},
153
glennrpc241d3c2013-05-15 04:24:40 +0000154 /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
glennrpecab7d72013-05-14 22:50:32 +0000155 { 3024, 0x5d5129ceUL, 1},
156
157 /* HP-Microsoft sRGB v2 perceptual */
158 { 3144, 0x182ea552UL, 0},
159
160 /* HP-Microsoft sRGB v2 media-relative */
161 { 3144, 0xf29e526dUL, 1},
162
glennrpc241d3c2013-05-15 04:24:40 +0000163 /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
164 { 524, 0xd4938c39UL, 0},
165
166 /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
167 { 3212, 0x034af5a1UL, 0},
168
glennrpecab7d72013-05-14 22:50:32 +0000169 /* Not recognized */
170 { 0, 0x00000000UL, 0},
171};
172
glennrp8e58efd2011-05-20 12:16:29 +0000173/* Macros for left-bit-replication to ensure that pixels
cristy16ea1392012-03-21 20:38:41 +0000174 * and PixelInfos all have the same image->depth, and for use
glennrp8e58efd2011-05-20 12:16:29 +0000175 * in PNG8 quantization.
176 */
177
glennrp8e58efd2011-05-20 12:16:29 +0000178/* LBR01: Replicate top bit */
179
glennrp05001c32011-08-06 13:04:16 +0000180#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000181 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
182 0 : QuantumRange);
183
glennrp91d99252011-06-25 14:30:13 +0000184#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000185 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
186 0 : QuantumRange);
187
glennrp91d99252011-06-25 14:30:13 +0000188#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000189 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
190 0 : QuantumRange);
191
cristy16ea1392012-03-21 20:38:41 +0000192#define LBR01PacketAlpha(pixelpacket) \
193 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000194 0 : QuantumRange);
195
glennrp91d99252011-06-25 14:30:13 +0000196#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000197 { \
glennrp05001c32011-08-06 13:04:16 +0000198 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000199 LBR01PacketGreen((pixelpacket)); \
200 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000201 }
glennrp8e58efd2011-05-20 12:16:29 +0000202
glennrp91d99252011-06-25 14:30:13 +0000203#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000204 { \
glennrp91d99252011-06-25 14:30:13 +0000205 LBR01PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000206 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000207 }
glennrp8e58efd2011-05-20 12:16:29 +0000208
cristyef618312011-06-25 12:26:44 +0000209#define LBR01PixelRed(pixel) \
glennrp360c1542013-01-06 01:21:46 +0000210 (SetPixelRed(image, \
211 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrpc7375f92013-01-06 01:32:16 +0000212 0 : QuantumRange,(pixel)));
glennrp8e58efd2011-05-20 12:16:29 +0000213
glennrp54cf7972011-08-06 14:28:09 +0000214#define LBR01PixelGreen(pixel) \
glennrp360c1542013-01-06 01:21:46 +0000215 (SetPixelGreen(image, \
216 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrpc7375f92013-01-06 01:32:16 +0000217 0 : QuantumRange,(pixel)));
glennrp8e58efd2011-05-20 12:16:29 +0000218
glennrp54cf7972011-08-06 14:28:09 +0000219#define LBR01PixelBlue(pixel) \
glennrp360c1542013-01-06 01:21:46 +0000220 (SetPixelBlue(image, \
221 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrpc7375f92013-01-06 01:32:16 +0000222 0 : QuantumRange,(pixel)));
glennrp8e58efd2011-05-20 12:16:29 +0000223
cristy16ea1392012-03-21 20:38:41 +0000224#define LBR01PixelAlpha(pixel) \
glennrp360c1542013-01-06 01:21:46 +0000225 (SetPixelAlpha(image, \
226 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrpc7375f92013-01-06 01:32:16 +0000227 0 : QuantumRange,(pixel)));
glennrp8e58efd2011-05-20 12:16:29 +0000228
glennrp54cf7972011-08-06 14:28:09 +0000229#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000230 { \
cristyef618312011-06-25 12:26:44 +0000231 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000232 LBR01PixelGreen((pixel)); \
233 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000234 }
glennrp8e58efd2011-05-20 12:16:29 +0000235
cristy16ea1392012-03-21 20:38:41 +0000236#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000237 { \
glennrp54cf7972011-08-06 14:28:09 +0000238 LBR01PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000239 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000240 }
glennrp8e58efd2011-05-20 12:16:29 +0000241
242/* LBR02: Replicate top 2 bits */
243
glennrp05001c32011-08-06 13:04:16 +0000244#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000245 { \
246 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
247 (pixelpacket).red=ScaleCharToQuantum( \
248 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
249 }
glennrp91d99252011-06-25 14:30:13 +0000250#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000251 { \
252 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
253 (pixelpacket).green=ScaleCharToQuantum( \
254 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
255 }
glennrp91d99252011-06-25 14:30:13 +0000256#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000257 { \
258 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
259 (pixelpacket).blue=ScaleCharToQuantum( \
260 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
261 }
cristy16ea1392012-03-21 20:38:41 +0000262#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000263 { \
cristy16ea1392012-03-21 20:38:41 +0000264 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
265 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
267 }
268
glennrp91d99252011-06-25 14:30:13 +0000269#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000270 { \
glennrp05001c32011-08-06 13:04:16 +0000271 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000272 LBR02PacketGreen((pixelpacket)); \
273 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000274 }
glennrp8e58efd2011-05-20 12:16:29 +0000275
glennrp91d99252011-06-25 14:30:13 +0000276#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000277 { \
glennrp91d99252011-06-25 14:30:13 +0000278 LBR02PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000279 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000280 }
glennrp8e58efd2011-05-20 12:16:29 +0000281
cristyef618312011-06-25 12:26:44 +0000282#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000283 { \
cristy16ea1392012-03-21 20:38:41 +0000284 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000285 & 0xc0; \
cristy16ea1392012-03-21 20:38:41 +0000286 SetPixelRed(image, ScaleCharToQuantum( \
287 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
288 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000289 }
glennrp54cf7972011-08-06 14:28:09 +0000290#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000291 { \
cristy16ea1392012-03-21 20:38:41 +0000292 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000293 & 0xc0; \
cristy16ea1392012-03-21 20:38:41 +0000294 SetPixelGreen(image, ScaleCharToQuantum( \
295 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
296 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000297 }
glennrp54cf7972011-08-06 14:28:09 +0000298#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000299 { \
300 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000301 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
302 SetPixelBlue(image, ScaleCharToQuantum( \
303 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
304 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000305 }
cristy16ea1392012-03-21 20:38:41 +0000306#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000307 { \
308 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000309 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
310 SetPixelAlpha(image, ScaleCharToQuantum( \
311 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
312 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000313 }
314
glennrp54cf7972011-08-06 14:28:09 +0000315#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000316 { \
cristyef618312011-06-25 12:26:44 +0000317 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000318 LBR02PixelGreen((pixel)); \
319 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000320 }
glennrp8e58efd2011-05-20 12:16:29 +0000321
cristy16ea1392012-03-21 20:38:41 +0000322#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000323 { \
glennrp54cf7972011-08-06 14:28:09 +0000324 LBR02PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000325 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000326 }
glennrp8e58efd2011-05-20 12:16:29 +0000327
328/* LBR03: Replicate top 3 bits (only used with opaque pixels during
329 PNG8 quantization) */
330
glennrp05001c32011-08-06 13:04:16 +0000331#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000332 { \
333 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
334 (pixelpacket).red=ScaleCharToQuantum( \
335 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
336 }
glennrp91d99252011-06-25 14:30:13 +0000337#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000338 { \
339 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
340 (pixelpacket).green=ScaleCharToQuantum( \
341 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
342 }
glennrp91d99252011-06-25 14:30:13 +0000343#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
346 (pixelpacket).blue=ScaleCharToQuantum( \
347 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
348 }
349
glennrp91d99252011-06-25 14:30:13 +0000350#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000351 { \
glennrp05001c32011-08-06 13:04:16 +0000352 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000353 LBR03PacketGreen((pixelpacket)); \
354 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000355 }
glennrp8e58efd2011-05-20 12:16:29 +0000356
cristyef618312011-06-25 12:26:44 +0000357#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000358 { \
cristy16ea1392012-03-21 20:38:41 +0000359 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000360 & 0xe0; \
cristy16ea1392012-03-21 20:38:41 +0000361 SetPixelRed(image, ScaleCharToQuantum( \
362 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000363 }
cristy16ea1392012-03-21 20:38:41 +0000364#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000365 { \
cristy16ea1392012-03-21 20:38:41 +0000366 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000367 & 0xe0; \
cristy16ea1392012-03-21 20:38:41 +0000368 SetPixelGreen(image, ScaleCharToQuantum( \
369 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000370 }
cristy16ea1392012-03-21 20:38:41 +0000371#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000372 { \
cristy16ea1392012-03-21 20:38:41 +0000373 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000374 & 0xe0; \
cristy16ea1392012-03-21 20:38:41 +0000375 SetPixelBlue(image, ScaleCharToQuantum( \
376 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000377 }
378
cristy16ea1392012-03-21 20:38:41 +0000379#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000380 { \
cristyef618312011-06-25 12:26:44 +0000381 LBR03PixelRed((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000382 LBR03Green((pixel)); \
383 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000384 }
glennrp8e58efd2011-05-20 12:16:29 +0000385
386/* LBR04: Replicate top 4 bits */
387
glennrp05001c32011-08-06 13:04:16 +0000388#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000389 { \
390 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
391 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
392 }
glennrp91d99252011-06-25 14:30:13 +0000393#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000394 { \
395 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
396 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
397 }
glennrp91d99252011-06-25 14:30:13 +0000398#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000399 { \
400 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
401 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
402 }
cristy16ea1392012-03-21 20:38:41 +0000403#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000404 { \
cristy16ea1392012-03-21 20:38:41 +0000405 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
406 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000407 }
408
glennrp91d99252011-06-25 14:30:13 +0000409#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000410 { \
glennrp05001c32011-08-06 13:04:16 +0000411 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000412 LBR04PacketGreen((pixelpacket)); \
413 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000414 }
glennrp8e58efd2011-05-20 12:16:29 +0000415
glennrp91d99252011-06-25 14:30:13 +0000416#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000417 { \
glennrp91d99252011-06-25 14:30:13 +0000418 LBR04PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000419 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000420 }
glennrp8e58efd2011-05-20 12:16:29 +0000421
cristyef618312011-06-25 12:26:44 +0000422#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
cristy16ea1392012-03-21 20:38:41 +0000424 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000425 & 0xf0; \
cristy16ea1392012-03-21 20:38:41 +0000426 SetPixelRed(image,\
427 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000428 }
glennrp54cf7972011-08-06 14:28:09 +0000429#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000430 { \
cristy16ea1392012-03-21 20:38:41 +0000431 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000432 & 0xf0; \
cristy16ea1392012-03-21 20:38:41 +0000433 SetPixelGreen(image,\
434 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000435 }
glennrp54cf7972011-08-06 14:28:09 +0000436#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000437 { \
438 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000439 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
440 SetPixelBlue(image,\
441 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000442 }
cristy16ea1392012-03-21 20:38:41 +0000443#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000444 { \
445 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000446 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
447 SetPixelAlpha(image,\
448 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000449 }
450
glennrp54cf7972011-08-06 14:28:09 +0000451#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000452 { \
cristyef618312011-06-25 12:26:44 +0000453 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000454 LBR04PixelGreen((pixel)); \
455 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000456 }
glennrp8e58efd2011-05-20 12:16:29 +0000457
cristy16ea1392012-03-21 20:38:41 +0000458#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000459 { \
glennrp54cf7972011-08-06 14:28:09 +0000460 LBR04PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000461 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000462 }
glennrp8e58efd2011-05-20 12:16:29 +0000463
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
glennrp85dcf872011-12-07 02:51:47 +0000528static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
529static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
530static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
531static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
532static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
533static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
534static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
535static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
536static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
537static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
538static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
539static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
540static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
541static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
542static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
543static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
544static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
545static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
546static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
547static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
548static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
549static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
550static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
551static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
552static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
553static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
554static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
555static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
556static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
557static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
558static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
559static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
560static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
561static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000562
563#if defined(JNG_SUPPORTED)
glennrp85dcf872011-12-07 02:51:47 +0000564static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
565static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
566static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
567static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
568static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
569static 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: */
glennrp85dcf872011-12-07 02:51:47 +0000574static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
glennrp85dcf872011-12-07 02:51:47 +0000575static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
576static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
577static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
578static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
579static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
580static 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
cristybb503372010-05-27 20:51:26 +00001041static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001042{
1043 if (x > y)
1044 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001045
cristy3ed852e2009-09-05 21:47:34 +00001046 return(y);
1047}
glennrp0c3e06b2010-11-19 13:45:02 +00001048
cristyd9ecd042012-06-17 18:26:12 +00001049static const char *
glennrp5dff4352012-06-06 22:12:04 +00001050Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1051{
1052 switch (ping_colortype)
1053 {
1054 case 0:
1055 return "Grayscale";
1056
1057 case 2:
1058 return "Truecolor";
1059
1060 case 3:
1061 return "Indexed";
1062
1063 case 4:
1064 return "GrayAlpha";
1065
1066 case 6:
1067 return "RGBA";
1068
1069 default:
1070 return "UndefinedColorType";
1071 }
1072}
1073
1074
cristybb503372010-05-27 20:51:26 +00001075static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001076{
1077 if (x < y)
1078 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001079
cristy3ed852e2009-09-05 21:47:34 +00001080 return(y);
1081}
glennrpd5045b42010-03-24 12:40:35 +00001082#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001083#endif /* MAGICKCORE_PNG_DELEGATE */
1084
1085/*
1086%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1087% %
1088% %
1089% %
1090% I s M N G %
1091% %
1092% %
1093% %
1094%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1095%
1096% IsMNG() returns MagickTrue if the image format type, identified by the
1097% magick string, is MNG.
1098%
1099% The format of the IsMNG method is:
1100%
1101% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1102%
1103% A description of each parameter follows:
1104%
1105% o magick: compare image format pattern against these bytes.
1106%
1107% o length: Specifies the length of the magick string.
1108%
1109%
1110*/
1111static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1112{
1113 if (length < 8)
1114 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001115
cristy3ed852e2009-09-05 21:47:34 +00001116 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1117 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001118
cristy3ed852e2009-09-05 21:47:34 +00001119 return(MagickFalse);
1120}
1121
1122/*
1123%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1124% %
1125% %
1126% %
1127% I s J N G %
1128% %
1129% %
1130% %
1131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1132%
1133% IsJNG() returns MagickTrue if the image format type, identified by the
1134% magick string, is JNG.
1135%
1136% The format of the IsJNG method is:
1137%
1138% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1139%
1140% A description of each parameter follows:
1141%
1142% o magick: compare image format pattern against these bytes.
1143%
1144% o length: Specifies the length of the magick string.
1145%
1146%
1147*/
1148static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1149{
1150 if (length < 8)
1151 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001152
cristy3ed852e2009-09-05 21:47:34 +00001153 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1154 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001155
cristy3ed852e2009-09-05 21:47:34 +00001156 return(MagickFalse);
1157}
1158
1159/*
1160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1161% %
1162% %
1163% %
1164% I s P N G %
1165% %
1166% %
1167% %
1168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1169%
1170% IsPNG() returns MagickTrue if the image format type, identified by the
1171% magick string, is PNG.
1172%
1173% The format of the IsPNG method is:
1174%
1175% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1176%
1177% A description of each parameter follows:
1178%
1179% o magick: compare image format pattern against these bytes.
1180%
1181% o length: Specifies the length of the magick string.
1182%
1183*/
1184static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1185{
1186 if (length < 8)
1187 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001188
cristy3ed852e2009-09-05 21:47:34 +00001189 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1190 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001191
cristy3ed852e2009-09-05 21:47:34 +00001192 return(MagickFalse);
1193}
1194
1195#if defined(MAGICKCORE_PNG_DELEGATE)
1196#if defined(__cplusplus) || defined(c_plusplus)
1197extern "C" {
1198#endif
1199
glennrpd5045b42010-03-24 12:40:35 +00001200#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001201static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001202{
1203 unsigned char
1204 buffer[4];
1205
1206 assert(image != (Image *) NULL);
1207 assert(image->signature == MagickSignature);
1208 buffer[0]=(unsigned char) (value >> 24);
1209 buffer[1]=(unsigned char) (value >> 16);
1210 buffer[2]=(unsigned char) (value >> 8);
1211 buffer[3]=(unsigned char) value;
1212 return((size_t) WriteBlob(image,4,buffer));
1213}
1214
1215static void PNGLong(png_bytep p,png_uint_32 value)
1216{
1217 *p++=(png_byte) ((value >> 24) & 0xff);
1218 *p++=(png_byte) ((value >> 16) & 0xff);
1219 *p++=(png_byte) ((value >> 8) & 0xff);
1220 *p++=(png_byte) (value & 0xff);
1221}
1222
glennrpa521b2f2010-10-29 04:11:03 +00001223#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001224static void PNGsLong(png_bytep p,png_int_32 value)
1225{
1226 *p++=(png_byte) ((value >> 24) & 0xff);
1227 *p++=(png_byte) ((value >> 16) & 0xff);
1228 *p++=(png_byte) ((value >> 8) & 0xff);
1229 *p++=(png_byte) (value & 0xff);
1230}
glennrpa521b2f2010-10-29 04:11:03 +00001231#endif
cristy3ed852e2009-09-05 21:47:34 +00001232
1233static void PNGShort(png_bytep p,png_uint_16 value)
1234{
1235 *p++=(png_byte) ((value >> 8) & 0xff);
1236 *p++=(png_byte) (value & 0xff);
1237}
1238
1239static void PNGType(png_bytep p,png_bytep type)
1240{
1241 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1242}
1243
glennrp03812ae2010-12-24 01:31:34 +00001244static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1245 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001246{
1247 if (logging != MagickFalse)
1248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001249 " Writing %c%c%c%c chunk, length: %.20g",
1250 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001251}
glennrpd5045b42010-03-24 12:40:35 +00001252#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001253
1254#if defined(__cplusplus) || defined(c_plusplus)
1255}
1256#endif
1257
glennrpd5045b42010-03-24 12:40:35 +00001258#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001259/*
1260%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1261% %
1262% %
1263% %
1264% R e a d P N G I m a g e %
1265% %
1266% %
1267% %
1268%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1269%
1270% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1271% Multiple-image Network Graphics (MNG) image file and returns it. It
1272% allocates the memory necessary for the new Image structure and returns a
1273% pointer to the new image or set of images.
1274%
1275% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1276%
1277% The format of the ReadPNGImage method is:
1278%
1279% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1280%
1281% A description of each parameter follows:
1282%
1283% o image_info: the image info.
1284%
1285% o exception: return any errors or warnings in this structure.
1286%
1287% To do, more or less in chronological order (as of version 5.5.2,
1288% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1289%
1290% Get 16-bit cheap transparency working.
1291%
1292% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1293%
1294% Preserve all unknown and not-yet-handled known chunks found in input
1295% PNG file and copy them into output PNG files according to the PNG
1296% copying rules.
1297%
1298% (At this point, PNG encoding should be in full MNG compliance)
1299%
1300% Provide options for choice of background to use when the MNG BACK
1301% chunk is not present or is not mandatory (i.e., leave transparent,
1302% user specified, MNG BACK, PNG bKGD)
1303%
1304% Implement LOOP/ENDL [done, but could do discretionary loops more
1305% efficiently by linking in the duplicate frames.].
1306%
1307% Decode and act on the MHDR simplicity profile (offer option to reject
1308% files or attempt to process them anyway when the profile isn't LC or VLC).
1309%
1310% Upgrade to full MNG without Delta-PNG.
1311%
1312% o BACK [done a while ago except for background image ID]
1313% o MOVE [done 15 May 1999]
1314% o CLIP [done 15 May 1999]
1315% o DISC [done 19 May 1999]
1316% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1317% o SEEK [partially done 19 May 1999 (discard function only)]
1318% o SHOW
1319% o PAST
1320% o BASI
1321% o MNG-level tEXt/iTXt/zTXt
1322% o pHYg
1323% o pHYs
1324% o sBIT
1325% o bKGD
1326% o iTXt (wait for libpng implementation).
1327%
1328% Use the scene signature to discover when an identical scene is
1329% being reused, and just point to the original image->exception instead
1330% of storing another set of pixels. This not specific to MNG
1331% but could be applied generally.
1332%
1333% Upgrade to full MNG with Delta-PNG.
1334%
1335% JNG tEXt/iTXt/zTXt
1336%
1337% We will not attempt to read files containing the CgBI chunk.
1338% They are really Xcode files meant for display on the iPhone.
1339% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001340% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001341% since irretrievable loss of color data has occurred due to the
1342% use of premultiplied alpha.
1343*/
1344
1345#if defined(__cplusplus) || defined(c_plusplus)
1346extern "C" {
1347#endif
1348
1349/*
1350 This the function that does the actual reading of data. It is
1351 the same as the one supplied in libpng, except that it receives the
1352 datastream from the ReadBlob() function instead of standard input.
1353*/
1354static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1355{
1356 Image
1357 *image;
1358
1359 image=(Image *) png_get_io_ptr(png_ptr);
1360 if (length)
1361 {
1362 png_size_t
1363 check;
1364
1365 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1366 if (check != length)
1367 {
1368 char
1369 msg[MaxTextExtent];
1370
cristy3b6fd2e2011-05-20 12:53:50 +00001371 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001372 "Expected %.20g bytes; found %.20g bytes",(double) length,
1373 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001374 png_warning(png_ptr,msg);
1375 png_error(png_ptr,"Read Exception");
1376 }
1377 }
1378}
1379
1380#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1381 !defined(PNG_MNG_FEATURES_SUPPORTED)
1382/* We use mng_get_data() instead of png_get_data() if we have a libpng
1383 * older than libpng-1.0.3a, which was the first to allow the empty
1384 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1385 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1386 * encountered after an empty PLTE, so we have to look ahead for bKGD
1387 * chunks and remove them from the datastream that is passed to libpng,
1388 * and store their contents for later use.
1389 */
1390static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1391{
1392 MngInfo
1393 *mng_info;
1394
1395 Image
1396 *image;
1397
1398 png_size_t
1399 check;
1400
cristybb503372010-05-27 20:51:26 +00001401 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001402 i;
1403
1404 i=0;
1405 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1406 image=(Image *) mng_info->image;
1407 while (mng_info->bytes_in_read_buffer && length)
1408 {
1409 data[i]=mng_info->read_buffer[i];
1410 mng_info->bytes_in_read_buffer--;
1411 length--;
1412 i++;
1413 }
1414 if (length)
1415 {
1416 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001417
cristy3ed852e2009-09-05 21:47:34 +00001418 if (check != length)
1419 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001420
cristy3ed852e2009-09-05 21:47:34 +00001421 if (length == 4)
1422 {
1423 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1424 (data[3] == 0))
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_PLTE,4) == 0)
1431 mng_info->found_empty_plte=MagickTrue;
1432 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1433 {
1434 mng_info->found_empty_plte=MagickFalse;
1435 mng_info->have_saved_bkgd_index=MagickFalse;
1436 }
1437 }
glennrp0fe50b42010-11-16 03:52:51 +00001438
cristy3ed852e2009-09-05 21:47:34 +00001439 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1440 (data[3] == 1))
1441 {
1442 check=(png_size_t) ReadBlob(image,(size_t) length,
1443 (char *) mng_info->read_buffer);
1444 mng_info->read_buffer[4]=0;
1445 mng_info->bytes_in_read_buffer=4;
1446 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1447 if (mng_info->found_empty_plte)
1448 {
1449 /*
1450 Skip the bKGD data byte and CRC.
1451 */
1452 check=(png_size_t)
1453 ReadBlob(image,5,(char *) mng_info->read_buffer);
1454 check=(png_size_t) ReadBlob(image,(size_t) length,
1455 (char *) mng_info->read_buffer);
1456 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1457 mng_info->have_saved_bkgd_index=MagickTrue;
1458 mng_info->bytes_in_read_buffer=0;
1459 }
1460 }
1461 }
1462 }
1463}
1464#endif
1465
1466static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1467{
1468 Image
1469 *image;
1470
1471 image=(Image *) png_get_io_ptr(png_ptr);
1472 if (length)
1473 {
1474 png_size_t
1475 check;
1476
cristybb503372010-05-27 20:51:26 +00001477 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001478
cristy3ed852e2009-09-05 21:47:34 +00001479 if (check != length)
1480 png_error(png_ptr,"WriteBlob Failed");
1481 }
1482}
1483
1484static void png_flush_data(png_structp png_ptr)
1485{
1486 (void) png_ptr;
1487}
1488
1489#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1490static int PalettesAreEqual(Image *a,Image *b)
1491{
cristybb503372010-05-27 20:51:26 +00001492 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001493 i;
1494
1495 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1496 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001497
cristy3ed852e2009-09-05 21:47:34 +00001498 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1499 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001500
cristy3ed852e2009-09-05 21:47:34 +00001501 if (a->colors != b->colors)
1502 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001503
cristybb503372010-05-27 20:51:26 +00001504 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001505 {
1506 if ((a->colormap[i].red != b->colormap[i].red) ||
1507 (a->colormap[i].green != b->colormap[i].green) ||
1508 (a->colormap[i].blue != b->colormap[i].blue))
1509 return((int) MagickFalse);
1510 }
glennrp0fe50b42010-11-16 03:52:51 +00001511
cristy3ed852e2009-09-05 21:47:34 +00001512 return((int) MagickTrue);
1513}
1514#endif
1515
1516static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1517{
1518 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1519 mng_info->exists[i] && !mng_info->frozen[i])
1520 {
1521#ifdef MNG_OBJECT_BUFFERS
1522 if (mng_info->ob[i] != (MngBuffer *) NULL)
1523 {
1524 if (mng_info->ob[i]->reference_count > 0)
1525 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001526
cristy3ed852e2009-09-05 21:47:34 +00001527 if (mng_info->ob[i]->reference_count == 0)
1528 {
1529 if (mng_info->ob[i]->image != (Image *) NULL)
1530 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001531
cristy3ed852e2009-09-05 21:47:34 +00001532 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1533 }
1534 }
1535 mng_info->ob[i]=(MngBuffer *) NULL;
1536#endif
1537 mng_info->exists[i]=MagickFalse;
1538 mng_info->invisible[i]=MagickFalse;
1539 mng_info->viewable[i]=MagickFalse;
1540 mng_info->frozen[i]=MagickFalse;
1541 mng_info->x_off[i]=0;
1542 mng_info->y_off[i]=0;
1543 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001544 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001545 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001546 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001547 }
1548}
1549
glennrp21f0e622011-01-07 16:20:57 +00001550static void MngInfoFreeStruct(MngInfo *mng_info,
1551 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001552{
glennrp21f0e622011-01-07 16:20:57 +00001553 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001554 {
cristybb503372010-05-27 20:51:26 +00001555 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001556 i;
1557
1558 for (i=1; i < MNG_MAX_OBJECTS; i++)
1559 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001560
cristy3ed852e2009-09-05 21:47:34 +00001561 if (mng_info->global_plte != (png_colorp) NULL)
1562 mng_info->global_plte=(png_colorp)
1563 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001564
cristy3ed852e2009-09-05 21:47:34 +00001565 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1566 *have_mng_structure=MagickFalse;
1567 }
1568}
1569
1570static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1571{
1572 MngBox
1573 box;
1574
1575 box=box1;
1576 if (box.left < box2.left)
1577 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001578
cristy3ed852e2009-09-05 21:47:34 +00001579 if (box.top < box2.top)
1580 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001581
cristy3ed852e2009-09-05 21:47:34 +00001582 if (box.right > box2.right)
1583 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001584
cristy3ed852e2009-09-05 21:47:34 +00001585 if (box.bottom > box2.bottom)
1586 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001587
cristy3ed852e2009-09-05 21:47:34 +00001588 return box;
1589}
1590
1591static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1592{
1593 MngBox
1594 box;
1595
1596 /*
1597 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1598 */
cristybb503372010-05-27 20:51:26 +00001599 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1600 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1601 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1602 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001603 if (delta_type != 0)
1604 {
1605 box.left+=previous_box.left;
1606 box.right+=previous_box.right;
1607 box.top+=previous_box.top;
1608 box.bottom+=previous_box.bottom;
1609 }
glennrp0fe50b42010-11-16 03:52:51 +00001610
cristy3ed852e2009-09-05 21:47:34 +00001611 return(box);
1612}
1613
1614static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1615 unsigned char *p)
1616{
1617 MngPair
1618 pair;
1619 /*
cristybb503372010-05-27 20:51:26 +00001620 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001621 */
cristy8182b072010-05-30 20:10:53 +00001622 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1623 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001624
cristy3ed852e2009-09-05 21:47:34 +00001625 if (delta_type != 0)
1626 {
1627 pair.a+=previous_pair.a;
1628 pair.b+=previous_pair.b;
1629 }
glennrp0fe50b42010-11-16 03:52:51 +00001630
cristy3ed852e2009-09-05 21:47:34 +00001631 return(pair);
1632}
1633
cristy8182b072010-05-30 20:10:53 +00001634static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001635{
cristy8182b072010-05-30 20:10:53 +00001636 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001637}
1638
cristy16ea1392012-03-21 20:38:41 +00001639typedef struct _PNGErrorInfo
cristyc82a27b2011-10-21 01:07:16 +00001640{
cristyc82a27b2011-10-21 01:07:16 +00001641 Image
1642 *image;
1643
cristy16ea1392012-03-21 20:38:41 +00001644 ExceptionInfo
1645 *exception;
1646} PNGErrorInfo;
cristyc82a27b2011-10-21 01:07:16 +00001647
cristy16ea1392012-03-21 20:38:41 +00001648static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1649{
1650 ExceptionInfo
1651 *exception;
cristyc82a27b2011-10-21 01:07:16 +00001652
cristy16ea1392012-03-21 20:38:41 +00001653 Image
1654 *image;
1655
1656 PNGErrorInfo
1657 *error_info;
1658
1659 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1660 image=error_info->image;
1661 exception=error_info->exception;
1662
1663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1664 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1665
1666 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1667 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001668
glennrpe4017e32011-01-08 17:16:09 +00001669#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001670 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1671 * are building with libpng-1.4.x and can be ignored.
1672 */
cristy3ed852e2009-09-05 21:47:34 +00001673 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001674#else
1675 png_longjmp(ping,1);
1676#endif
cristy3ed852e2009-09-05 21:47:34 +00001677}
1678
glennrpcf002022011-01-30 02:38:15 +00001679static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001680{
cristy16ea1392012-03-21 20:38:41 +00001681 ExceptionInfo
1682 *exception;
1683
cristy3ed852e2009-09-05 21:47:34 +00001684 Image
1685 *image;
1686
cristy16ea1392012-03-21 20:38:41 +00001687 PNGErrorInfo
1688 *error_info;
1689
cristy3ed852e2009-09-05 21:47:34 +00001690 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1691 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001692
cristy16ea1392012-03-21 20:38:41 +00001693 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1694 image=error_info->image;
1695 exception=error_info->exception;
1696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1697 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001698
cristy16ea1392012-03-21 20:38:41 +00001699 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001700 message,"`%s'",image->filename);
1701}
1702
1703#ifdef PNG_USER_MEM_SUPPORTED
glennrp943b7d32013-04-21 00:40:38 +00001704#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +00001705static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1706#else
1707static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1708#endif
cristy3ed852e2009-09-05 21:47:34 +00001709{
cristydf0d90e2011-12-12 01:03:55 +00001710 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001711 return((png_voidp) AcquireMagickMemory((size_t) size));
cristy3ed852e2009-09-05 21:47:34 +00001712}
1713
1714/*
1715 Free a pointer. It is removed from the list at the same time.
1716*/
glennrpcf002022011-01-30 02:38:15 +00001717static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001718{
glennrp3bd393f2011-12-21 18:54:53 +00001719 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001720 ptr=RelinquishMagickMemory(ptr);
1721 return((png_free_ptr) NULL);
1722}
1723#endif
1724
1725#if defined(__cplusplus) || defined(c_plusplus)
1726}
1727#endif
1728
1729static int
glennrpedaa0382012-04-12 14:16:21 +00001730Magick_png_read_raw_profile(png_struct *ping,Image *image,
1731 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001732{
cristybb503372010-05-27 20:51:26 +00001733 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001734 i;
1735
1736 register unsigned char
1737 *dp;
1738
1739 register png_charp
1740 sp;
1741
1742 png_uint_32
1743 length,
1744 nibbles;
1745
1746 StringInfo
1747 *profile;
1748
glennrp0c3e06b2010-11-19 13:45:02 +00001749 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001750 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1751 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1752 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1753 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1754 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1755 13,14,15};
1756
1757 sp=text[ii].text+1;
1758 /* look for newline */
1759 while (*sp != '\n')
1760 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001761
cristy3ed852e2009-09-05 21:47:34 +00001762 /* look for length */
1763 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1764 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001765
cristyf2f27272009-12-17 14:48:46 +00001766 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001767
glennrp97f90e22011-02-22 05:47:58 +00001768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1769 " length: %lu",(unsigned long) length);
1770
cristy3ed852e2009-09-05 21:47:34 +00001771 while (*sp != ' ' && *sp != '\n')
1772 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001773
cristy3ed852e2009-09-05 21:47:34 +00001774 /* allocate space */
1775 if (length == 0)
1776 {
glennrpedaa0382012-04-12 14:16:21 +00001777 png_warning(ping,"invalid profile length");
cristy3ed852e2009-09-05 21:47:34 +00001778 return(MagickFalse);
1779 }
glennrp0fe50b42010-11-16 03:52:51 +00001780
cristy8723e4b2011-09-01 13:11:19 +00001781 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001782
cristy3ed852e2009-09-05 21:47:34 +00001783 if (profile == (StringInfo *) NULL)
1784 {
glennrpedaa0382012-04-12 14:16:21 +00001785 png_warning(ping, "unable to copy profile");
cristy3ed852e2009-09-05 21:47:34 +00001786 return(MagickFalse);
1787 }
glennrp0fe50b42010-11-16 03:52:51 +00001788
cristy3ed852e2009-09-05 21:47:34 +00001789 /* copy profile, skipping white space and column 1 "=" signs */
1790 dp=GetStringInfoDatum(profile);
1791 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001792
cristybb503372010-05-27 20:51:26 +00001793 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001794 {
1795 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1796 {
1797 if (*sp == '\0')
1798 {
glennrpedaa0382012-04-12 14:16:21 +00001799 png_warning(ping, "ran out of profile data");
cristy3ed852e2009-09-05 21:47:34 +00001800 profile=DestroyStringInfo(profile);
1801 return(MagickFalse);
1802 }
1803 sp++;
1804 }
glennrp0fe50b42010-11-16 03:52:51 +00001805
cristy3ed852e2009-09-05 21:47:34 +00001806 if (i%2 == 0)
1807 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001808
cristy3ed852e2009-09-05 21:47:34 +00001809 else
1810 (*dp++)+=unhex[(int) *sp++];
1811 }
1812 /*
1813 We have already read "Raw profile type.
1814 */
cristy16ea1392012-03-21 20:38:41 +00001815 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001816 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001817
cristy3ed852e2009-09-05 21:47:34 +00001818 if (image_info->verbose)
1819 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001820
cristy3ed852e2009-09-05 21:47:34 +00001821 return MagickTrue;
1822}
1823
1824#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1825static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1826{
1827 Image
1828 *image;
1829
1830
1831 /* The unknown chunk structure contains the chunk data:
1832 png_byte name[5];
1833 png_byte *data;
1834 png_size_t size;
1835
1836 Note that libpng has already taken care of the CRC handling.
1837 */
1838
glennrp2ad70152013-03-03 00:44:57 +00001839 LogMagickEvent(CoderEvent,GetMagickModule(),
1840 " read_vpag_chunk: found %c%c%c%c chunk",
1841 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
cristy3ed852e2009-09-05 21:47:34 +00001842
1843 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1844 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1845 return(0); /* Did not recognize */
1846
1847 /* recognized vpAg */
1848
1849 if (chunk->size != 9)
1850 return(-1); /* Error return */
1851
1852 if (chunk->data[8] != 0)
1853 return(0); /* ImageMagick requires pixel units */
1854
1855 image=(Image *) png_get_user_chunk_ptr(ping);
1856
cristybb503372010-05-27 20:51:26 +00001857 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001858 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001859
cristybb503372010-05-27 20:51:26 +00001860 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001861 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1862
1863 /* Return one of the following: */
1864 /* return(-n); chunk had an error */
1865 /* return(0); did not recognize */
1866 /* return(n); success */
1867
1868 return(1);
1869
1870}
1871#endif
1872
dirkfd6fd072014-10-24 21:10:08 +00001873#if defined(PNG_tIME_SUPPORTED)
1874static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
1875 ExceptionInfo *exception)
1876{
1877 png_timep
1878 time;
1879
1880 if (png_get_tIME(ping,info,&time))
1881 {
1882 char
1883 timestamp[21];
1884
1885 FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
1886 time->year,time->month,time->day,time->hour,time->minute,time->second);
1887 SetImageProperty(image,"png:tIME",timestamp,exception);
1888 }
1889}
1890#endif
1891
cristy3ed852e2009-09-05 21:47:34 +00001892/*
1893%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1894% %
1895% %
1896% %
1897% R e a d O n e P N G I m a g e %
1898% %
1899% %
1900% %
1901%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1902%
1903% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1904% (minus the 8-byte signature) and returns it. It allocates the memory
1905% necessary for the new Image structure and returns a pointer to the new
1906% image.
1907%
1908% The format of the ReadOnePNGImage method is:
1909%
1910% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1911% ExceptionInfo *exception)
1912%
1913% A description of each parameter follows:
1914%
1915% o mng_info: Specifies a pointer to a MngInfo structure.
1916%
1917% o image_info: the image info.
1918%
1919% o exception: return any errors or warnings in this structure.
1920%
1921*/
1922static Image *ReadOnePNGImage(MngInfo *mng_info,
1923 const ImageInfo *image_info, ExceptionInfo *exception)
1924{
1925 /* Read one PNG image */
1926
glennrpcc95c3f2011-04-18 16:46:48 +00001927 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1928
cristy3ed852e2009-09-05 21:47:34 +00001929 Image
1930 *image;
1931
glennrpd0cae252013-03-15 22:30:41 +00001932 char
glennrpec0ddbc2013-03-16 12:40:12 +00001933 im_vers[32],
1934 libpng_runv[32],
1935 libpng_vers[32],
1936 zlib_runv[32],
1937 zlib_vers[32];
glennrpd0cae252013-03-15 22:30:41 +00001938
cristy3ed852e2009-09-05 21:47:34 +00001939 int
glennrp98b83d42012-07-23 02:50:31 +00001940 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
glennrpcb395ac2011-03-30 19:50:23 +00001941 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001942 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001943 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001944 num_passes,
glennrp913f9612012-06-27 17:50:00 +00001945 number_colors,
glennrpfaa852b2010-03-30 12:17:00 +00001946 pass,
1947 ping_bit_depth,
1948 ping_color_type,
glennrpfcf06162012-11-05 14:57:08 +00001949 ping_file_depth,
glennrpfaa852b2010-03-30 12:17:00 +00001950 ping_interlace_method,
1951 ping_compression_method,
1952 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001953 ping_num_trans,
1954 unit_type;
1955
1956 double
1957 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00001958
1959 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00001960 logging,
glennrp98b83d42012-07-23 02:50:31 +00001961 ping_found_cHRM,
1962 ping_found_gAMA,
1963 ping_found_iCCP,
1964 ping_found_sRGB,
glennrp3d627862013-02-26 00:19:34 +00001965 ping_found_sRGB_cHRM,
glennrpecab7d72013-05-14 22:50:32 +00001966 ping_preserve_iCCP,
cristy3ed852e2009-09-05 21:47:34 +00001967 status;
1968
cristy09973322013-06-30 21:06:30 +00001969 MemoryInfo
1970 *volatile pixel_info;
1971
cristy16ea1392012-03-21 20:38:41 +00001972 PixelInfo
1973 transparent_color;
1974
1975 PNGErrorInfo
1976 error_info;
1977
glennrpfaa852b2010-03-30 12:17:00 +00001978 png_bytep
1979 ping_trans_alpha;
1980
1981 png_color_16p
1982 ping_background,
1983 ping_trans_color;
1984
cristy3ed852e2009-09-05 21:47:34 +00001985 png_info
1986 *end_info,
1987 *ping_info;
1988
1989 png_struct
1990 *ping;
1991
1992 png_textp
1993 text;
1994
glennrpfaa852b2010-03-30 12:17:00 +00001995 png_uint_32
1996 ping_height,
1997 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00001998 x_resolution,
1999 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002000
cristy16ea1392012-03-21 20:38:41 +00002001 QuantumInfo
2002 *quantum_info;
2003
cristybb503372010-05-27 20:51:26 +00002004 ssize_t
cristy756ae432011-11-19 02:18:25 +00002005 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002006 y;
2007
2008 register unsigned char
2009 *p;
2010
cristybb503372010-05-27 20:51:26 +00002011 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002012 i,
2013 x;
2014
cristy16ea1392012-03-21 20:38:41 +00002015 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002016 *q;
2017
2018 size_t
glennrp39992b42010-11-14 00:03:43 +00002019 length,
cristy3ed852e2009-09-05 21:47:34 +00002020 row_offset;
2021
cristyeb3b22a2011-03-31 20:16:11 +00002022 ssize_t
2023 j;
2024
cristy75fc68f2012-10-08 16:26:00 +00002025 unsigned char
cristy09973322013-06-30 21:06:30 +00002026 *ping_pixels;
cristy55b78b52012-10-08 14:01:27 +00002027
glennrp629960f2012-05-29 19:13:52 +00002028#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002029 png_byte unused_chunks[]=
2030 {
2031 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2032 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2033 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2034 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2035 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
dirkfd6fd072014-10-24 21:10:08 +00002036#if !defined(PNG_tIME_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00002037 116, 73, 77, 69, (png_byte) '\0', /* tIME */
dirkfd6fd072014-10-24 21:10:08 +00002038#endif
glennrp629960f2012-05-29 19:13:52 +00002039#ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2040 /* ignore the APNG chunks */
2041 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2042 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2043 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2044#endif
cristy3ed852e2009-09-05 21:47:34 +00002045 };
2046#endif
2047
glennrpd0cae252013-03-15 22:30:41 +00002048 /* Define these outside of the following "if logging()" block so they will
2049 * show in debuggers.
2050 */
2051 *im_vers='\0';
2052 (void) ConcatenateMagickString(im_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002053 MagickLibVersionText,32);
glennrpd0cae252013-03-15 22:30:41 +00002054 (void) ConcatenateMagickString(im_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002055 MagickLibAddendum,32);
2056
glennrpd0cae252013-03-15 22:30:41 +00002057 *libpng_vers='\0';
2058 (void) ConcatenateMagickString(libpng_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002059 PNG_LIBPNG_VER_STRING,32);
2060 *libpng_runv='\0';
2061 (void) ConcatenateMagickString(libpng_runv,
2062 png_get_libpng_ver(NULL),32);
2063
glennrpd0cae252013-03-15 22:30:41 +00002064 *zlib_vers='\0';
2065 (void) ConcatenateMagickString(zlib_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00002066 ZLIB_VERSION,32);
2067 *zlib_runv='\0';
2068 (void) ConcatenateMagickString(zlib_runv,
2069 zlib_version,32);
2070
glennrp2dd19062014-07-28 00:22:24 +00002071 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2072 " Enter ReadOnePNGImage()\n"
2073 " IM version = %s\n"
2074 " Libpng version = %s",
2075 im_vers, libpng_vers);
2076
2077 if (logging != MagickFalse)
2078 {
2079 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
glennrpd0cae252013-03-15 22:30:41 +00002080 {
glennrp2dd19062014-07-28 00:22:24 +00002081 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2082 libpng_runv);
glennrpd0cae252013-03-15 22:30:41 +00002083 }
glennrp2dd19062014-07-28 00:22:24 +00002084 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
2085 zlib_vers);
2086 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2087 {
2088 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2089 zlib_runv);
2090 }
2091 }
glennrpd0cae252013-03-15 22:30:41 +00002092
glennrp25c1e2b2010-03-25 01:39:56 +00002093#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002094 if (image_info->verbose)
2095 printf("Your PNG library (libpng-%s) is rather old.\n",
2096 PNG_LIBPNG_VER_STRING);
2097#endif
2098
glennrp61b4c952009-11-10 20:40:41 +00002099#if (PNG_LIBPNG_VER >= 10400)
2100# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2101 if (image_info->verbose)
2102 {
2103 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2104 PNG_LIBPNG_VER_STRING);
2105 printf("Please update it.\n");
2106 }
2107# endif
2108#endif
2109
cristy16ea1392012-03-21 20:38:41 +00002110
2111 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002112 image=mng_info->image;
2113
glennrpa6a06632011-01-19 15:15:34 +00002114 if (logging != MagickFalse)
glennrp98b83d42012-07-23 02:50:31 +00002115 {
glennrpa6a06632011-01-19 15:15:34 +00002116 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00002117 " Before reading:\n"
2118 " image->alpha_trait=%d"
2119 " image->rendering_intent=%d\n"
2120 " image->colorspace=%d\n"
2121 " image->gamma=%f",
2122 (int) image->alpha_trait, (int) image->rendering_intent,
2123 (int) image->colorspace, image->gamma);
glennrp98b83d42012-07-23 02:50:31 +00002124 }
2125 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2126
glennrp0e319732011-01-25 21:53:13 +00002127 /* Set to an out-of-range color unless tRNS chunk is present */
2128 transparent_color.red=65537;
2129 transparent_color.green=65537;
2130 transparent_color.blue=65537;
cristy16ea1392012-03-21 20:38:41 +00002131 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002132
glennrp913f9612012-06-27 17:50:00 +00002133 number_colors=0;
glennrpcb395ac2011-03-30 19:50:23 +00002134 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002135 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002136 num_raw_profiles = 0;
2137
glennrp98b83d42012-07-23 02:50:31 +00002138 ping_found_cHRM = MagickFalse;
2139 ping_found_gAMA = MagickFalse;
2140 ping_found_iCCP = MagickFalse;
2141 ping_found_sRGB = MagickFalse;
glennrp4b917592013-06-20 19:53:59 +00002142 ping_found_sRGB_cHRM = MagickFalse;
glennrpecab7d72013-05-14 22:50:32 +00002143 ping_preserve_iCCP = MagickFalse;
2144
glennrp98b83d42012-07-23 02:50:31 +00002145
cristy3ed852e2009-09-05 21:47:34 +00002146 /*
2147 Allocate the PNG structures
2148 */
2149#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00002150 error_info.image=image;
2151 error_info.exception=exception;
2152 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002153 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2154 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002155#else
cristy16ea1392012-03-21 20:38:41 +00002156 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002157 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002158#endif
2159 if (ping == (png_struct *) NULL)
2160 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002161
cristy3ed852e2009-09-05 21:47:34 +00002162 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002163
cristy3ed852e2009-09-05 21:47:34 +00002164 if (ping_info == (png_info *) NULL)
2165 {
2166 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2167 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2168 }
glennrp0fe50b42010-11-16 03:52:51 +00002169
cristy3ed852e2009-09-05 21:47:34 +00002170 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002171
cristy3ed852e2009-09-05 21:47:34 +00002172 if (end_info == (png_info *) NULL)
2173 {
2174 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2175 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2176 }
glennrp0fe50b42010-11-16 03:52:51 +00002177
cristy09973322013-06-30 21:06:30 +00002178 pixel_info=(MemoryInfo *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002179
glennrpfaa852b2010-03-30 12:17:00 +00002180 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002181 {
2182 /*
2183 PNG image is corrupt.
2184 */
2185 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002186
glennrp868fff32014-03-16 22:09:06 +00002187#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002188 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002189#endif
glennrpedaa0382012-04-12 14:16:21 +00002190
cristy09973322013-06-30 21:06:30 +00002191 if (pixel_info != (MemoryInfo *) NULL)
2192 pixel_info=RelinquishVirtualMemory(pixel_info);
glennrpedaa0382012-04-12 14:16:21 +00002193
cristy3ed852e2009-09-05 21:47:34 +00002194 if (logging != MagickFalse)
2195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2196 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002197
cristy3ed852e2009-09-05 21:47:34 +00002198 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002199 {
cristy16ea1392012-03-21 20:38:41 +00002200 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002201 image->columns=0;
2202 }
glennrp0fe50b42010-11-16 03:52:51 +00002203
cristy3ed852e2009-09-05 21:47:34 +00002204 return(GetFirstImageInList(image));
2205 }
glennrpedaa0382012-04-12 14:16:21 +00002206
2207 /* { For navigation to end of SETJMP-protected block. Within this
2208 * block, use png_error() instead of Throwing an Exception, to ensure
2209 * that libpng is able to clean up, and that the semaphore is unlocked.
2210 */
2211
glennrp868fff32014-03-16 22:09:06 +00002212#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpedaa0382012-04-12 14:16:21 +00002213 LockSemaphoreInfo(ping_semaphore);
2214#endif
2215
glennrp943b7d32013-04-21 00:40:38 +00002216#ifdef PNG_BENIGN_ERRORS_SUPPORTED
glennrpa3a5f952013-04-17 12:58:15 +00002217 /* Allow benign errors */
2218 png_set_benign_errors(ping, 1);
2219#endif
2220
cristy3ed852e2009-09-05 21:47:34 +00002221 /*
2222 Prepare PNG for reading.
2223 */
glennrpfaa852b2010-03-30 12:17:00 +00002224
cristy3ed852e2009-09-05 21:47:34 +00002225 mng_info->image_found++;
2226 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002227
cristy3ed852e2009-09-05 21:47:34 +00002228 if (LocaleCompare(image_info->magick,"MNG") == 0)
2229 {
2230#if defined(PNG_MNG_FEATURES_SUPPORTED)
2231 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2232 png_set_read_fn(ping,image,png_get_data);
2233#else
2234#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2235 png_permit_empty_plte(ping,MagickTrue);
2236 png_set_read_fn(ping,image,png_get_data);
2237#else
2238 mng_info->image=image;
2239 mng_info->bytes_in_read_buffer=0;
2240 mng_info->found_empty_plte=MagickFalse;
2241 mng_info->have_saved_bkgd_index=MagickFalse;
2242 png_set_read_fn(ping,mng_info,mng_get_data);
2243#endif
2244#endif
2245 }
glennrp0fe50b42010-11-16 03:52:51 +00002246
cristy3ed852e2009-09-05 21:47:34 +00002247 else
2248 png_set_read_fn(ping,image,png_get_data);
2249
glennrpe604a752014-06-28 02:24:30 +00002250 {
2251 const char
2252 *value;
2253
2254 value=GetImageOption(image_info,"profile:skip");
2255
2256 if (IsOptionMember("ICC",value) == MagickFalse)
2257 {
2258
2259 value=GetImageOption(image_info,"png:preserve-iCCP");
2260
2261 if (value == NULL)
2262 value=GetImageArtifact(image,"png:preserve-iCCP");
2263
2264 if (value != NULL)
2265 ping_preserve_iCCP=MagickTrue;
glennrp201f0c92014-06-28 16:13:49 +00002266
2267#if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2268 /* Don't let libpng check for ICC/sRGB profile because we're going
2269 * to do that anyway. This feature was added at libpng-1.6.12.
glennrpcf45b202014-06-29 16:46:17 +00002270 * If logging, go ahead and check and issue a warning as appropriate.
glennrp201f0c92014-06-28 16:13:49 +00002271 */
glennrpcf45b202014-06-29 16:46:17 +00002272 if (logging == MagickFalse)
2273 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
glennrp201f0c92014-06-28 16:13:49 +00002274#endif
glennrpe604a752014-06-28 02:24:30 +00002275 }
2276#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2277 else
2278 {
2279 png_set_keep_unknown_chunks(ping, 1, mng_iCCP, 1);
2280 }
2281#endif
2282 }
cristy3ed852e2009-09-05 21:47:34 +00002283#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2284 /* Ignore unused chunks and all unknown chunks except for vpAg */
glennrp2ad70152013-03-03 00:44:57 +00002285#if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2286 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2287#else
cristy3ed852e2009-09-05 21:47:34 +00002288 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
glennrp2ad70152013-03-03 00:44:57 +00002289#endif
cristy3ed852e2009-09-05 21:47:34 +00002290 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2291 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2292 (int)sizeof(unused_chunks)/5);
2293 /* Callback for other unknown chunks */
2294 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2295#endif
2296
glennrp2feb1412013-01-22 15:02:16 +00002297#ifdef PNG_SET_USER_LIMITS_SUPPORTED
glennrp09cd9622013-01-22 15:17:38 +00002298# if (PNG_LIBPNG_VER >= 10400)
glennrp2feb1412013-01-22 15:02:16 +00002299 /* Limit the size of the chunk storage cache used for sPLT, text,
glennrp68736192013-01-24 06:32:08 +00002300 * and unknown chunks.
glennrp2feb1412013-01-22 15:02:16 +00002301 */
glennrp68736192013-01-24 06:32:08 +00002302 png_set_chunk_cache_max(ping, 32767);
glennrp09cd9622013-01-22 15:17:38 +00002303# endif
glennrp2feb1412013-01-22 15:02:16 +00002304#endif
2305
glennrp9bf97b62012-06-06 21:03:14 +00002306#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2307 /* Disable new libpng-1.5.10 feature */
2308 png_set_check_for_invalid_index (ping, 0);
2309#endif
2310
glennrp991e92a2010-01-28 03:09:00 +00002311#if (PNG_LIBPNG_VER < 10400)
2312# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2313 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002314 /* Disable thread-unsafe features of pnggccrd */
2315 if (png_access_version_number() >= 10200)
2316 {
2317 png_uint_32 mmx_disable_mask=0;
2318 png_uint_32 asm_flags;
2319
2320 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2321 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2322 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2323 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2324 asm_flags=png_get_asm_flags(ping);
2325 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2326 }
glennrp991e92a2010-01-28 03:09:00 +00002327# endif
cristy3ed852e2009-09-05 21:47:34 +00002328#endif
2329
2330 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002331
2332 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2333 &ping_bit_depth,&ping_color_type,
2334 &ping_interlace_method,&ping_compression_method,
2335 &ping_filter_method);
2336
glennrpfcf06162012-11-05 14:57:08 +00002337 ping_file_depth = ping_bit_depth;
2338
glennrp0d767722014-01-17 16:08:31 +00002339 /* Swap bytes if requested */
2340 if (ping_file_depth == 16)
2341 {
2342 const char
glennrpa3d5f0e2014-01-30 18:58:51 +00002343 *value;
2344
2345 value=GetImageOption(image_info,"png:swap-bytes");
2346
2347 if (value == NULL)
2348 value=GetImageArtifact(image,"png:swap-bytes");
2349
2350 if (value != NULL)
2351 png_set_swap(ping);
glennrp0d767722014-01-17 16:08:31 +00002352 }
2353
glennrp5830fbc2013-01-27 06:11:45 +00002354 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2355 {
2356 char
2357 msg[MaxTextExtent];
2358
2359 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
glennrp3398b5b2013-05-03 23:10:31 +00002360 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
glennrp5830fbc2013-01-27 06:11:45 +00002361
2362 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
glennrp3398b5b2013-05-03 23:10:31 +00002363 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
glennrp5830fbc2013-01-27 06:11:45 +00002364 }
2365
glennrpfaa852b2010-03-30 12:17:00 +00002366 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2367 &ping_trans_color);
2368
2369 (void) png_get_bKGD(ping, ping_info, &ping_background);
2370
2371 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002372 {
glennrpfcf06162012-11-05 14:57:08 +00002373 png_set_packing(ping);
2374 ping_bit_depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00002375 }
glennrpfaa852b2010-03-30 12:17:00 +00002376
2377 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002378 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002379 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
glennrp98b83d42012-07-23 02:50:31 +00002380
cristy176b29a2012-06-21 13:35:15 +00002381 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2382 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2383 {
2384 image->rendering_intent=UndefinedIntent;
glennrp98b83d42012-07-23 02:50:31 +00002385 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
cristy176b29a2012-06-21 13:35:15 +00002386 image->gamma=1.000;
2387 (void) ResetMagickMemory(&image->chromaticity,0,
2388 sizeof(image->chromaticity));
2389 }
glennrp98b83d42012-07-23 02:50:31 +00002390
cristy3ed852e2009-09-05 21:47:34 +00002391 if (logging != MagickFalse)
2392 {
2393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00002394 " PNG width: %.20g, height: %.20g\n"
2395 " PNG color_type: %d, bit_depth: %d\n"
2396 " PNG compression_method: %d\n"
cristy3ed852e2009-09-05 21:47:34 +00002397 " PNG interlace_method: %d, filter_method: %d",
glennrp2dd19062014-07-28 00:22:24 +00002398 (double) ping_width, (double) ping_height,
2399 ping_color_type, ping_bit_depth,
2400 ping_compression_method,
glennrpfaa852b2010-03-30 12:17:00 +00002401 ping_interlace_method,ping_filter_method);
glennrp2dd19062014-07-28 00:22:24 +00002402
cristy3ed852e2009-09-05 21:47:34 +00002403 }
2404
glennrpecab7d72013-05-14 22:50:32 +00002405 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2406 {
2407 ping_found_iCCP=MagickTrue;
2408 if (logging != MagickFalse)
2409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2410 " Found PNG iCCP chunk.");
2411 }
2412
glennrp98b83d42012-07-23 02:50:31 +00002413 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2414 {
2415 ping_found_gAMA=MagickTrue;
2416 if (logging != MagickFalse)
2417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2418 " Found PNG gAMA chunk.");
2419 }
2420
2421 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2422 {
2423 ping_found_cHRM=MagickTrue;
2424 if (logging != MagickFalse)
2425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2426 " Found PNG cHRM chunk.");
2427 }
2428
glennrpecab7d72013-05-14 22:50:32 +00002429 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2430 PNG_INFO_sRGB))
glennrp98b83d42012-07-23 02:50:31 +00002431 {
2432 ping_found_sRGB=MagickTrue;
2433 if (logging != MagickFalse)
2434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2435 " Found PNG sRGB chunk.");
2436 }
2437
glennrpfaa852b2010-03-30 12:17:00 +00002438#ifdef PNG_READ_iCCP_SUPPORTED
glennrpe604a752014-06-28 02:24:30 +00002439 if (ping_found_iCCP !=MagickTrue &&
glennrpecab7d72013-05-14 22:50:32 +00002440 ping_found_sRGB != MagickTrue &&
2441 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2442 {
2443 ping_found_iCCP=MagickTrue;
2444 if (logging != MagickFalse)
2445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2446 " Found PNG iCCP chunk.");
2447 }
2448
glennrpfaa852b2010-03-30 12:17:00 +00002449 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002450 {
2451 int
2452 compression;
2453
glennrpe4017e32011-01-08 17:16:09 +00002454#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002455 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002456 info;
2457#else
2458 png_bytep
2459 info;
2460#endif
2461
2462 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002463 name;
2464
2465 png_uint_32
2466 profile_length;
2467
2468 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2469 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002470
cristy3ed852e2009-09-05 21:47:34 +00002471 if (profile_length != 0)
2472 {
2473 StringInfo
2474 *profile;
2475
2476 if (logging != MagickFalse)
2477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2478 " Reading PNG iCCP chunk.");
glennrpecab7d72013-05-14 22:50:32 +00002479
cristye8f8f382011-09-01 13:32:37 +00002480 profile=BlobToStringInfo(info,profile_length);
glennrpecab7d72013-05-14 22:50:32 +00002481
cristye8f8f382011-09-01 13:32:37 +00002482 if (profile == (StringInfo *) NULL)
glennrpecab7d72013-05-14 22:50:32 +00002483 {
2484 png_warning(ping, "ICC profile is NULL");
2485 profile=DestroyStringInfo(profile);
2486 }
glennrpedaa0382012-04-12 14:16:21 +00002487 else
glennrpecab7d72013-05-14 22:50:32 +00002488 {
2489 if (ping_preserve_iCCP == MagickFalse)
glennrpedaa0382012-04-12 14:16:21 +00002490 {
glennrpecab7d72013-05-14 22:50:32 +00002491 int
2492 icheck,
2493 got_crc=0;
2494
2495
2496 png_uint_32
2497 length,
2498 profile_crc=0;
2499
2500 unsigned char
2501 *data;
2502
2503 length=(png_uint_32) GetStringInfoLength(profile);
2504
2505 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2506 {
2507 if (length == sRGB_info[icheck].len)
2508 {
2509 if (got_crc == 0)
2510 {
2511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2512 " Got a %lu-byte ICC profile (potentially sRGB)",
2513 (unsigned long) length);
2514
2515 data=GetStringInfoDatum(profile);
2516 profile_crc=crc32(0,data,length);
2517
2518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2519 " with crc=%8x",(unsigned int) profile_crc);
2520 got_crc++;
2521 }
2522
2523 if (profile_crc == sRGB_info[icheck].crc)
2524 {
2525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2526 " It is sRGB with rendering intent = %s",
2527 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2528 sRGB_info[icheck].intent));
2529 if (image->rendering_intent==UndefinedIntent)
2530 {
2531 image->rendering_intent=
2532 Magick_RenderingIntent_from_PNG_RenderingIntent(
2533 sRGB_info[icheck].intent);
2534 }
2535 break;
2536 }
2537 }
2538 }
2539 if (sRGB_info[icheck].len == 0)
2540 {
2541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2542 " Got a %lu-byte ICC profile not recognized as sRGB",
2543 (unsigned long) length);
2544 (void) SetImageProfile(image,"icc",profile,exception);
2545 }
glennrpedaa0382012-04-12 14:16:21 +00002546 }
glennrpecab7d72013-05-14 22:50:32 +00002547 else /* Preserve-iCCP */
2548 {
2549 (void) SetImageProfile(image,"icc",profile,exception);
2550 }
2551
2552 profile=DestroyStringInfo(profile);
2553 }
cristy3ed852e2009-09-05 21:47:34 +00002554 }
2555 }
2556#endif
glennrpecab7d72013-05-14 22:50:32 +00002557
cristy3ed852e2009-09-05 21:47:34 +00002558#if defined(PNG_READ_sRGB_SUPPORTED)
2559 {
glennrpecab7d72013-05-14 22:50:32 +00002560 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2561 PNG_INFO_sRGB))
2562 {
2563 if (png_get_sRGB(ping,ping_info,&intent))
cristy2ea7a8e2012-02-08 01:04:50 +00002564 {
glennrpecab7d72013-05-14 22:50:32 +00002565 if (image->rendering_intent == UndefinedIntent)
2566 image->rendering_intent=
2567 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002568
cristy3ed852e2009-09-05 21:47:34 +00002569 if (logging != MagickFalse)
2570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002571 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002572 }
glennrpecab7d72013-05-14 22:50:32 +00002573 }
2574
2575 else if (mng_info->have_global_srgb)
2576 {
2577 if (image->rendering_intent == UndefinedIntent)
2578 image->rendering_intent=
2579 Magick_RenderingIntent_from_PNG_RenderingIntent
2580 (mng_info->global_srgb_intent);
2581 }
cristy3ed852e2009-09-05 21:47:34 +00002582 }
2583#endif
glennrpecab7d72013-05-14 22:50:32 +00002584
2585
cristy3ed852e2009-09-05 21:47:34 +00002586 {
glennrpfaa852b2010-03-30 12:17:00 +00002587 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2588 if (mng_info->have_global_gama)
2589 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002590
cristy3ed852e2009-09-05 21:47:34 +00002591 if (png_get_gAMA(ping,ping_info,&file_gamma))
2592 {
2593 image->gamma=(float) file_gamma;
2594 if (logging != MagickFalse)
2595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2596 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2597 }
2598 }
glennrp98b83d42012-07-23 02:50:31 +00002599
glennrpfaa852b2010-03-30 12:17:00 +00002600 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2601 {
2602 if (mng_info->have_global_chrm != MagickFalse)
2603 {
2604 (void) png_set_cHRM(ping,ping_info,
2605 mng_info->global_chrm.white_point.x,
2606 mng_info->global_chrm.white_point.y,
2607 mng_info->global_chrm.red_primary.x,
2608 mng_info->global_chrm.red_primary.y,
2609 mng_info->global_chrm.green_primary.x,
2610 mng_info->global_chrm.green_primary.y,
2611 mng_info->global_chrm.blue_primary.x,
2612 mng_info->global_chrm.blue_primary.y);
2613 }
2614 }
glennrp0fe50b42010-11-16 03:52:51 +00002615
glennrpfaa852b2010-03-30 12:17:00 +00002616 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002617 {
2618 (void) png_get_cHRM(ping,ping_info,
2619 &image->chromaticity.white_point.x,
2620 &image->chromaticity.white_point.y,
2621 &image->chromaticity.red_primary.x,
2622 &image->chromaticity.red_primary.y,
2623 &image->chromaticity.green_primary.x,
2624 &image->chromaticity.green_primary.y,
2625 &image->chromaticity.blue_primary.x,
2626 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002627
glennrpecab7d72013-05-14 22:50:32 +00002628 ping_found_cHRM=MagickTrue;
2629
glennrp3d627862013-02-26 00:19:34 +00002630 if (image->chromaticity.red_primary.x>0.6399f &&
2631 image->chromaticity.red_primary.x<0.6401f &&
2632 image->chromaticity.red_primary.y>0.3299f &&
2633 image->chromaticity.red_primary.y<0.3301f &&
2634 image->chromaticity.green_primary.x>0.2999f &&
2635 image->chromaticity.green_primary.x<0.3001f &&
2636 image->chromaticity.green_primary.y>0.5999f &&
2637 image->chromaticity.green_primary.y<0.6001f &&
2638 image->chromaticity.blue_primary.x>0.1499f &&
2639 image->chromaticity.blue_primary.x<0.1501f &&
2640 image->chromaticity.blue_primary.y>0.0599f &&
2641 image->chromaticity.blue_primary.y<0.0601f &&
2642 image->chromaticity.white_point.x>0.3126f &&
2643 image->chromaticity.white_point.x<0.3128f &&
2644 image->chromaticity.white_point.y>0.3289f &&
2645 image->chromaticity.white_point.y<0.3291f)
2646 ping_found_sRGB_cHRM=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002647 }
glennrp0fe50b42010-11-16 03:52:51 +00002648
glennrpe610a072010-08-05 17:08:46 +00002649 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002650 {
glennrp3d627862013-02-26 00:19:34 +00002651 if (ping_found_sRGB != MagickTrue &&
2652 (ping_found_gAMA != MagickTrue ||
glennrp84288232013-03-02 15:52:36 +00002653 (image->gamma > .45 && image->gamma < .46)) &&
glennrp3d627862013-02-26 00:19:34 +00002654 (ping_found_cHRM != MagickTrue ||
cristycd8b3312013-12-22 01:51:11 +00002655 ping_found_sRGB_cHRM != MagickFalse) &&
glennrp3d627862013-02-26 00:19:34 +00002656 ping_found_iCCP != MagickTrue)
glennrp5cf1bff2013-01-16 02:44:03 +00002657 {
2658 png_set_sRGB(ping,ping_info,
2659 Magick_RenderingIntent_to_PNG_RenderingIntent
2660 (image->rendering_intent));
glennrp5cf1bff2013-01-16 02:44:03 +00002661 file_gamma=1.000f/2.200f;
2662 ping_found_sRGB=MagickTrue;
glennrp84288232013-03-02 15:52:36 +00002663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp918b9dc2013-04-03 13:41:41 +00002664 " Setting sRGB as if in input");
glennrp5cf1bff2013-01-16 02:44:03 +00002665 }
cristy3ed852e2009-09-05 21:47:34 +00002666 }
glennrp5cf1bff2013-01-16 02:44:03 +00002667
cristy3ed852e2009-09-05 21:47:34 +00002668#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002669 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002670 {
cristy905ef802011-02-23 00:29:18 +00002671 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2672 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002673
cristy3ed852e2009-09-05 21:47:34 +00002674 if (logging != MagickFalse)
2675 if (image->page.x || image->page.y)
2676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002677 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2678 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002679 }
2680#endif
2681#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002682 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2683 {
2684 if (mng_info->have_global_phys)
2685 {
2686 png_set_pHYs(ping,ping_info,
2687 mng_info->global_x_pixels_per_unit,
2688 mng_info->global_y_pixels_per_unit,
2689 mng_info->global_phys_unit_type);
2690 }
2691 }
2692
cristy5c97f622014-01-15 12:36:09 +00002693 x_resolution=0;
2694 y_resolution=0;
2695 unit_type=0;
glennrpfaa852b2010-03-30 12:17:00 +00002696 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002697 {
cristy3ed852e2009-09-05 21:47:34 +00002698 /*
2699 Set image resolution.
2700 */
2701 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002702 &unit_type);
cristy16ea1392012-03-21 20:38:41 +00002703 image->resolution.x=(double) x_resolution;
2704 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002705
cristy3ed852e2009-09-05 21:47:34 +00002706 if (unit_type == PNG_RESOLUTION_METER)
2707 {
2708 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00002709 image->resolution.x=(double) x_resolution/100.0;
2710 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002711 }
glennrp0fe50b42010-11-16 03:52:51 +00002712
cristy3ed852e2009-09-05 21:47:34 +00002713 if (logging != MagickFalse)
2714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002715 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2716 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002717 }
cristy3ed852e2009-09-05 21:47:34 +00002718#endif
glennrp823b55c2011-03-14 18:46:46 +00002719
glennrpfaa852b2010-03-30 12:17:00 +00002720 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002721 {
cristy3ed852e2009-09-05 21:47:34 +00002722 png_colorp
2723 palette;
2724
2725 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002726
cristy3ed852e2009-09-05 21:47:34 +00002727 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002728 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002729 {
2730 if (mng_info->global_plte_length)
2731 {
2732 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2733 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002734
glennrpfaa852b2010-03-30 12:17:00 +00002735 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrpedaa0382012-04-12 14:16:21 +00002736 {
cristy3ed852e2009-09-05 21:47:34 +00002737 if (mng_info->global_trns_length)
2738 {
glennrpedaa0382012-04-12 14:16:21 +00002739 png_warning(ping,
2740 "global tRNS has more entries than global PLTE");
cristy3ed852e2009-09-05 21:47:34 +00002741 }
glennrpedaa0382012-04-12 14:16:21 +00002742 else
2743 {
2744 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2745 (int) mng_info->global_trns_length,NULL);
2746 }
2747 }
glennrpbfd9e612011-04-22 14:02:20 +00002748#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002749 if (
2750#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2751 mng_info->have_saved_bkgd_index ||
2752#endif
glennrpfaa852b2010-03-30 12:17:00 +00002753 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002754 {
2755 png_color_16
2756 background;
2757
2758#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2759 if (mng_info->have_saved_bkgd_index)
2760 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002761#endif
glennrpfaa852b2010-03-30 12:17:00 +00002762 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2763 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002764
cristy3ed852e2009-09-05 21:47:34 +00002765 background.red=(png_uint_16)
2766 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002767
cristy3ed852e2009-09-05 21:47:34 +00002768 background.green=(png_uint_16)
2769 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002770
cristy3ed852e2009-09-05 21:47:34 +00002771 background.blue=(png_uint_16)
2772 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002773
glennrpc6c391a2011-04-27 02:23:56 +00002774 background.gray=(png_uint_16)
2775 mng_info->global_plte[background.index].green;
2776
cristy3ed852e2009-09-05 21:47:34 +00002777 png_set_bKGD(ping,ping_info,&background);
2778 }
2779#endif
2780 }
2781 else
glennrpedaa0382012-04-12 14:16:21 +00002782 png_error(ping,"No global PLTE in file");
cristy3ed852e2009-09-05 21:47:34 +00002783 }
2784 }
2785
glennrpbfd9e612011-04-22 14:02:20 +00002786#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002787 if (mng_info->have_global_bkgd &&
2788 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002789 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002790
glennrpfaa852b2010-03-30 12:17:00 +00002791 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002792 {
glennrpbfd9e612011-04-22 14:02:20 +00002793 unsigned int
2794 bkgd_scale;
2795
glennrp2dd19062014-07-28 00:22:24 +00002796 /* Set image background color.
2797 * Scale background components to 16-bit, then scale
glennrpbfd9e612011-04-22 14:02:20 +00002798 * to quantum depth
2799 */
glennrp0fe50b42010-11-16 03:52:51 +00002800
glennrpbfd9e612011-04-22 14:02:20 +00002801 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002802
glennrpfcf06162012-11-05 14:57:08 +00002803 if (ping_file_depth == 1)
glennrpbfd9e612011-04-22 14:02:20 +00002804 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002805
glennrpfcf06162012-11-05 14:57:08 +00002806 else if (ping_file_depth == 2)
glennrpbfd9e612011-04-22 14:02:20 +00002807 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002808
glennrpfcf06162012-11-05 14:57:08 +00002809 else if (ping_file_depth == 4)
glennrpbfd9e612011-04-22 14:02:20 +00002810 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002811
glennrpfcf06162012-11-05 14:57:08 +00002812 if (ping_file_depth <= 8)
glennrpbfd9e612011-04-22 14:02:20 +00002813 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002814
glennrpbfd9e612011-04-22 14:02:20 +00002815 ping_background->red *= bkgd_scale;
2816 ping_background->green *= bkgd_scale;
2817 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002818
glennrpbfd9e612011-04-22 14:02:20 +00002819 if (logging != MagickFalse)
2820 {
glennrp2dd19062014-07-28 00:22:24 +00002821 if (logging != MagickFalse)
2822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2823 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d).\n"
2824 " bkgd_scale=%d. ping_background=(%d,%d,%d).",
2825 ping_background->red,ping_background->green,
2826 ping_background->blue,
2827 bkgd_scale,ping_background->red,
2828 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002829 }
glennrp2cbb4482010-06-02 04:37:24 +00002830
glennrpbfd9e612011-04-22 14:02:20 +00002831 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002832 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002833
glennrpbfd9e612011-04-22 14:02:20 +00002834 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002835 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002836
glennrpbfd9e612011-04-22 14:02:20 +00002837 image->background_color.blue=
2838 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002839
cristy16ea1392012-03-21 20:38:41 +00002840 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002841
glennrpbfd9e612011-04-22 14:02:20 +00002842 if (logging != MagickFalse)
2843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2844 " image->background_color=(%.20g,%.20g,%.20g).",
2845 (double) image->background_color.red,
2846 (double) image->background_color.green,
2847 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002848 }
glennrpbfd9e612011-04-22 14:02:20 +00002849#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002850
glennrpfaa852b2010-03-30 12:17:00 +00002851 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002852 {
2853 /*
glennrpa6a06632011-01-19 15:15:34 +00002854 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002855 */
2856 int
2857 max_sample;
2858
cristy35ef8242010-06-03 16:24:13 +00002859 size_t
2860 one=1;
2861
cristy3ed852e2009-09-05 21:47:34 +00002862 if (logging != MagickFalse)
2863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2864 " Reading PNG tRNS chunk.");
2865
glennrpfcf06162012-11-05 14:57:08 +00002866 max_sample = (int) ((one << ping_file_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002867
glennrpfaa852b2010-03-30 12:17:00 +00002868 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2869 (int)ping_trans_color->gray > max_sample) ||
2870 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2871 ((int)ping_trans_color->red > max_sample ||
2872 (int)ping_trans_color->green > max_sample ||
2873 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002874 {
2875 if (logging != MagickFalse)
2876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2877 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002878 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002879 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy8a46d822012-08-28 23:32:39 +00002880 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00002881 }
2882 else
2883 {
glennrpa6a06632011-01-19 15:15:34 +00002884 int
2885 scale_to_short;
2886
glennrpfcf06162012-11-05 14:57:08 +00002887 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
glennrpa6a06632011-01-19 15:15:34 +00002888
2889 /* Scale transparent_color to short */
2890 transparent_color.red= scale_to_short*ping_trans_color->red;
2891 transparent_color.green= scale_to_short*ping_trans_color->green;
2892 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy16ea1392012-03-21 20:38:41 +00002893 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002894
glennrpfaa852b2010-03-30 12:17:00 +00002895 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002896 {
glennrp0f111982010-07-07 20:18:33 +00002897 if (logging != MagickFalse)
2898 {
2899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00002900 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
2901 (int) ping_trans_color->gray,(int) transparent_color.alpha);
glennrp0fe50b42010-11-16 03:52:51 +00002902
glennrp0f111982010-07-07 20:18:33 +00002903 }
cristy16ea1392012-03-21 20:38:41 +00002904 transparent_color.red=transparent_color.alpha;
2905 transparent_color.green=transparent_color.alpha;
2906 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002907 }
2908 }
2909 }
2910#if defined(PNG_READ_sBIT_SUPPORTED)
2911 if (mng_info->have_global_sbit)
2912 {
glennrpfaa852b2010-03-30 12:17:00 +00002913 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002914 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2915 }
2916#endif
2917 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002918
cristy3ed852e2009-09-05 21:47:34 +00002919 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002920
2921 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2922
cristy3ed852e2009-09-05 21:47:34 +00002923 /*
2924 Initialize image structure.
2925 */
2926 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002927 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002928 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002929 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002930 if (mng_info->mng_type == 0)
2931 {
glennrpfaa852b2010-03-30 12:17:00 +00002932 mng_info->mng_width=ping_width;
2933 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002934 mng_info->frame=mng_info->image_box;
2935 mng_info->clip=mng_info->image_box;
2936 }
glennrp0fe50b42010-11-16 03:52:51 +00002937
cristy3ed852e2009-09-05 21:47:34 +00002938 else
2939 {
2940 image->page.y=mng_info->y_off[mng_info->object_id];
2941 }
glennrp0fe50b42010-11-16 03:52:51 +00002942
cristy3ed852e2009-09-05 21:47:34 +00002943 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002944 image->columns=ping_width;
2945 image->rows=ping_height;
glennrpa6c5d342012-05-14 13:21:17 +00002946
cristy16ea1392012-03-21 20:38:41 +00002947 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2948 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
glennrp8d0bca52012-05-17 02:33:23 +00002949 {
glennrp0a5b92c2014-03-16 22:08:03 +00002950 double
2951 image_gamma = image->gamma;
2952
2953 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2954 " image->gamma=%f",(float) image_gamma);
2955
2956 if (image_gamma > 0.75)
glennrp8d0bca52012-05-17 02:33:23 +00002957 {
glennrp0a5b92c2014-03-16 22:08:03 +00002958 /* Set image->rendering_intent to Undefined,
glennrpe88af772012-08-22 13:59:50 +00002959 * image->colorspace to GRAY, and reset image->chromaticity.
glennrp8d0bca52012-05-17 02:33:23 +00002960 */
glennrp67429932013-10-05 14:59:56 +00002961 image->intensity = Rec709LuminancePixelIntensityMethod;
glennrp8d0bca52012-05-17 02:33:23 +00002962 SetImageColorspace(image,GRAYColorspace,exception);
2963 }
glennrpccc36af2014-09-08 23:22:03 +00002964 else
2965 {
2966 RenderingIntent
2967 save_rendering_intent = image->rendering_intent;
2968 ChromaticityInfo
2969 save_chromaticity = image->chromaticity;
2970
2971 SetImageColorspace(image,GRAYColorspace,exception);
2972 image->rendering_intent = save_rendering_intent;
2973 image->chromaticity = save_chromaticity;
2974 }
2975
2976 image->gamma = image_gamma;
glennrp8d0bca52012-05-17 02:33:23 +00002977 }
glennrpe88af772012-08-22 13:59:50 +00002978
2979 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0a5b92c2014-03-16 22:08:03 +00002980 " image->colorspace=%d",(int) image->colorspace);
glennrpa6c5d342012-05-14 13:21:17 +00002981
glennrpfaa852b2010-03-30 12:17:00 +00002982 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrp32340ff2012-08-25 17:36:19 +00002983 ((int) ping_bit_depth < 16 &&
2984 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002985 {
cristybefe4d22010-06-07 01:18:58 +00002986 size_t
2987 one;
2988
cristy3ed852e2009-09-05 21:47:34 +00002989 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002990 one=1;
glennrpfcf06162012-11-05 14:57:08 +00002991 image->colors=one << ping_file_depth;
cristy3ed852e2009-09-05 21:47:34 +00002992#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2993 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002994 image->colors=256;
2995#else
2996 if (image->colors > 65536L)
2997 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002998#endif
glennrpfaa852b2010-03-30 12:17:00 +00002999 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00003000 {
cristy3ed852e2009-09-05 21:47:34 +00003001 png_colorp
3002 palette;
3003
3004 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00003005 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00003006
cristy3ed852e2009-09-05 21:47:34 +00003007 if (logging != MagickFalse)
3008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3009 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3010 }
3011 }
3012
3013 if (image->storage_class == PseudoClass)
3014 {
3015 /*
3016 Initialize image colormap.
3017 */
cristy16ea1392012-03-21 20:38:41 +00003018 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
glennrpedaa0382012-04-12 14:16:21 +00003019 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003020
glennrpfaa852b2010-03-30 12:17:00 +00003021 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00003022 {
cristy3ed852e2009-09-05 21:47:34 +00003023 png_colorp
3024 palette;
3025
3026 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00003027
glennrp6af6cf12011-04-22 13:05:16 +00003028 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00003029 {
3030 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3031 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3032 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3033 }
glennrp6af6cf12011-04-22 13:05:16 +00003034
glennrp67b9c1a2011-04-22 18:47:36 +00003035 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00003036 {
3037 image->colormap[i].red=0;
3038 image->colormap[i].green=0;
3039 image->colormap[i].blue=0;
3040 }
cristy3ed852e2009-09-05 21:47:34 +00003041 }
glennrp0fe50b42010-11-16 03:52:51 +00003042
cristy3ed852e2009-09-05 21:47:34 +00003043 else
3044 {
cristybb503372010-05-27 20:51:26 +00003045 size_t
cristy3ed852e2009-09-05 21:47:34 +00003046 scale;
3047
glennrpfcf06162012-11-05 14:57:08 +00003048 scale=(QuantumRange/((1UL << ping_file_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00003049
cristy3ed852e2009-09-05 21:47:34 +00003050 if (scale < 1)
3051 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00003052
cristybb503372010-05-27 20:51:26 +00003053 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00003054 {
3055 image->colormap[i].red=(Quantum) (i*scale);
3056 image->colormap[i].green=(Quantum) (i*scale);
3057 image->colormap[i].blue=(Quantum) (i*scale);
3058 }
3059 }
3060 }
glennrp147bc912011-03-30 18:47:21 +00003061
glennrpcb395ac2011-03-30 19:50:23 +00003062 /* Set some properties for reporting by "identify" */
3063 {
glennrp147bc912011-03-30 18:47:21 +00003064 char
3065 msg[MaxTextExtent];
3066
glennrpfcf06162012-11-05 14:57:08 +00003067 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
glennrp147bc912011-03-30 18:47:21 +00003068 ping_interlace_method in value */
3069
cristy3b6fd2e2011-05-20 12:53:50 +00003070 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00003071 "%d, %d",(int) ping_width, (int) ping_height);
glennrp3398b5b2013-05-03 23:10:31 +00003072 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00003073
glennrpfcf06162012-11-05 14:57:08 +00003074 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
glennrp3398b5b2013-05-03 23:10:31 +00003075 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00003076
glennrp5dff4352012-06-06 22:12:04 +00003077 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3078 (int) ping_color_type,
3079 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
glennrp3398b5b2013-05-03 23:10:31 +00003080 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00003081
glennrp913f9612012-06-27 17:50:00 +00003082 if (ping_interlace_method == 0)
3083 {
3084 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3085 (int) ping_interlace_method);
3086 }
3087 else if (ping_interlace_method == 1)
3088 {
3089 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3090 (int) ping_interlace_method);
3091 }
3092 else
3093 {
3094 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3095 (int) ping_interlace_method);
3096 }
3097 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3098
3099 if (number_colors != 0)
3100 {
3101 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3102 (int) number_colors);
glennrp3398b5b2013-05-03 23:10:31 +00003103 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
glennrp913f9612012-06-27 17:50:00 +00003104 exception);
3105 }
dirkfd6fd072014-10-24 21:10:08 +00003106
3107#if defined(PNG_tIME_SUPPORTED)
3108 read_tIME_chunk(image,ping,ping_info,exception);
3109#endif
glennrpcb395ac2011-03-30 19:50:23 +00003110 }
glennrp147bc912011-03-30 18:47:21 +00003111
cristy3ed852e2009-09-05 21:47:34 +00003112 /*
3113 Read image scanlines.
3114 */
3115 if (image->delay != 0)
3116 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00003117
glennrp0ca69b12010-07-26 01:57:52 +00003118 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00003119 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3120 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00003121 {
glennrp1b888c42012-03-02 15:18:14 +00003122 /* This happens later in non-ping decodes */
3123 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3124 image->storage_class=DirectClass;
3125
cristy3ed852e2009-09-05 21:47:34 +00003126 if (logging != MagickFalse)
3127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003128 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00003129 mng_info->scenes_found-1);
3130 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00003131
glennrp868fff32014-03-16 22:09:06 +00003132#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00003133 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003134#endif
glennrpedaa0382012-04-12 14:16:21 +00003135
cristy3ed852e2009-09-05 21:47:34 +00003136 if (logging != MagickFalse)
3137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3138 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00003139
cristy3ed852e2009-09-05 21:47:34 +00003140 return(image);
3141 }
glennrp0fe50b42010-11-16 03:52:51 +00003142
cristy3ed852e2009-09-05 21:47:34 +00003143 if (logging != MagickFalse)
3144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3145 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00003146
cristy3ed852e2009-09-05 21:47:34 +00003147 if (num_passes > 1)
cristy09973322013-06-30 21:06:30 +00003148 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
glennrpcf002022011-01-30 02:38:15 +00003149 sizeof(*ping_pixels));
cristy09973322013-06-30 21:06:30 +00003150 else
3151 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00003152
cristy09973322013-06-30 21:06:30 +00003153 if (pixel_info == (MemoryInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003154 png_error(ping,"Memory allocation failed");
cristy09973322013-06-30 21:06:30 +00003155 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
glennrp0fe50b42010-11-16 03:52:51 +00003156
cristy3ed852e2009-09-05 21:47:34 +00003157 if (logging != MagickFalse)
3158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3159 " Converting PNG pixels to pixel packets");
3160 /*
3161 Convert PNG pixels to pixel packets.
3162 */
cristy16ea1392012-03-21 20:38:41 +00003163 quantum_info=AcquireQuantumInfo(image_info,image);
3164
3165 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003166 png_error(ping,"Failed to allocate quantum_info");
glennrp0fe50b42010-11-16 03:52:51 +00003167
glennrp4b840d72012-11-22 16:01:16 +00003168 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3169
glennrpc8cbc5d2011-01-01 00:12:34 +00003170 {
3171
3172 MagickBooleanType
3173 found_transparent_pixel;
3174
3175 found_transparent_pixel=MagickFalse;
3176
cristy3ed852e2009-09-05 21:47:34 +00003177 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00003178 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003179 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00003180 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003181 /*
3182 Convert image to DirectClass pixel packets.
3183 */
glennrpccc36af2014-09-08 23:22:03 +00003184 image->alpha_trait=
3185 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
glennrpc8cbc5d2011-01-01 00:12:34 +00003186 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3187 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
cristyb0a657e2012-08-29 00:45:37 +00003188 BlendPixelTrait : UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00003189
glennrpc8cbc5d2011-01-01 00:12:34 +00003190 for (y=0; y < (ssize_t) image->rows; y++)
3191 {
3192 if (num_passes > 1)
3193 row_offset=ping_rowbytes*y;
3194
3195 else
3196 row_offset=0;
3197
glennrpcf002022011-01-30 02:38:15 +00003198 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc5c31be2012-11-07 18:42:18 +00003199
3200 if (pass < num_passes-1)
3201 continue;
3202
cristy862a33c2012-05-17 22:49:37 +00003203 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003204
cristy16ea1392012-03-21 20:38:41 +00003205 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00003206 break;
3207
cristy16ea1392012-03-21 20:38:41 +00003208 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3209 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3210 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003211
cristy16ea1392012-03-21 20:38:41 +00003212 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3213 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3214 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003215
cristy16ea1392012-03-21 20:38:41 +00003216 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3217 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3218 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003219
cristy16ea1392012-03-21 20:38:41 +00003220 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3221 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3222 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003223
cristy16ea1392012-03-21 20:38:41 +00003224 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3225 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3226 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00003227
glennrpc8cbc5d2011-01-01 00:12:34 +00003228 if (found_transparent_pixel == MagickFalse)
3229 {
3230 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00003231 if (y== 0 && logging != MagickFalse)
3232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3233 " Looking for cheap transparent pixel");
3234
glennrpc8cbc5d2011-01-01 00:12:34 +00003235 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3236 {
glennrp5aa37f62011-01-02 03:07:57 +00003237 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3238 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy16ea1392012-03-21 20:38:41 +00003239 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00003240 {
glennrpa6a06632011-01-19 15:15:34 +00003241 if (logging != MagickFalse)
3242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3243 " ...got one.");
3244
glennrpc8cbc5d2011-01-01 00:12:34 +00003245 found_transparent_pixel = MagickTrue;
3246 break;
3247 }
glennrp4f25bd02011-01-01 18:51:28 +00003248 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3249 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
cristy16ea1392012-03-21 20:38:41 +00003250 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3251 transparent_color.red &&
3252 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3253 transparent_color.green &&
3254 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3255 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00003256 {
glennrpa6a06632011-01-19 15:15:34 +00003257 if (logging != MagickFalse)
3258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3259 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00003260 found_transparent_pixel = MagickTrue;
3261 break;
3262 }
cristy16ea1392012-03-21 20:38:41 +00003263 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00003264 }
3265 }
3266
3267 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3268 {
3269 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3270 image->rows);
3271
3272 if (status == MagickFalse)
3273 break;
3274 }
3275 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3276 break;
3277 }
3278
3279 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3280 {
3281 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003282 if (status == MagickFalse)
3283 break;
3284 }
cristy3ed852e2009-09-05 21:47:34 +00003285 }
cristy3ed852e2009-09-05 21:47:34 +00003286 }
glennrp0fe50b42010-11-16 03:52:51 +00003287
cristy3ed852e2009-09-05 21:47:34 +00003288 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003289
cristy3ed852e2009-09-05 21:47:34 +00003290 for (pass=0; pass < num_passes; pass++)
3291 {
3292 Quantum
3293 *quantum_scanline;
3294
3295 register Quantum
3296 *r;
3297
3298 /*
3299 Convert grayscale image to PseudoClass pixel packets.
3300 */
glennrpc17d96f2011-06-27 01:20:11 +00003301 if (logging != MagickFalse)
3302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3303 " Converting grayscale pixels to pixel packets");
cristy16ea1392012-03-21 20:38:41 +00003304
cristy8a46d822012-08-28 23:32:39 +00003305 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristyb0a657e2012-08-29 00:45:37 +00003306 BlendPixelTrait : UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00003307
cristy3ed852e2009-09-05 21:47:34 +00003308 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
cristyb0a657e2012-08-29 00:45:37 +00003309 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3310 sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003311
cristy3ed852e2009-09-05 21:47:34 +00003312 if (quantum_scanline == (Quantum *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003313 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003314
cristybb503372010-05-27 20:51:26 +00003315 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003316 {
glennrp4f996392013-01-26 22:56:50 +00003317 Quantum
3318 alpha;
3319
cristy3ed852e2009-09-05 21:47:34 +00003320 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003321 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003322
cristy3ed852e2009-09-05 21:47:34 +00003323 else
3324 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003325
glennrpcf002022011-01-30 02:38:15 +00003326 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc5c31be2012-11-07 18:42:18 +00003327
3328 if (pass < num_passes-1)
3329 continue;
3330
glennrp4f996392013-01-26 22:56:50 +00003331 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003332
cristy16ea1392012-03-21 20:38:41 +00003333 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003334 break;
glennrp0fe50b42010-11-16 03:52:51 +00003335
glennrpcf002022011-01-30 02:38:15 +00003336 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003337 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003338
glennrpfaa852b2010-03-30 12:17:00 +00003339 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003340 {
cristy3ed852e2009-09-05 21:47:34 +00003341 case 8:
3342 {
glennrp4f996392013-01-26 22:56:50 +00003343
glennrpfaa852b2010-03-30 12:17:00 +00003344 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003345 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003346 {
glennrpa18d5bc2011-04-23 14:51:34 +00003347 *r++=*p++;
glennrp4f996392013-01-26 22:56:50 +00003348
3349 alpha=ScaleCharToQuantum((unsigned char)*p++);
3350
3351 SetPixelAlpha(image,alpha,q);
3352
3353 if (alpha != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003354 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003355
cristy16ea1392012-03-21 20:38:41 +00003356 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003357 }
glennrp0fe50b42010-11-16 03:52:51 +00003358
cristy3ed852e2009-09-05 21:47:34 +00003359 else
cristybb503372010-05-27 20:51:26 +00003360 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003361 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003362
cristy3ed852e2009-09-05 21:47:34 +00003363 break;
3364 }
glennrp47b9dd52010-11-24 18:12:06 +00003365
cristy3ed852e2009-09-05 21:47:34 +00003366 case 16:
3367 {
cristybb503372010-05-27 20:51:26 +00003368 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003369 {
glennrpc17d96f2011-06-27 01:20:11 +00003370#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
cristy87281ec2013-05-08 11:50:14 +00003371 unsigned short
glennrp58f77c72011-04-23 14:09:09 +00003372 quantum;
3373
3374 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003375 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003376
3377 else
glennrpc17d96f2011-06-27 01:20:11 +00003378 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003379
glennrp58f77c72011-04-23 14:09:09 +00003380 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003381 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003382 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003383
3384 if (ping_color_type == 4)
3385 {
glennrpc17d96f2011-06-27 01:20:11 +00003386 if (image->colors > 256)
3387 quantum=((*p++) << 8);
3388 else
3389 quantum=0;
3390
glennrp58f77c72011-04-23 14:09:09 +00003391 quantum|=(*p++);
glennrp4f996392013-01-26 22:56:50 +00003392
3393 alpha=ScaleShortToQuantum(quantum);
3394 SetPixelAlpha(image,alpha,q);
3395
3396 if (alpha != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003397 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003398
cristy16ea1392012-03-21 20:38:41 +00003399 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003400 }
glennrp58f77c72011-04-23 14:09:09 +00003401
3402#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3403 *r++=(*p++);
3404 p++; /* strip low byte */
3405
3406 if (ping_color_type == 4)
3407 {
cristy16ea1392012-03-21 20:38:41 +00003408 SetPixelAlpha(image,*p++,q);
glennrp4f996392013-01-26 22:56:50 +00003409
cristy16ea1392012-03-21 20:38:41 +00003410 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003411 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003412
glennrp9d0ea4d2011-04-22 18:35:57 +00003413 p++;
cristy16ea1392012-03-21 20:38:41 +00003414 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003415 }
cristy3ed852e2009-09-05 21:47:34 +00003416#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003417 }
glennrp47b9dd52010-11-24 18:12:06 +00003418
cristy3ed852e2009-09-05 21:47:34 +00003419 break;
3420 }
glennrp47b9dd52010-11-24 18:12:06 +00003421
cristy3ed852e2009-09-05 21:47:34 +00003422 default:
3423 break;
3424 }
glennrp3faa9a32011-04-23 14:00:25 +00003425
cristy3ed852e2009-09-05 21:47:34 +00003426 /*
3427 Transfer image scanline.
3428 */
3429 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003430
cristy16ea1392012-03-21 20:38:41 +00003431 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3432
3433 if (q == (Quantum *) NULL)
3434 break;
cristybb503372010-05-27 20:51:26 +00003435 for (x=0; x < (ssize_t) image->columns; x++)
cristy16ea1392012-03-21 20:38:41 +00003436 {
3437 SetPixelIndex(image,*r++,q);
3438 q+=GetPixelChannels(image);
3439 }
glennrp0fe50b42010-11-16 03:52:51 +00003440
cristy3ed852e2009-09-05 21:47:34 +00003441 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3442 break;
glennrp0fe50b42010-11-16 03:52:51 +00003443
cristy7a287bf2010-02-14 02:18:09 +00003444 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3445 {
cristycee97112010-05-28 00:44:52 +00003446 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003447 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003448
cristy7a287bf2010-02-14 02:18:09 +00003449 if (status == MagickFalse)
3450 break;
3451 }
cristy3ed852e2009-09-05 21:47:34 +00003452 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003453
cristy7a287bf2010-02-14 02:18:09 +00003454 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003455 {
3456 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003457
cristy3ed852e2009-09-05 21:47:34 +00003458 if (status == MagickFalse)
3459 break;
3460 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003461
cristy3ed852e2009-09-05 21:47:34 +00003462 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3463 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003464
cristyb0a657e2012-08-29 00:45:37 +00003465 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3466 UndefinedPixelTrait;
glennrpc8cbc5d2011-01-01 00:12:34 +00003467
3468 if (logging != MagickFalse)
3469 {
3470 if (found_transparent_pixel != MagickFalse)
3471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3472 " Found transparent pixel");
3473 else
glennrp5aa37f62011-01-02 03:07:57 +00003474 {
3475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3476 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003477
glennrp5aa37f62011-01-02 03:07:57 +00003478 ping_color_type&=0x03;
3479 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003480 }
3481 }
3482
cristy16ea1392012-03-21 20:38:41 +00003483 if (quantum_info != (QuantumInfo *) NULL)
3484 quantum_info=DestroyQuantumInfo(quantum_info);
3485
cristy5c6f7892010-05-05 22:53:29 +00003486 if (image->storage_class == PseudoClass)
3487 {
cristyb0a657e2012-08-29 00:45:37 +00003488 PixelTrait
3489 alpha_trait;
cristy5c6f7892010-05-05 22:53:29 +00003490
cristyb0a657e2012-08-29 00:45:37 +00003491 alpha_trait=image->alpha_trait;
cristy8a46d822012-08-28 23:32:39 +00003492 image->alpha_trait=UndefinedPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003493 (void) SyncImage(image,exception);
cristyb0a657e2012-08-29 00:45:37 +00003494 image->alpha_trait=alpha_trait;
cristy5c6f7892010-05-05 22:53:29 +00003495 }
glennrp47b9dd52010-11-24 18:12:06 +00003496
glennrp4eb39312011-03-30 21:34:55 +00003497 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003498
glennrpccc36af2014-09-08 23:22:03 +00003499 if (logging != MagickFalse)
3500 {
3501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3502 " image->storage_class=%d\n",(int) image->storage_class);
3503 }
3504
cristy3ed852e2009-09-05 21:47:34 +00003505 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003506 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003507 {
3508 png_destroy_read_struct(&ping,&ping_info,&end_info);
cristy09973322013-06-30 21:06:30 +00003509 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00003510 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00003511 (void) SetImageBackgroundColor(image,exception);
glennrp868fff32014-03-16 22:09:06 +00003512#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00003513 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003514#endif
3515 if (logging != MagickFalse)
3516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3517 " exit ReadOnePNGImage() early.");
3518 return(image);
3519 }
glennrp47b9dd52010-11-24 18:12:06 +00003520
glennrpfaa852b2010-03-30 12:17:00 +00003521 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003522 {
3523 ClassType
3524 storage_class;
3525
3526 /*
3527 Image has a transparent background.
3528 */
3529 storage_class=image->storage_class;
cristy8a46d822012-08-28 23:32:39 +00003530 image->alpha_trait=BlendPixelTrait;
glennrpc11cf6a2010-03-20 16:46:19 +00003531
glennrp3c218112010-11-27 15:31:26 +00003532/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003533
glennrp0fe50b42010-11-16 03:52:51 +00003534 if (storage_class == PseudoClass)
3535 {
3536 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003537 {
glennrp0fe50b42010-11-16 03:52:51 +00003538 for (x=0; x < ping_num_trans; x++)
3539 {
cristy8a46d822012-08-28 23:32:39 +00003540 image->colormap[x].alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003541 image->colormap[x].alpha =
3542 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003543 }
glennrpc11cf6a2010-03-20 16:46:19 +00003544 }
glennrp47b9dd52010-11-24 18:12:06 +00003545
glennrp0fe50b42010-11-16 03:52:51 +00003546 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3547 {
3548 for (x=0; x < (int) image->colors; x++)
3549 {
3550 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy16ea1392012-03-21 20:38:41 +00003551 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003552 {
cristy8a46d822012-08-28 23:32:39 +00003553 image->colormap[x].alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003554 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003555 }
3556 }
3557 }
cristy16ea1392012-03-21 20:38:41 +00003558 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003559 }
glennrp47b9dd52010-11-24 18:12:06 +00003560
glennrpa6a06632011-01-19 15:15:34 +00003561#if 1 /* Should have already been done above, but glennrp problem P10
3562 * needs this.
3563 */
glennrp0fe50b42010-11-16 03:52:51 +00003564 else
3565 {
3566 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003567 {
glennrp0fe50b42010-11-16 03:52:51 +00003568 image->storage_class=storage_class;
3569 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3570
cristy16ea1392012-03-21 20:38:41 +00003571 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003572 break;
3573
glennrp0fe50b42010-11-16 03:52:51 +00003574
glennrpa6a06632011-01-19 15:15:34 +00003575 /* Caution: on a Q8 build, this does not distinguish between
3576 * 16-bit colors that differ only in the low byte
3577 */
glennrp0fe50b42010-11-16 03:52:51 +00003578 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3579 {
cristy16ea1392012-03-21 20:38:41 +00003580 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3581 transparent_color.red &&
3582 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3583 transparent_color.green &&
3584 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3585 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003586 {
cristy16ea1392012-03-21 20:38:41 +00003587 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003588 }
glennrp0fe50b42010-11-16 03:52:51 +00003589
glennrp67b9c1a2011-04-22 18:47:36 +00003590#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003591 else
glennrp4f25bd02011-01-01 18:51:28 +00003592 {
cristy16ea1392012-03-21 20:38:41 +00003593 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003594 }
glennrpa6a06632011-01-19 15:15:34 +00003595#endif
glennrp0fe50b42010-11-16 03:52:51 +00003596
cristy16ea1392012-03-21 20:38:41 +00003597 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003598 }
3599
3600 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3601 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003602 }
glennrp0fe50b42010-11-16 03:52:51 +00003603 }
glennrpa6a06632011-01-19 15:15:34 +00003604#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003605
cristy3ed852e2009-09-05 21:47:34 +00003606 image->storage_class=DirectClass;
3607 }
glennrp3c218112010-11-27 15:31:26 +00003608
cristyeb3b22a2011-03-31 20:16:11 +00003609 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003610 {
3611 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003612 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3613 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003614 else
glennrpa0ed0092011-04-18 16:36:29 +00003615 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3616 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003617
glennrp4eb39312011-03-30 21:34:55 +00003618 if (status != MagickFalse)
3619 for (i=0; i < (ssize_t) num_text; i++)
3620 {
3621 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003622
glennrp4eb39312011-03-30 21:34:55 +00003623 if (logging != MagickFalse)
3624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3625 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003626
glennrp4eb39312011-03-30 21:34:55 +00003627 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003628 {
glennrp99f96812014-06-30 02:57:09 +00003629 const char
3630 *value;
3631
3632 value=GetImageOption(image_info,"profile:skip");
3633
3634 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3635 {
3636 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3637 (int) i,exception);
3638 num_raw_profiles++;
3639 if (logging != MagickFalse)
3640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3641 " Read raw profile %s",text[i].key+17);
3642 }
3643 else
3644 {
3645 if (logging != MagickFalse)
3646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3647 " Skipping raw profile %s",text[i].key+17);
3648 }
glennrp97f90e22011-02-22 05:47:58 +00003649 }
glennrp0fe50b42010-11-16 03:52:51 +00003650
glennrp4eb39312011-03-30 21:34:55 +00003651 else
3652 {
3653 char
3654 *value;
3655
3656 length=text[i].text_length;
3657 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3658 sizeof(*value));
3659 if (value == (char *) NULL)
3660 {
glennrpedaa0382012-04-12 14:16:21 +00003661 png_error(ping,"Memory allocation failed");
glennrp4eb39312011-03-30 21:34:55 +00003662 break;
3663 }
3664 *value='\0';
3665 (void) ConcatenateMagickString(value,text[i].text,length+2);
3666
3667 /* Don't save "density" or "units" property if we have a pHYs
3668 * chunk
3669 */
3670 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3671 (LocaleCompare(text[i].key,"density") != 0 &&
3672 LocaleCompare(text[i].key,"units") != 0))
cristy16ea1392012-03-21 20:38:41 +00003673 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003674
3675 if (logging != MagickFalse)
3676 {
3677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00003678 " length: %lu\n"
3679 " Keyword: %s",
3680 (unsigned long) length,
3681 text[i].key);
glennrp4eb39312011-03-30 21:34:55 +00003682 }
3683
3684 value=DestroyString(value);
3685 }
3686 }
dirkfd6fd072014-10-24 21:10:08 +00003687 num_text_total += num_text;
3688 }
glennrp3c218112010-11-27 15:31:26 +00003689
cristy3ed852e2009-09-05 21:47:34 +00003690#ifdef MNG_OBJECT_BUFFERS
3691 /*
3692 Store the object if necessary.
3693 */
3694 if (object_id && !mng_info->frozen[object_id])
3695 {
3696 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3697 {
3698 /*
3699 create a new object buffer.
3700 */
3701 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003702 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003703
cristy3ed852e2009-09-05 21:47:34 +00003704 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3705 {
3706 mng_info->ob[object_id]->image=(Image *) NULL;
3707 mng_info->ob[object_id]->reference_count=1;
3708 }
3709 }
glennrp47b9dd52010-11-24 18:12:06 +00003710
cristy3ed852e2009-09-05 21:47:34 +00003711 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3712 mng_info->ob[object_id]->frozen)
3713 {
3714 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003715 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003716
cristy3ed852e2009-09-05 21:47:34 +00003717 if (mng_info->ob[object_id]->frozen)
glennrpedaa0382012-04-12 14:16:21 +00003718 png_error(ping,"Cannot overwrite frozen MNG object buffer");
cristy3ed852e2009-09-05 21:47:34 +00003719 }
glennrp0fe50b42010-11-16 03:52:51 +00003720
cristy3ed852e2009-09-05 21:47:34 +00003721 else
3722 {
cristy3ed852e2009-09-05 21:47:34 +00003723
3724 if (mng_info->ob[object_id]->image != (Image *) NULL)
3725 mng_info->ob[object_id]->image=DestroyImage
3726 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003727
cristy3ed852e2009-09-05 21:47:34 +00003728 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristy16ea1392012-03-21 20:38:41 +00003729 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003730
cristy3ed852e2009-09-05 21:47:34 +00003731 if (mng_info->ob[object_id]->image != (Image *) NULL)
3732 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003733
cristy3ed852e2009-09-05 21:47:34 +00003734 else
glennrpedaa0382012-04-12 14:16:21 +00003735 png_error(ping, "Cloning image for object buffer failed");
glennrp0fe50b42010-11-16 03:52:51 +00003736
glennrpfaa852b2010-03-30 12:17:00 +00003737 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003738 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003739
glennrpfaa852b2010-03-30 12:17:00 +00003740 mng_info->ob[object_id]->width=ping_width;
3741 mng_info->ob[object_id]->height=ping_height;
3742 mng_info->ob[object_id]->color_type=ping_color_type;
3743 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3744 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3745 mng_info->ob[object_id]->compression_method=
3746 ping_compression_method;
3747 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003748
glennrpfaa852b2010-03-30 12:17:00 +00003749 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003750 {
cristy3ed852e2009-09-05 21:47:34 +00003751 png_colorp
3752 plte;
3753
3754 /*
3755 Copy the PLTE to the object buffer.
3756 */
3757 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3758 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003759
cristy3ed852e2009-09-05 21:47:34 +00003760 for (i=0; i < number_colors; i++)
3761 {
3762 mng_info->ob[object_id]->plte[i]=plte[i];
3763 }
3764 }
glennrp47b9dd52010-11-24 18:12:06 +00003765
cristy3ed852e2009-09-05 21:47:34 +00003766 else
3767 mng_info->ob[object_id]->plte_length=0;
3768 }
3769 }
3770#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003771
cristy8a46d822012-08-28 23:32:39 +00003772 /* Set image->alpha_trait to MagickTrue if the input colortype supports
glennrp0a55b4c2011-03-23 12:38:38 +00003773 * alpha or if a valid tRNS chunk is present, no matter whether there
3774 * is actual transparency present.
3775 */
cristy8a46d822012-08-28 23:32:39 +00003776 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
glennrp0a55b4c2011-03-23 12:38:38 +00003777 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3778 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
cristyb0a657e2012-08-29 00:45:37 +00003779 BlendPixelTrait : UndefinedPixelTrait;
glennrp0a55b4c2011-03-23 12:38:38 +00003780
glennrp224ad8d2013-02-01 17:04:04 +00003781#if 0 /* I'm not sure what's wrong here but it does not work. */
glennrp5830fbc2013-01-27 06:11:45 +00003782 if (image->alpha_trait == BlendPixelTrait)
3783 {
3784 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3785 (void) SetImageType(image,GrayscaleMatteType,exception);
3786
3787 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3788 (void) SetImageType(image,PaletteMatteType,exception);
3789
3790 else
3791 (void) SetImageType(image,TrueColorMatteType,exception);
3792 }
3793
3794 else
3795 {
3796 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3797 (void) SetImageType(image,GrayscaleType,exception);
3798
3799 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3800 (void) SetImageType(image,PaletteType,exception);
3801
3802 else
3803 (void) SetImageType(image,TrueColorType,exception);
3804 }
glennrp224ad8d2013-02-01 17:04:04 +00003805#endif
glennrp5830fbc2013-01-27 06:11:45 +00003806
glennrpcb395ac2011-03-30 19:50:23 +00003807 /* Set more properties for identify to retrieve */
3808 {
3809 char
3810 msg[MaxTextExtent];
3811
glennrp4eb39312011-03-30 21:34:55 +00003812 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003813 {
3814 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003815 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003816 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrp3398b5b2013-05-03 23:10:31 +00003817 (void) SetImageProperty(image,"png:text",msg,
cristy16ea1392012-03-21 20:38:41 +00003818 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003819 }
3820
3821 if (num_raw_profiles != 0)
3822 {
cristy3b6fd2e2011-05-20 12:53:50 +00003823 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003824 "%d were found", num_raw_profiles);
cristy16ea1392012-03-21 20:38:41 +00003825 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3826 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003827 }
3828
glennrp98b83d42012-07-23 02:50:31 +00003829 if (ping_found_cHRM != MagickFalse)
glennrp59612252011-03-30 21:45:21 +00003830 {
cristy3b6fd2e2011-05-20 12:53:50 +00003831 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003832 "chunk was found (see Chromaticity, above)");
glennrp3398b5b2013-05-03 23:10:31 +00003833 (void) SetImageProperty(image,"png:cHRM",msg,
cristy16ea1392012-03-21 20:38:41 +00003834 exception);
glennrp59612252011-03-30 21:45:21 +00003835 }
glennrpcb395ac2011-03-30 19:50:23 +00003836
3837 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003838 {
cristy3b6fd2e2011-05-20 12:53:50 +00003839 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003840 "chunk was found (see Background color, above)");
glennrp3398b5b2013-05-03 23:10:31 +00003841 (void) SetImageProperty(image,"png:bKGD",msg,
cristy16ea1392012-03-21 20:38:41 +00003842 exception);
glennrp59612252011-03-30 21:45:21 +00003843 }
3844
cristy3b6fd2e2011-05-20 12:53:50 +00003845 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003846 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003847
glennrp98b83d42012-07-23 02:50:31 +00003848#if defined(PNG_iCCP_SUPPORTED)
3849 if (ping_found_iCCP != MagickFalse)
glennrp3398b5b2013-05-03 23:10:31 +00003850 (void) SetImageProperty(image,"png:iCCP",msg,
cristy16ea1392012-03-21 20:38:41 +00003851 exception);
glennrp98b83d42012-07-23 02:50:31 +00003852#endif
glennrpcb395ac2011-03-30 19:50:23 +00003853
glennrpcb395ac2011-03-30 19:50:23 +00003854 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrp3398b5b2013-05-03 23:10:31 +00003855 (void) SetImageProperty(image,"png:tRNS",msg,
cristy16ea1392012-03-21 20:38:41 +00003856 exception);
glennrp4eb39312011-03-30 21:34:55 +00003857
3858#if defined(PNG_sRGB_SUPPORTED)
glennrp98b83d42012-07-23 02:50:31 +00003859 if (ping_found_sRGB != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003860 {
cristy3b6fd2e2011-05-20 12:53:50 +00003861 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp98b83d42012-07-23 02:50:31 +00003862 "intent=%d (%s)",
3863 (int) intent,
3864 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
glennrp3398b5b2013-05-03 23:10:31 +00003865 (void) SetImageProperty(image,"png:sRGB",msg,
glennrp98b83d42012-07-23 02:50:31 +00003866 exception);
glennrp4eb39312011-03-30 21:34:55 +00003867 }
3868#endif
3869
glennrp98b83d42012-07-23 02:50:31 +00003870 if (ping_found_gAMA != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003871 {
cristy3b6fd2e2011-05-20 12:53:50 +00003872 (void) FormatLocaleString(msg,MaxTextExtent,
cristy16ea1392012-03-21 20:38:41 +00003873 "gamma=%.8g (See Gamma, above)",
3874 file_gamma);
glennrp3398b5b2013-05-03 23:10:31 +00003875 (void) SetImageProperty(image,"png:gAMA",msg,
cristy16ea1392012-03-21 20:38:41 +00003876 exception);
glennrp4eb39312011-03-30 21:34:55 +00003877 }
3878
3879#if defined(PNG_pHYs_SUPPORTED)
3880 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3881 {
cristy3b6fd2e2011-05-20 12:53:50 +00003882 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003883 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003884 (double) x_resolution,(double) y_resolution, unit_type);
glennrp3398b5b2013-05-03 23:10:31 +00003885 (void) SetImageProperty(image,"png:pHYs",msg,
cristy16ea1392012-03-21 20:38:41 +00003886 exception);
glennrp4eb39312011-03-30 21:34:55 +00003887 }
3888#endif
3889
3890#if defined(PNG_oFFs_SUPPORTED)
3891 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3892 {
cristy3b6fd2e2011-05-20 12:53:50 +00003893 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003894 (double) image->page.x,(double) image->page.y);
glennrp3398b5b2013-05-03 23:10:31 +00003895 (void) SetImageProperty(image,"png:oFFs",msg,
cristy16ea1392012-03-21 20:38:41 +00003896 exception);
glennrp4eb39312011-03-30 21:34:55 +00003897 }
3898#endif
3899
dirkfd6fd072014-10-24 21:10:08 +00003900#if defined(PNG_tIME_SUPPORTED)
3901 read_tIME_chunk(image,ping,end_info,exception);
3902#endif
3903
glennrp07523c72011-03-31 18:12:10 +00003904 if ((image->page.width != 0 && image->page.width != image->columns) ||
3905 (image->page.height != 0 && image->page.height != image->rows))
3906 {
cristy3b6fd2e2011-05-20 12:53:50 +00003907 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003908 "width=%.20g, height=%.20g",
3909 (double) image->page.width,(double) image->page.height);
glennrp3398b5b2013-05-03 23:10:31 +00003910 (void) SetImageProperty(image,"png:vpAg",msg,
cristy16ea1392012-03-21 20:38:41 +00003911 exception);
glennrp07523c72011-03-31 18:12:10 +00003912 }
glennrpcb395ac2011-03-30 19:50:23 +00003913 }
3914
cristy3ed852e2009-09-05 21:47:34 +00003915 /*
3916 Relinquish resources.
3917 */
3918 png_destroy_read_struct(&ping,&ping_info,&end_info);
3919
cristy09973322013-06-30 21:06:30 +00003920 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00003921
3922 if (logging != MagickFalse)
3923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3924 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003925
glennrp868fff32014-03-16 22:09:06 +00003926#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpedaa0382012-04-12 14:16:21 +00003927 UnlockSemaphoreInfo(ping_semaphore);
3928#endif
3929
3930 /* } for navigation to beginning of SETJMP-protected block, revert to
3931 * Throwing an Exception when an error occurs.
3932 */
3933
cristy3ed852e2009-09-05 21:47:34 +00003934 return(image);
3935
3936/* end of reading one PNG image */
3937}
3938
3939static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3940{
3941 Image
3942 *image,
3943 *previous;
3944
3945 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003946 have_mng_structure,
3947 logging,
cristy3ed852e2009-09-05 21:47:34 +00003948 status;
3949
3950 MngInfo
3951 *mng_info;
3952
3953 char
3954 magic_number[MaxTextExtent];
3955
cristy3ed852e2009-09-05 21:47:34 +00003956 ssize_t
3957 count;
3958
3959 /*
3960 Open image file.
3961 */
3962 assert(image_info != (const ImageInfo *) NULL);
3963 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003964
cristy3ed852e2009-09-05 21:47:34 +00003965 if (image_info->debug != MagickFalse)
3966 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3967 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003968
cristy3ed852e2009-09-05 21:47:34 +00003969 assert(exception != (ExceptionInfo *) NULL);
3970 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003971 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy16ea1392012-03-21 20:38:41 +00003972 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003973 mng_info=(MngInfo *) NULL;
3974 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003975
cristy3ed852e2009-09-05 21:47:34 +00003976 if (status == MagickFalse)
3977 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003978
cristy3ed852e2009-09-05 21:47:34 +00003979 /*
3980 Verify PNG signature.
3981 */
3982 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003983
glennrpdde35db2011-02-21 12:06:32 +00003984 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003985 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003986
cristy3ed852e2009-09-05 21:47:34 +00003987 /*
3988 Allocate a MngInfo structure.
3989 */
3990 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003991 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003992
cristy3ed852e2009-09-05 21:47:34 +00003993 if (mng_info == (MngInfo *) NULL)
3994 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003995
cristy3ed852e2009-09-05 21:47:34 +00003996 /*
3997 Initialize members of the MngInfo structure.
3998 */
3999 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4000 mng_info->image=image;
4001 have_mng_structure=MagickTrue;
4002
4003 previous=image;
4004 image=ReadOnePNGImage(mng_info,image_info,exception);
4005 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00004006
cristy3ed852e2009-09-05 21:47:34 +00004007 if (image == (Image *) NULL)
4008 {
4009 if (previous != (Image *) NULL)
4010 {
4011 if (previous->signature != MagickSignature)
4012 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004013
cristy3ed852e2009-09-05 21:47:34 +00004014 (void) CloseBlob(previous);
4015 (void) DestroyImageList(previous);
4016 }
glennrp0fe50b42010-11-16 03:52:51 +00004017
cristy3ed852e2009-09-05 21:47:34 +00004018 if (logging != MagickFalse)
4019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4020 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004021
cristy3ed852e2009-09-05 21:47:34 +00004022 return((Image *) NULL);
4023 }
glennrp47b9dd52010-11-24 18:12:06 +00004024
cristy3ed852e2009-09-05 21:47:34 +00004025 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004026
cristy3ed852e2009-09-05 21:47:34 +00004027 if ((image->columns == 0) || (image->rows == 0))
4028 {
4029 if (logging != MagickFalse)
4030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4031 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00004032
cristy3ed852e2009-09-05 21:47:34 +00004033 ThrowReaderException(CorruptImageError,"CorruptImage");
4034 }
glennrp47b9dd52010-11-24 18:12:06 +00004035
cristy72715f52012-06-26 17:55:16 +00004036 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
glennrp3d627862013-02-26 00:19:34 +00004037 ((image->gamma < .45) || (image->gamma > .46)) &&
4038 !(image->chromaticity.red_primary.x>0.6399f &&
4039 image->chromaticity.red_primary.x<0.6401f &&
4040 image->chromaticity.red_primary.y>0.3299f &&
4041 image->chromaticity.red_primary.y<0.3301f &&
4042 image->chromaticity.green_primary.x>0.2999f &&
4043 image->chromaticity.green_primary.x<0.3001f &&
4044 image->chromaticity.green_primary.y>0.5999f &&
4045 image->chromaticity.green_primary.y<0.6001f &&
4046 image->chromaticity.blue_primary.x>0.1499f &&
4047 image->chromaticity.blue_primary.x<0.1501f &&
4048 image->chromaticity.blue_primary.y>0.0599f &&
4049 image->chromaticity.blue_primary.y<0.0601f &&
4050 image->chromaticity.white_point.x>0.3126f &&
4051 image->chromaticity.white_point.x<0.3128f &&
4052 image->chromaticity.white_point.y>0.3289f &&
4053 image->chromaticity.white_point.y<0.3291f))
glennrpccc36af2014-09-08 23:22:03 +00004054 {
4055 SetImageColorspace(image,RGBColorspace,exception);
4056 }
cristy72715f52012-06-26 17:55:16 +00004057
cristy3ed852e2009-09-05 21:47:34 +00004058 if (logging != MagickFalse)
glennrpccc36af2014-09-08 23:22:03 +00004059 {
4060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4061 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4062 (double) image->page.width,(double) image->page.height,
4063 (double) image->page.x,(double) image->page.y);
4064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4065 " image->colorspace: %d", (int) image->colorspace);
4066 }
glennrp97f90e22011-02-22 05:47:58 +00004067
4068 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00004069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004070
cristy3ed852e2009-09-05 21:47:34 +00004071 return(image);
4072}
4073
4074
4075
4076#if defined(JNG_SUPPORTED)
4077/*
4078%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4079% %
4080% %
4081% %
4082% R e a d O n e J N G I m a g e %
4083% %
4084% %
4085% %
4086%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4087%
4088% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4089% (minus the 8-byte signature) and returns it. It allocates the memory
4090% necessary for the new Image structure and returns a pointer to the new
4091% image.
4092%
4093% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4094%
4095% The format of the ReadOneJNGImage method is:
4096%
4097% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4098% ExceptionInfo *exception)
4099%
4100% A description of each parameter follows:
4101%
4102% o mng_info: Specifies a pointer to a MngInfo structure.
4103%
4104% o image_info: the image info.
4105%
4106% o exception: return any errors or warnings in this structure.
4107%
4108*/
4109static Image *ReadOneJNGImage(MngInfo *mng_info,
4110 const ImageInfo *image_info, ExceptionInfo *exception)
4111{
4112 Image
4113 *alpha_image,
4114 *color_image,
4115 *image,
4116 *jng_image;
4117
4118 ImageInfo
4119 *alpha_image_info,
4120 *color_image_info;
4121
cristy4383ec82011-01-05 15:42:32 +00004122 MagickBooleanType
4123 logging;
4124
cristybb503372010-05-27 20:51:26 +00004125 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004126 y;
4127
4128 MagickBooleanType
4129 status;
4130
4131 png_uint_32
4132 jng_height,
4133 jng_width;
4134
4135 png_byte
4136 jng_color_type,
4137 jng_image_sample_depth,
4138 jng_image_compression_method,
4139 jng_image_interlace_method,
4140 jng_alpha_sample_depth,
4141 jng_alpha_compression_method,
4142 jng_alpha_filter_method,
4143 jng_alpha_interlace_method;
4144
cristy16ea1392012-03-21 20:38:41 +00004145 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00004146 *s;
4147
cristybb503372010-05-27 20:51:26 +00004148 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004149 i,
4150 x;
4151
cristy16ea1392012-03-21 20:38:41 +00004152 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00004153 *q;
4154
4155 register unsigned char
4156 *p;
4157
4158 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00004159 read_JSEP,
glennrpe0421fe2014-06-08 02:04:34 +00004160 reading_idat;
cristy3ed852e2009-09-05 21:47:34 +00004161
cristybb503372010-05-27 20:51:26 +00004162 size_t
cristy3ed852e2009-09-05 21:47:34 +00004163 length;
4164
4165 jng_alpha_compression_method=0;
4166 jng_alpha_sample_depth=8;
4167 jng_color_type=0;
4168 jng_height=0;
4169 jng_width=0;
4170 alpha_image=(Image *) NULL;
4171 color_image=(Image *) NULL;
4172 alpha_image_info=(ImageInfo *) NULL;
4173 color_image_info=(ImageInfo *) NULL;
4174
4175 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00004176 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004177
4178 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00004179
cristy16ea1392012-03-21 20:38:41 +00004180 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004181 {
4182 /*
4183 Allocate next image structure.
4184 */
4185 if (logging != MagickFalse)
4186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4187 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004188
cristy16ea1392012-03-21 20:38:41 +00004189 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004190
cristy3ed852e2009-09-05 21:47:34 +00004191 if (GetNextImageInList(image) == (Image *) NULL)
4192 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004193
cristy3ed852e2009-09-05 21:47:34 +00004194 image=SyncNextImageInList(image);
4195 }
4196 mng_info->image=image;
4197
4198 /*
4199 Signature bytes have already been read.
4200 */
4201
4202 read_JSEP=MagickFalse;
4203 reading_idat=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00004204 for (;;)
4205 {
4206 char
4207 type[MaxTextExtent];
4208
4209 unsigned char
4210 *chunk;
4211
4212 unsigned int
4213 count;
4214
4215 /*
4216 Read a new JNG chunk.
4217 */
4218 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4219 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004220
cristy3ed852e2009-09-05 21:47:34 +00004221 if (status == MagickFalse)
4222 break;
glennrp0fe50b42010-11-16 03:52:51 +00004223
cristy3ed852e2009-09-05 21:47:34 +00004224 type[0]='\0';
4225 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4226 length=ReadBlobMSBLong(image);
4227 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4228
4229 if (logging != MagickFalse)
4230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004231 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4232 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004233
4234 if (length > PNG_UINT_31_MAX || count == 0)
4235 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004236
cristy3ed852e2009-09-05 21:47:34 +00004237 p=NULL;
4238 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00004239
cristy3ed852e2009-09-05 21:47:34 +00004240 if (length)
4241 {
4242 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00004243
cristy3ed852e2009-09-05 21:47:34 +00004244 if (chunk == (unsigned char *) NULL)
4245 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004246
cristybb503372010-05-27 20:51:26 +00004247 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004248 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00004249
cristy3ed852e2009-09-05 21:47:34 +00004250 p=chunk;
4251 }
glennrp47b9dd52010-11-24 18:12:06 +00004252
cristy3ed852e2009-09-05 21:47:34 +00004253 (void) ReadBlobMSBLong(image); /* read crc word */
4254
cristy3ed852e2009-09-05 21:47:34 +00004255 if (memcmp(type,mng_JHDR,4) == 0)
4256 {
4257 if (length == 16)
4258 {
cristybb503372010-05-27 20:51:26 +00004259 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristye2440c22014-05-18 12:54:47 +00004260 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00004261 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristye2440c22014-05-18 12:54:47 +00004262 (p[6] << 8) | p[7]);
4263 if ((jng_width == 0) || (jng_height == 0))
4264 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
cristy3ed852e2009-09-05 21:47:34 +00004265 jng_color_type=p[8];
4266 jng_image_sample_depth=p[9];
4267 jng_image_compression_method=p[10];
4268 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00004269
cristy3ed852e2009-09-05 21:47:34 +00004270 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4271 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00004272
cristy3ed852e2009-09-05 21:47:34 +00004273 jng_alpha_sample_depth=p[12];
4274 jng_alpha_compression_method=p[13];
4275 jng_alpha_filter_method=p[14];
4276 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00004277
cristy3ed852e2009-09-05 21:47:34 +00004278 if (logging != MagickFalse)
4279 {
4280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00004281 " jng_width: %16lu, jng_height: %16lu\n"
4282 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
cristy3ed852e2009-09-05 21:47:34 +00004283 " jng_image_compression_method:%3d",
glennrp2dd19062014-07-28 00:22:24 +00004284 (unsigned long) jng_width, (unsigned long) jng_height,
4285 jng_color_type, jng_image_sample_depth,
cristy3ed852e2009-09-05 21:47:34 +00004286 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004287
cristy3ed852e2009-09-05 21:47:34 +00004288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00004289 " jng_image_interlace_method: %3d"
cristy3ed852e2009-09-05 21:47:34 +00004290 " jng_alpha_sample_depth: %3d",
glennrp2dd19062014-07-28 00:22:24 +00004291 jng_image_interlace_method,
cristy3ed852e2009-09-05 21:47:34 +00004292 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004293
cristy3ed852e2009-09-05 21:47:34 +00004294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +00004295 " jng_alpha_compression_method:%3d\n"
4296 " jng_alpha_filter_method: %3d\n"
cristy3ed852e2009-09-05 21:47:34 +00004297 " jng_alpha_interlace_method: %3d",
glennrp2dd19062014-07-28 00:22:24 +00004298 jng_alpha_compression_method,
4299 jng_alpha_filter_method,
cristy3ed852e2009-09-05 21:47:34 +00004300 jng_alpha_interlace_method);
4301 }
4302 }
glennrp47b9dd52010-11-24 18:12:06 +00004303
cristy3ed852e2009-09-05 21:47:34 +00004304 if (length)
4305 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004306
cristy3ed852e2009-09-05 21:47:34 +00004307 continue;
4308 }
4309
4310
4311 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4312 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4313 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4314 {
4315 /*
4316 o create color_image
4317 o open color_blob, attached to color_image
4318 o if (color type has alpha)
4319 open alpha_blob, attached to alpha_image
4320 */
4321
cristy73bd4a52010-10-05 11:24:23 +00004322 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004323
cristy3ed852e2009-09-05 21:47:34 +00004324 if (color_image_info == (ImageInfo *) NULL)
4325 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004326
cristy3ed852e2009-09-05 21:47:34 +00004327 GetImageInfo(color_image_info);
cristy16ea1392012-03-21 20:38:41 +00004328 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004329
cristy3ed852e2009-09-05 21:47:34 +00004330 if (color_image == (Image *) NULL)
4331 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4332
4333 if (logging != MagickFalse)
4334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4335 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004336
cristy3ed852e2009-09-05 21:47:34 +00004337 (void) AcquireUniqueFilename(color_image->filename);
4338 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4339 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004340
cristy3ed852e2009-09-05 21:47:34 +00004341 if (status == MagickFalse)
4342 return((Image *) NULL);
4343
4344 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4345 {
4346 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004347 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004348
cristy3ed852e2009-09-05 21:47:34 +00004349 if (alpha_image_info == (ImageInfo *) NULL)
4350 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004351
cristy3ed852e2009-09-05 21:47:34 +00004352 GetImageInfo(alpha_image_info);
cristy16ea1392012-03-21 20:38:41 +00004353 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004354
cristy3ed852e2009-09-05 21:47:34 +00004355 if (alpha_image == (Image *) NULL)
4356 {
4357 alpha_image=DestroyImage(alpha_image);
4358 ThrowReaderException(ResourceLimitError,
4359 "MemoryAllocationFailed");
4360 }
glennrp0fe50b42010-11-16 03:52:51 +00004361
cristy3ed852e2009-09-05 21:47:34 +00004362 if (logging != MagickFalse)
4363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4364 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004365
cristy3ed852e2009-09-05 21:47:34 +00004366 (void) AcquireUniqueFilename(alpha_image->filename);
4367 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4368 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004369
cristy3ed852e2009-09-05 21:47:34 +00004370 if (status == MagickFalse)
4371 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004372
cristy3ed852e2009-09-05 21:47:34 +00004373 if (jng_alpha_compression_method == 0)
4374 {
4375 unsigned char
4376 data[18];
4377
4378 if (logging != MagickFalse)
4379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4380 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004381
cristy3ed852e2009-09-05 21:47:34 +00004382 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4383 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004384
cristy3ed852e2009-09-05 21:47:34 +00004385 (void) WriteBlobMSBULong(alpha_image,13L);
4386 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004387 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004388 PNGLong(data+4,jng_width);
4389 PNGLong(data+8,jng_height);
4390 data[12]=jng_alpha_sample_depth;
4391 data[13]=0; /* color_type gray */
4392 data[14]=0; /* compression method 0 */
4393 data[15]=0; /* filter_method 0 */
4394 data[16]=0; /* interlace_method 0 */
4395 (void) WriteBlob(alpha_image,17,data);
4396 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4397 }
4398 }
4399 reading_idat=MagickTrue;
4400 }
4401
4402 if (memcmp(type,mng_JDAT,4) == 0)
4403 {
glennrp47b9dd52010-11-24 18:12:06 +00004404 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004405
4406 if (logging != MagickFalse)
4407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4408 " Copying JDAT chunk data to color_blob.");
4409
4410 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004411
cristy3ed852e2009-09-05 21:47:34 +00004412 if (length)
4413 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004414
cristy3ed852e2009-09-05 21:47:34 +00004415 continue;
4416 }
4417
4418 if (memcmp(type,mng_IDAT,4) == 0)
4419 {
4420 png_byte
4421 data[5];
4422
glennrp47b9dd52010-11-24 18:12:06 +00004423 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004424
4425 if (image_info->ping == MagickFalse)
4426 {
4427 if (logging != MagickFalse)
4428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4429 " Copying IDAT chunk data to alpha_blob.");
4430
cristybb503372010-05-27 20:51:26 +00004431 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004432 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004433 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004434 (void) WriteBlob(alpha_image,4,data);
4435 (void) WriteBlob(alpha_image,length,chunk);
4436 (void) WriteBlobMSBULong(alpha_image,
4437 crc32(crc32(0,data,4),chunk,(uInt) length));
4438 }
glennrp0fe50b42010-11-16 03:52:51 +00004439
cristy3ed852e2009-09-05 21:47:34 +00004440 if (length)
4441 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004442
cristy3ed852e2009-09-05 21:47:34 +00004443 continue;
4444 }
4445
4446 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4447 {
glennrp47b9dd52010-11-24 18:12:06 +00004448 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004449
4450 if (image_info->ping == MagickFalse)
4451 {
4452 if (logging != MagickFalse)
4453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4454 " Copying JDAA chunk data to alpha_blob.");
4455
4456 (void) WriteBlob(alpha_image,length,chunk);
4457 }
glennrp0fe50b42010-11-16 03:52:51 +00004458
cristy3ed852e2009-09-05 21:47:34 +00004459 if (length)
4460 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004461
cristy3ed852e2009-09-05 21:47:34 +00004462 continue;
4463 }
4464
4465 if (memcmp(type,mng_JSEP,4) == 0)
4466 {
4467 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004468
cristy3ed852e2009-09-05 21:47:34 +00004469 if (length)
4470 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004471
cristy3ed852e2009-09-05 21:47:34 +00004472 continue;
4473 }
4474
4475 if (memcmp(type,mng_bKGD,4) == 0)
4476 {
4477 if (length == 2)
4478 {
4479 image->background_color.red=ScaleCharToQuantum(p[1]);
4480 image->background_color.green=image->background_color.red;
4481 image->background_color.blue=image->background_color.red;
4482 }
glennrp0fe50b42010-11-16 03:52:51 +00004483
cristy3ed852e2009-09-05 21:47:34 +00004484 if (length == 6)
4485 {
4486 image->background_color.red=ScaleCharToQuantum(p[1]);
4487 image->background_color.green=ScaleCharToQuantum(p[3]);
4488 image->background_color.blue=ScaleCharToQuantum(p[5]);
4489 }
glennrp0fe50b42010-11-16 03:52:51 +00004490
cristy3ed852e2009-09-05 21:47:34 +00004491 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4492 continue;
4493 }
4494
4495 if (memcmp(type,mng_gAMA,4) == 0)
4496 {
4497 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004498 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004499
cristy3ed852e2009-09-05 21:47:34 +00004500 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4501 continue;
4502 }
4503
4504 if (memcmp(type,mng_cHRM,4) == 0)
4505 {
4506 if (length == 32)
4507 {
cristy8182b072010-05-30 20:10:53 +00004508 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4509 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4510 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4511 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4512 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4513 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4514 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4515 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004516 }
glennrp47b9dd52010-11-24 18:12:06 +00004517
cristy3ed852e2009-09-05 21:47:34 +00004518 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4519 continue;
4520 }
4521
4522 if (memcmp(type,mng_sRGB,4) == 0)
4523 {
4524 if (length == 1)
4525 {
glennrpe610a072010-08-05 17:08:46 +00004526 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004527 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristyda7803d2012-05-03 01:22:45 +00004528 image->gamma=1.000f/2.200f;
cristy3ed852e2009-09-05 21:47:34 +00004529 image->chromaticity.red_primary.x=0.6400f;
4530 image->chromaticity.red_primary.y=0.3300f;
4531 image->chromaticity.green_primary.x=0.3000f;
4532 image->chromaticity.green_primary.y=0.6000f;
4533 image->chromaticity.blue_primary.x=0.1500f;
4534 image->chromaticity.blue_primary.y=0.0600f;
4535 image->chromaticity.white_point.x=0.3127f;
4536 image->chromaticity.white_point.y=0.3290f;
4537 }
glennrp47b9dd52010-11-24 18:12:06 +00004538
cristy3ed852e2009-09-05 21:47:34 +00004539 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4540 continue;
4541 }
4542
4543 if (memcmp(type,mng_oFFs,4) == 0)
4544 {
4545 if (length > 8)
4546 {
glennrp5eae7602011-02-22 15:21:32 +00004547 image->page.x=(ssize_t) mng_get_long(p);
4548 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004549
cristy3ed852e2009-09-05 21:47:34 +00004550 if ((int) p[8] != 0)
4551 {
4552 image->page.x/=10000;
4553 image->page.y/=10000;
4554 }
4555 }
glennrp47b9dd52010-11-24 18:12:06 +00004556
cristy3ed852e2009-09-05 21:47:34 +00004557 if (length)
4558 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004559
cristy3ed852e2009-09-05 21:47:34 +00004560 continue;
4561 }
4562
4563 if (memcmp(type,mng_pHYs,4) == 0)
4564 {
4565 if (length > 8)
4566 {
cristy16ea1392012-03-21 20:38:41 +00004567 image->resolution.x=(double) mng_get_long(p);
4568 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004569 if ((int) p[8] == PNG_RESOLUTION_METER)
4570 {
4571 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00004572 image->resolution.x=image->resolution.x/100.0f;
4573 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004574 }
4575 }
glennrp0fe50b42010-11-16 03:52:51 +00004576
cristy3ed852e2009-09-05 21:47:34 +00004577 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4578 continue;
4579 }
4580
4581#if 0
4582 if (memcmp(type,mng_iCCP,4) == 0)
4583 {
glennrpfd05d622011-02-25 04:10:33 +00004584 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004585 if (length)
4586 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004587
cristy3ed852e2009-09-05 21:47:34 +00004588 continue;
4589 }
4590#endif
4591
4592 if (length)
4593 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4594
4595 if (memcmp(type,mng_IEND,4))
4596 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004597
cristy3ed852e2009-09-05 21:47:34 +00004598 break;
4599 }
4600
4601
4602 /* IEND found */
4603
4604 /*
4605 Finish up reading image data:
4606
4607 o read main image from color_blob.
4608
4609 o close color_blob.
4610
4611 o if (color_type has alpha)
4612 if alpha_encoding is PNG
4613 read secondary image from alpha_blob via ReadPNG
4614 if alpha_encoding is JPEG
4615 read secondary image from alpha_blob via ReadJPEG
4616
4617 o close alpha_blob.
4618
4619 o copy intensity of secondary image into
cristy16ea1392012-03-21 20:38:41 +00004620 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004621
4622 o destroy the secondary image.
4623 */
4624
4625 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004626
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);
cristy3b6fd2e2011-05-20 12:53:50 +00004632 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%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 if (jng_image == (Image *) NULL)
4639 return((Image *) NULL);
4640
4641 (void) RelinquishUniqueFileResource(color_image->filename);
4642 color_image=DestroyImage(color_image);
4643 color_image_info=DestroyImageInfo(color_image_info);
4644
4645 if (jng_image == (Image *) NULL)
4646 return((Image *) NULL);
4647
4648 if (logging != MagickFalse)
4649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4650 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004651
cristy3ed852e2009-09-05 21:47:34 +00004652 image->rows=jng_height;
4653 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004654
cristybb503372010-05-27 20:51:26 +00004655 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004656 {
cristy16ea1392012-03-21 20:38:41 +00004657 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004658 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00004659 for (x=(ssize_t) image->columns; x != 0; x--)
4660 {
4661 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4662 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4663 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4664 q+=GetPixelChannels(image);
4665 s+=GetPixelChannels(jng_image);
4666 }
glennrp47b9dd52010-11-24 18:12:06 +00004667
cristy3ed852e2009-09-05 21:47:34 +00004668 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4669 break;
4670 }
glennrp0fe50b42010-11-16 03:52:51 +00004671
cristy3ed852e2009-09-05 21:47:34 +00004672 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004673
cristy3ed852e2009-09-05 21:47:34 +00004674 if (image_info->ping == MagickFalse)
4675 {
4676 if (jng_color_type >= 12)
4677 {
4678 if (jng_alpha_compression_method == 0)
4679 {
4680 png_byte
4681 data[5];
4682 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4683 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004684 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004685 (void) WriteBlob(alpha_image,4,data);
4686 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4687 }
glennrp0fe50b42010-11-16 03:52:51 +00004688
cristy3ed852e2009-09-05 21:47:34 +00004689 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004690
cristy3ed852e2009-09-05 21:47:34 +00004691 if (logging != MagickFalse)
4692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00004693 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004694
cristy3b6fd2e2011-05-20 12:53:50 +00004695 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004696 "%s",alpha_image->filename);
4697
4698 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004699
cristy3ed852e2009-09-05 21:47:34 +00004700 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004701 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004702 {
4703 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy16ea1392012-03-21 20:38:41 +00004704 exception);
cristy3ed852e2009-09-05 21:47:34 +00004705 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004706
cristy8a46d822012-08-28 23:32:39 +00004707 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00004708 for (x=(ssize_t) image->columns; x != 0; x--)
4709 {
4710 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4711 q+=GetPixelChannels(image);
4712 s+=GetPixelChannels(jng_image);
4713 }
glennrp0fe50b42010-11-16 03:52:51 +00004714
cristy3ed852e2009-09-05 21:47:34 +00004715 else
cristy16ea1392012-03-21 20:38:41 +00004716 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004717 {
cristy16ea1392012-03-21 20:38:41 +00004718 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4719 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy8a46d822012-08-28 23:32:39 +00004720 image->alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00004721 q+=GetPixelChannels(image);
4722 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004723 }
glennrp0fe50b42010-11-16 03:52:51 +00004724
cristy3ed852e2009-09-05 21:47:34 +00004725 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4726 break;
4727 }
4728 (void) RelinquishUniqueFileResource(alpha_image->filename);
4729 alpha_image=DestroyImage(alpha_image);
4730 alpha_image_info=DestroyImageInfo(alpha_image_info);
4731 if (jng_image != (Image *) NULL)
4732 jng_image=DestroyImage(jng_image);
4733 }
4734 }
4735
glennrp47b9dd52010-11-24 18:12:06 +00004736 /* Read the JNG image. */
4737
cristy3ed852e2009-09-05 21:47:34 +00004738 if (mng_info->mng_type == 0)
4739 {
4740 mng_info->mng_width=jng_width;
4741 mng_info->mng_height=jng_height;
4742 }
glennrp0fe50b42010-11-16 03:52:51 +00004743
cristy3ed852e2009-09-05 21:47:34 +00004744 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004745 {
4746 image->page.width=jng_width;
4747 image->page.height=jng_height;
4748 }
4749
cristy3ed852e2009-09-05 21:47:34 +00004750 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004751 {
4752 image->page.x=mng_info->x_off[mng_info->object_id];
4753 image->page.y=mng_info->y_off[mng_info->object_id];
4754 }
4755
cristy3ed852e2009-09-05 21:47:34 +00004756 else
glennrp0fe50b42010-11-16 03:52:51 +00004757 {
4758 image->page.y=mng_info->y_off[mng_info->object_id];
4759 }
4760
cristy3ed852e2009-09-05 21:47:34 +00004761 mng_info->image_found++;
4762 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4763 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004764
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
4806 *image,
4807 *previous;
4808
4809 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004810 have_mng_structure,
4811 logging,
cristy3ed852e2009-09-05 21:47:34 +00004812 status;
4813
4814 MngInfo
4815 *mng_info;
4816
4817 char
4818 magic_number[MaxTextExtent];
4819
cristy3ed852e2009-09-05 21:47:34 +00004820 size_t
4821 count;
4822
4823 /*
4824 Open image file.
4825 */
4826 assert(image_info != (const ImageInfo *) NULL);
4827 assert(image_info->signature == MagickSignature);
4828 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4829 assert(exception != (ExceptionInfo *) NULL);
4830 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004831 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004832 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004833 mng_info=(MngInfo *) NULL;
4834 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004835
cristy3ed852e2009-09-05 21:47:34 +00004836 if (status == MagickFalse)
4837 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004838
cristy3ed852e2009-09-05 21:47:34 +00004839 if (LocaleCompare(image_info->magick,"JNG") != 0)
4840 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004841
glennrp47b9dd52010-11-24 18:12:06 +00004842 /* Verify JNG signature. */
4843
cristy3ed852e2009-09-05 21:47:34 +00004844 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004845
glennrp3b8763e2011-02-21 12:08:18 +00004846 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004847 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004848
glennrp47b9dd52010-11-24 18:12:06 +00004849 /* Allocate a MngInfo structure. */
4850
cristy3ed852e2009-09-05 21:47:34 +00004851 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004852 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004853
cristy3ed852e2009-09-05 21:47:34 +00004854 if (mng_info == (MngInfo *) NULL)
4855 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004856
glennrp47b9dd52010-11-24 18:12:06 +00004857 /* Initialize members of the MngInfo structure. */
4858
cristy3ed852e2009-09-05 21:47:34 +00004859 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4860 have_mng_structure=MagickTrue;
4861
4862 mng_info->image=image;
4863 previous=image;
4864 image=ReadOneJNGImage(mng_info,image_info,exception);
4865 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004866
cristy3ed852e2009-09-05 21:47:34 +00004867 if (image == (Image *) NULL)
4868 {
4869 if (IsImageObject(previous) != MagickFalse)
4870 {
4871 (void) CloseBlob(previous);
4872 (void) DestroyImageList(previous);
4873 }
glennrp0fe50b42010-11-16 03:52:51 +00004874
cristy3ed852e2009-09-05 21:47:34 +00004875 if (logging != MagickFalse)
4876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4877 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004878
cristy3ed852e2009-09-05 21:47:34 +00004879 return((Image *) NULL);
4880 }
4881 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004882
cristy3ed852e2009-09-05 21:47:34 +00004883 if (image->columns == 0 || image->rows == 0)
4884 {
4885 if (logging != MagickFalse)
4886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4887 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004888
cristy3ed852e2009-09-05 21:47:34 +00004889 ThrowReaderException(CorruptImageError,"CorruptImage");
4890 }
glennrp0fe50b42010-11-16 03:52:51 +00004891
cristy3ed852e2009-09-05 21:47:34 +00004892 if (logging != MagickFalse)
4893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004894
cristy3ed852e2009-09-05 21:47:34 +00004895 return(image);
4896}
4897#endif
4898
4899static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4900{
4901 char
4902 page_geometry[MaxTextExtent];
4903
4904 Image
4905 *image,
4906 *previous;
4907
cristy4383ec82011-01-05 15:42:32 +00004908 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004909 logging,
4910 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004911
cristy3ed852e2009-09-05 21:47:34 +00004912 volatile int
4913 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004914 object_id,
4915 term_chunk_found,
4916 skip_to_iend;
4917
cristybb503372010-05-27 20:51:26 +00004918 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004919 image_count=0;
4920
4921 MagickBooleanType
4922 status;
4923
4924 MagickOffsetType
4925 offset;
4926
4927 MngInfo
4928 *mng_info;
4929
4930 MngBox
4931 default_fb,
4932 fb,
4933 previous_fb;
4934
4935#if defined(MNG_INSERT_LAYERS)
cristy16ea1392012-03-21 20:38:41 +00004936 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004937 mng_background_color;
4938#endif
4939
4940 register unsigned char
4941 *p;
4942
cristybb503372010-05-27 20:51:26 +00004943 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004944 i;
4945
4946 size_t
4947 count;
4948
cristybb503372010-05-27 20:51:26 +00004949 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004950 loop_level;
4951
4952 volatile short
4953 skipping_loop;
4954
4955#if defined(MNG_INSERT_LAYERS)
4956 unsigned int
4957 mandatory_back=0;
4958#endif
4959
4960 volatile unsigned int
4961#ifdef MNG_OBJECT_BUFFERS
4962 mng_background_object=0,
4963#endif
4964 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4965
cristybb503372010-05-27 20:51:26 +00004966 size_t
cristy3ed852e2009-09-05 21:47:34 +00004967 default_frame_timeout,
4968 frame_timeout,
4969#if defined(MNG_INSERT_LAYERS)
4970 image_height,
4971 image_width,
4972#endif
4973 length;
4974
glennrp38ea0832010-06-02 18:50:28 +00004975 /* These delays are all measured in image ticks_per_second,
4976 * not in MNG ticks_per_second
4977 */
cristybb503372010-05-27 20:51:26 +00004978 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004979 default_frame_delay,
4980 final_delay,
4981 final_image_delay,
4982 frame_delay,
4983#if defined(MNG_INSERT_LAYERS)
4984 insert_layers,
4985#endif
4986 mng_iterations=1,
4987 simplicity=0,
4988 subframe_height=0,
4989 subframe_width=0;
4990
4991 previous_fb.top=0;
4992 previous_fb.bottom=0;
4993 previous_fb.left=0;
4994 previous_fb.right=0;
4995 default_fb.top=0;
4996 default_fb.bottom=0;
4997 default_fb.left=0;
4998 default_fb.right=0;
4999
glennrp47b9dd52010-11-24 18:12:06 +00005000 /* Open image file. */
5001
cristy3ed852e2009-09-05 21:47:34 +00005002 assert(image_info != (const ImageInfo *) NULL);
5003 assert(image_info->signature == MagickSignature);
5004 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
5005 assert(exception != (ExceptionInfo *) NULL);
5006 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00005007 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy16ea1392012-03-21 20:38:41 +00005008 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00005009 mng_info=(MngInfo *) NULL;
5010 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005011
cristy3ed852e2009-09-05 21:47:34 +00005012 if (status == MagickFalse)
5013 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00005014
cristy3ed852e2009-09-05 21:47:34 +00005015 first_mng_object=MagickFalse;
5016 skipping_loop=(-1);
5017 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005018
5019 /* Allocate a MngInfo structure. */
5020
cristy73bd4a52010-10-05 11:24:23 +00005021 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00005022
cristy3ed852e2009-09-05 21:47:34 +00005023 if (mng_info == (MngInfo *) NULL)
5024 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00005025
glennrp47b9dd52010-11-24 18:12:06 +00005026 /* Initialize members of the MngInfo structure. */
5027
cristy3ed852e2009-09-05 21:47:34 +00005028 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5029 mng_info->image=image;
5030 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00005031
5032 if (LocaleCompare(image_info->magick,"MNG") == 0)
5033 {
5034 char
5035 magic_number[MaxTextExtent];
5036
glennrp47b9dd52010-11-24 18:12:06 +00005037 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00005038 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5039 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5040 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005041
5042 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00005043 for (i=0; i < MNG_MAX_OBJECTS; i++)
5044 {
cristybb503372010-05-27 20:51:26 +00005045 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5046 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00005047 }
5048 mng_info->exists[0]=MagickTrue;
5049 }
glennrp47b9dd52010-11-24 18:12:06 +00005050
cristy3ed852e2009-09-05 21:47:34 +00005051 first_mng_object=MagickTrue;
5052 mng_type=0;
5053#if defined(MNG_INSERT_LAYERS)
5054 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5055#endif
5056 default_frame_delay=0;
5057 default_frame_timeout=0;
5058 frame_delay=0;
5059 final_delay=1;
5060 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5061 object_id=0;
5062 skip_to_iend=MagickFalse;
5063 term_chunk_found=MagickFalse;
5064 mng_info->framing_mode=1;
5065#if defined(MNG_INSERT_LAYERS)
5066 mandatory_back=MagickFalse;
5067#endif
5068#if defined(MNG_INSERT_LAYERS)
5069 mng_background_color=image->background_color;
5070#endif
5071 default_fb=mng_info->frame;
5072 previous_fb=mng_info->frame;
5073 do
5074 {
5075 char
5076 type[MaxTextExtent];
5077
5078 if (LocaleCompare(image_info->magick,"MNG") == 0)
5079 {
5080 unsigned char
5081 *chunk;
5082
5083 /*
5084 Read a new chunk.
5085 */
5086 type[0]='\0';
5087 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5088 length=ReadBlobMSBLong(image);
5089 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5090
5091 if (logging != MagickFalse)
5092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005093 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5094 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00005095
5096 if (length > PNG_UINT_31_MAX)
5097 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005098
cristy3ed852e2009-09-05 21:47:34 +00005099 if (count == 0)
5100 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00005101
cristy3ed852e2009-09-05 21:47:34 +00005102 p=NULL;
5103 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00005104
cristy3ed852e2009-09-05 21:47:34 +00005105 if (length)
5106 {
5107 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00005108
cristy3ed852e2009-09-05 21:47:34 +00005109 if (chunk == (unsigned char *) NULL)
5110 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00005111
cristybb503372010-05-27 20:51:26 +00005112 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005113 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00005114
cristy3ed852e2009-09-05 21:47:34 +00005115 p=chunk;
5116 }
glennrp0fe50b42010-11-16 03:52:51 +00005117
cristy3ed852e2009-09-05 21:47:34 +00005118 (void) ReadBlobMSBLong(image); /* read crc word */
5119
5120#if !defined(JNG_SUPPORTED)
5121 if (memcmp(type,mng_JHDR,4) == 0)
5122 {
5123 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005124
cristy3ed852e2009-09-05 21:47:34 +00005125 if (mng_info->jhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005126 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005127 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005128
cristy3ed852e2009-09-05 21:47:34 +00005129 mng_info->jhdr_warning++;
5130 }
5131#endif
5132 if (memcmp(type,mng_DHDR,4) == 0)
5133 {
5134 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005135
cristy3ed852e2009-09-05 21:47:34 +00005136 if (mng_info->dhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005137 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005138 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005139
cristy3ed852e2009-09-05 21:47:34 +00005140 mng_info->dhdr_warning++;
5141 }
5142 if (memcmp(type,mng_MEND,4) == 0)
5143 break;
glennrp47b9dd52010-11-24 18:12:06 +00005144
cristy3ed852e2009-09-05 21:47:34 +00005145 if (skip_to_iend)
5146 {
5147 if (memcmp(type,mng_IEND,4) == 0)
5148 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005149
cristy3ed852e2009-09-05 21:47:34 +00005150 if (length)
5151 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005152
cristy3ed852e2009-09-05 21:47:34 +00005153 if (logging != MagickFalse)
5154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5155 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00005156
cristy3ed852e2009-09-05 21:47:34 +00005157 continue;
5158 }
glennrp0fe50b42010-11-16 03:52:51 +00005159
cristy3ed852e2009-09-05 21:47:34 +00005160 if (memcmp(type,mng_MHDR,4) == 0)
5161 {
cristybb503372010-05-27 20:51:26 +00005162 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005163 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00005164
cristybb503372010-05-27 20:51:26 +00005165 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005166 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00005167
cristy3ed852e2009-09-05 21:47:34 +00005168 if (logging != MagickFalse)
5169 {
5170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005171 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00005172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005173 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005174 }
glennrp0fe50b42010-11-16 03:52:51 +00005175
cristy3ed852e2009-09-05 21:47:34 +00005176 p+=8;
cristy8182b072010-05-30 20:10:53 +00005177 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005178
cristy3ed852e2009-09-05 21:47:34 +00005179 if (mng_info->ticks_per_second == 0)
5180 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005181
cristy3ed852e2009-09-05 21:47:34 +00005182 else
5183 default_frame_delay=1UL*image->ticks_per_second/
5184 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005185
cristy3ed852e2009-09-05 21:47:34 +00005186 frame_delay=default_frame_delay;
5187 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00005188
cristy3ed852e2009-09-05 21:47:34 +00005189 if (length > 16)
5190 {
5191 p+=16;
cristy8182b072010-05-30 20:10:53 +00005192 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005193 }
glennrp0fe50b42010-11-16 03:52:51 +00005194
cristy3ed852e2009-09-05 21:47:34 +00005195 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00005196
cristy3ed852e2009-09-05 21:47:34 +00005197 if ((simplicity != 0) && ((simplicity | 11) == 11))
5198 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00005199
cristy3ed852e2009-09-05 21:47:34 +00005200 if ((simplicity != 0) && ((simplicity | 9) == 9))
5201 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00005202
cristy3ed852e2009-09-05 21:47:34 +00005203#if defined(MNG_INSERT_LAYERS)
5204 if (mng_type != 3)
5205 insert_layers=MagickTrue;
5206#endif
cristy16ea1392012-03-21 20:38:41 +00005207 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005208 {
glennrp47b9dd52010-11-24 18:12:06 +00005209 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005210 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005211
cristy3ed852e2009-09-05 21:47:34 +00005212 if (GetNextImageInList(image) == (Image *) NULL)
5213 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00005214
cristy3ed852e2009-09-05 21:47:34 +00005215 image=SyncNextImageInList(image);
5216 mng_info->image=image;
5217 }
5218
5219 if ((mng_info->mng_width > 65535L) ||
5220 (mng_info->mng_height > 65535L))
5221 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00005222
cristy3b6fd2e2011-05-20 12:53:50 +00005223 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00005224 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00005225 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00005226
cristy3ed852e2009-09-05 21:47:34 +00005227 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00005228 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00005229 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00005230 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00005231 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005232
cristy3ed852e2009-09-05 21:47:34 +00005233 for (i=0; i < MNG_MAX_OBJECTS; i++)
5234 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005235
cristy3ed852e2009-09-05 21:47:34 +00005236 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5237 continue;
5238 }
5239
5240 if (memcmp(type,mng_TERM,4) == 0)
5241 {
5242 int
5243 repeat=0;
5244
5245
5246 if (length)
5247 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005248
cristy3ed852e2009-09-05 21:47:34 +00005249 if (repeat == 3)
5250 {
cristy8182b072010-05-30 20:10:53 +00005251 final_delay=(png_uint_32) mng_get_long(&p[2]);
5252 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00005253
cristy3ed852e2009-09-05 21:47:34 +00005254 if (mng_iterations == PNG_UINT_31_MAX)
5255 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00005256
cristy3ed852e2009-09-05 21:47:34 +00005257 image->iterations=mng_iterations;
5258 term_chunk_found=MagickTrue;
5259 }
glennrp0fe50b42010-11-16 03:52:51 +00005260
cristy3ed852e2009-09-05 21:47:34 +00005261 if (logging != MagickFalse)
5262 {
5263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp280283d2014-08-01 15:02:04 +00005264 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5265 repeat,(double) final_delay, (double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00005266 }
glennrp0fe50b42010-11-16 03:52:51 +00005267
cristy3ed852e2009-09-05 21:47:34 +00005268 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5269 continue;
5270 }
5271 if (memcmp(type,mng_DEFI,4) == 0)
5272 {
5273 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005274 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005275 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5276 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005277
cristy3ed852e2009-09-05 21:47:34 +00005278 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005279
cristy3ed852e2009-09-05 21:47:34 +00005280 if (mng_type == 2 && object_id != 0)
cristy16ea1392012-03-21 20:38:41 +00005281 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005282 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5283 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005284
cristy3ed852e2009-09-05 21:47:34 +00005285 if (object_id > MNG_MAX_OBJECTS)
5286 {
5287 /*
glennrpedaa0382012-04-12 14:16:21 +00005288 Instead of using a warning we should allocate a larger
cristy3ed852e2009-09-05 21:47:34 +00005289 MngInfo structure and continue.
5290 */
cristy16ea1392012-03-21 20:38:41 +00005291 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005292 CoderError,"object id too large","`%s'",image->filename);
5293 object_id=MNG_MAX_OBJECTS;
5294 }
glennrp0fe50b42010-11-16 03:52:51 +00005295
cristy3ed852e2009-09-05 21:47:34 +00005296 if (mng_info->exists[object_id])
5297 if (mng_info->frozen[object_id])
5298 {
5299 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristy16ea1392012-03-21 20:38:41 +00005300 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005301 GetMagickModule(),CoderError,
5302 "DEFI cannot redefine a frozen MNG object","`%s'",
5303 image->filename);
5304 continue;
5305 }
glennrp0fe50b42010-11-16 03:52:51 +00005306
cristy3ed852e2009-09-05 21:47:34 +00005307 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005308
cristy3ed852e2009-09-05 21:47:34 +00005309 if (length > 2)
5310 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005311
cristy3ed852e2009-09-05 21:47:34 +00005312 /*
5313 Extract object offset info.
5314 */
5315 if (length > 11)
5316 {
glennrp0fe50b42010-11-16 03:52:51 +00005317 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5318 (p[5] << 16) | (p[6] << 8) | p[7]);
5319
5320 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5321 (p[9] << 16) | (p[10] << 8) | p[11]);
5322
cristy3ed852e2009-09-05 21:47:34 +00005323 if (logging != MagickFalse)
5324 {
5325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp280283d2014-08-01 15:02:04 +00005326 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5327 object_id,(double) mng_info->x_off[object_id],
5328 object_id,(double) mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005329 }
5330 }
glennrp0fe50b42010-11-16 03:52:51 +00005331
cristy3ed852e2009-09-05 21:47:34 +00005332 /*
5333 Extract object clipping info.
5334 */
5335 if (length > 27)
5336 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5337 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005338
cristy3ed852e2009-09-05 21:47:34 +00005339 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5340 continue;
5341 }
5342 if (memcmp(type,mng_bKGD,4) == 0)
5343 {
5344 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005345
cristy3ed852e2009-09-05 21:47:34 +00005346 if (length > 5)
5347 {
5348 mng_info->mng_global_bkgd.red=
5349 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005350
cristy3ed852e2009-09-05 21:47:34 +00005351 mng_info->mng_global_bkgd.green=
5352 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005353
cristy3ed852e2009-09-05 21:47:34 +00005354 mng_info->mng_global_bkgd.blue=
5355 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005356
cristy3ed852e2009-09-05 21:47:34 +00005357 mng_info->have_global_bkgd=MagickTrue;
5358 }
glennrp0fe50b42010-11-16 03:52:51 +00005359
cristy3ed852e2009-09-05 21:47:34 +00005360 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5361 continue;
5362 }
5363 if (memcmp(type,mng_BACK,4) == 0)
5364 {
5365#if defined(MNG_INSERT_LAYERS)
5366 if (length > 6)
5367 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005368
cristy3ed852e2009-09-05 21:47:34 +00005369 else
5370 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005371
cristy3ed852e2009-09-05 21:47:34 +00005372 if (mandatory_back && length > 5)
5373 {
5374 mng_background_color.red=
5375 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005376
cristy3ed852e2009-09-05 21:47:34 +00005377 mng_background_color.green=
5378 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005379
cristy3ed852e2009-09-05 21:47:34 +00005380 mng_background_color.blue=
5381 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005382
cristy16ea1392012-03-21 20:38:41 +00005383 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005384 }
glennrp0fe50b42010-11-16 03:52:51 +00005385
cristy3ed852e2009-09-05 21:47:34 +00005386#ifdef MNG_OBJECT_BUFFERS
5387 if (length > 8)
5388 mng_background_object=(p[7] << 8) | p[8];
5389#endif
5390#endif
5391 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5392 continue;
5393 }
glennrp47b9dd52010-11-24 18:12:06 +00005394
cristy3ed852e2009-09-05 21:47:34 +00005395 if (memcmp(type,mng_PLTE,4) == 0)
5396 {
glennrp47b9dd52010-11-24 18:12:06 +00005397 /* Read global PLTE. */
5398
cristy3ed852e2009-09-05 21:47:34 +00005399 if (length && (length < 769))
5400 {
5401 if (mng_info->global_plte == (png_colorp) NULL)
5402 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5403 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005404
cristybb503372010-05-27 20:51:26 +00005405 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005406 {
5407 mng_info->global_plte[i].red=p[3*i];
5408 mng_info->global_plte[i].green=p[3*i+1];
5409 mng_info->global_plte[i].blue=p[3*i+2];
5410 }
glennrp0fe50b42010-11-16 03:52:51 +00005411
cristy35ef8242010-06-03 16:24:13 +00005412 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005413 }
5414#ifdef MNG_LOOSE
5415 for ( ; i < 256; i++)
5416 {
5417 mng_info->global_plte[i].red=i;
5418 mng_info->global_plte[i].green=i;
5419 mng_info->global_plte[i].blue=i;
5420 }
glennrp0fe50b42010-11-16 03:52:51 +00005421
cristy3ed852e2009-09-05 21:47:34 +00005422 if (length)
5423 mng_info->global_plte_length=256;
5424#endif
5425 else
5426 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005427
cristy3ed852e2009-09-05 21:47:34 +00005428 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5429 continue;
5430 }
glennrp47b9dd52010-11-24 18:12:06 +00005431
cristy3ed852e2009-09-05 21:47:34 +00005432 if (memcmp(type,mng_tRNS,4) == 0)
5433 {
5434 /* read global tRNS */
5435
5436 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005437 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005438 mng_info->global_trns[i]=p[i];
5439
5440#ifdef MNG_LOOSE
5441 for ( ; i < 256; i++)
5442 mng_info->global_trns[i]=255;
5443#endif
cristy12560f32010-06-03 16:51:08 +00005444 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005445 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5446 continue;
5447 }
5448 if (memcmp(type,mng_gAMA,4) == 0)
5449 {
5450 if (length == 4)
5451 {
cristybb503372010-05-27 20:51:26 +00005452 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005453 igamma;
5454
cristy8182b072010-05-30 20:10:53 +00005455 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005456 mng_info->global_gamma=((float) igamma)*0.00001;
5457 mng_info->have_global_gama=MagickTrue;
5458 }
glennrp0fe50b42010-11-16 03:52:51 +00005459
cristy3ed852e2009-09-05 21:47:34 +00005460 else
5461 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005462
cristy3ed852e2009-09-05 21:47:34 +00005463 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5464 continue;
5465 }
5466
5467 if (memcmp(type,mng_cHRM,4) == 0)
5468 {
glennrp47b9dd52010-11-24 18:12:06 +00005469 /* Read global cHRM */
5470
cristy3ed852e2009-09-05 21:47:34 +00005471 if (length == 32)
5472 {
cristy8182b072010-05-30 20:10:53 +00005473 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5474 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5475 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005476 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005477 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005478 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005479 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005480 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005481 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005482 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005483 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005484 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005485 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005486 mng_info->have_global_chrm=MagickTrue;
5487 }
5488 else
5489 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005490
cristy3ed852e2009-09-05 21:47:34 +00005491 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5492 continue;
5493 }
glennrp47b9dd52010-11-24 18:12:06 +00005494
cristy3ed852e2009-09-05 21:47:34 +00005495 if (memcmp(type,mng_sRGB,4) == 0)
5496 {
5497 /*
5498 Read global sRGB.
5499 */
5500 if (length)
5501 {
glennrpe610a072010-08-05 17:08:46 +00005502 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005503 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005504 mng_info->have_global_srgb=MagickTrue;
5505 }
5506 else
5507 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005508
cristy3ed852e2009-09-05 21:47:34 +00005509 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5510 continue;
5511 }
glennrp47b9dd52010-11-24 18:12:06 +00005512
cristy3ed852e2009-09-05 21:47:34 +00005513 if (memcmp(type,mng_iCCP,4) == 0)
5514 {
glennrpfd05d622011-02-25 04:10:33 +00005515 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005516
5517 /*
5518 Read global iCCP.
5519 */
5520 if (length)
5521 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005522
cristy3ed852e2009-09-05 21:47:34 +00005523 continue;
5524 }
glennrp47b9dd52010-11-24 18:12:06 +00005525
cristy3ed852e2009-09-05 21:47:34 +00005526 if (memcmp(type,mng_FRAM,4) == 0)
5527 {
5528 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005529 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005530 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5531 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005532
cristy3ed852e2009-09-05 21:47:34 +00005533 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5534 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005535
cristy3ed852e2009-09-05 21:47:34 +00005536 frame_delay=default_frame_delay;
5537 frame_timeout=default_frame_timeout;
5538 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005539
cristy3ed852e2009-09-05 21:47:34 +00005540 if (length)
5541 if (p[0])
5542 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005543
cristy3ed852e2009-09-05 21:47:34 +00005544 if (logging != MagickFalse)
5545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5546 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005547
cristy3ed852e2009-09-05 21:47:34 +00005548 if (length > 6)
5549 {
glennrp47b9dd52010-11-24 18:12:06 +00005550 /* Note the delay and frame clipping boundaries. */
5551
cristy3ed852e2009-09-05 21:47:34 +00005552 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005553
cristybb503372010-05-27 20:51:26 +00005554 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005555 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005556
cristy3ed852e2009-09-05 21:47:34 +00005557 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005558
cristybb503372010-05-27 20:51:26 +00005559 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005560 {
5561 int
5562 change_delay,
5563 change_timeout,
5564 change_clipping;
5565
5566 change_delay=(*p++);
5567 change_timeout=(*p++);
5568 change_clipping=(*p++);
5569 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005570
cristy3ed852e2009-09-05 21:47:34 +00005571 if (change_delay)
5572 {
cristy8182b072010-05-30 20:10:53 +00005573 frame_delay=1UL*image->ticks_per_second*
5574 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005575
cristy8182b072010-05-30 20:10:53 +00005576 if (mng_info->ticks_per_second != 0)
5577 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005578
glennrpbb010dd2010-06-01 13:07:15 +00005579 else
5580 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005581
cristy3ed852e2009-09-05 21:47:34 +00005582 if (change_delay == 2)
5583 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005584
cristy3ed852e2009-09-05 21:47:34 +00005585 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005586
cristy3ed852e2009-09-05 21:47:34 +00005587 if (logging != MagickFalse)
5588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005589 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005590 }
glennrp47b9dd52010-11-24 18:12:06 +00005591
cristy3ed852e2009-09-05 21:47:34 +00005592 if (change_timeout)
5593 {
glennrpbb010dd2010-06-01 13:07:15 +00005594 frame_timeout=1UL*image->ticks_per_second*
5595 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005596
glennrpbb010dd2010-06-01 13:07:15 +00005597 if (mng_info->ticks_per_second != 0)
5598 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005599
glennrpbb010dd2010-06-01 13:07:15 +00005600 else
5601 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005602
cristy3ed852e2009-09-05 21:47:34 +00005603 if (change_delay == 2)
5604 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005605
cristy3ed852e2009-09-05 21:47:34 +00005606 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005607
cristy3ed852e2009-09-05 21:47:34 +00005608 if (logging != MagickFalse)
5609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005610 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005611 }
glennrp47b9dd52010-11-24 18:12:06 +00005612
cristy3ed852e2009-09-05 21:47:34 +00005613 if (change_clipping)
5614 {
5615 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5616 p+=17;
5617 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619 if (logging != MagickFalse)
5620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005621 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005622 (double) fb.left,(double) fb.right,(double) fb.top,
5623 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005624
cristy3ed852e2009-09-05 21:47:34 +00005625 if (change_clipping == 2)
5626 default_fb=fb;
5627 }
5628 }
5629 }
5630 mng_info->clip=fb;
5631 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005632
cristybb503372010-05-27 20:51:26 +00005633 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005634 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005635
cristybb503372010-05-27 20:51:26 +00005636 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005637 -mng_info->clip.top);
5638 /*
5639 Insert a background layer behind the frame if framing_mode is 4.
5640 */
5641#if defined(MNG_INSERT_LAYERS)
5642 if (logging != MagickFalse)
5643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005644 " subframe_width=%.20g, subframe_height=%.20g",(double)
5645 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005646
cristy3ed852e2009-09-05 21:47:34 +00005647 if (insert_layers && (mng_info->framing_mode == 4) &&
5648 (subframe_width) && (subframe_height))
5649 {
glennrp47b9dd52010-11-24 18:12:06 +00005650 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005651 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005652 {
cristy16ea1392012-03-21 20:38:41 +00005653 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005654
cristy3ed852e2009-09-05 21:47:34 +00005655 if (GetNextImageInList(image) == (Image *) NULL)
5656 {
5657 image=DestroyImageList(image);
5658 MngInfoFreeStruct(mng_info,&have_mng_structure);
5659 return((Image *) NULL);
5660 }
glennrp47b9dd52010-11-24 18:12:06 +00005661
cristy3ed852e2009-09-05 21:47:34 +00005662 image=SyncNextImageInList(image);
5663 }
glennrp0fe50b42010-11-16 03:52:51 +00005664
cristy3ed852e2009-09-05 21:47:34 +00005665 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005666
cristy3ed852e2009-09-05 21:47:34 +00005667 if (term_chunk_found)
5668 {
5669 image->start_loop=MagickTrue;
5670 image->iterations=mng_iterations;
5671 term_chunk_found=MagickFalse;
5672 }
glennrp0fe50b42010-11-16 03:52:51 +00005673
cristy3ed852e2009-09-05 21:47:34 +00005674 else
5675 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005676
cristy3ed852e2009-09-05 21:47:34 +00005677 image->columns=subframe_width;
5678 image->rows=subframe_height;
5679 image->page.width=subframe_width;
5680 image->page.height=subframe_height;
5681 image->page.x=mng_info->clip.left;
5682 image->page.y=mng_info->clip.top;
5683 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00005684 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00005685 image->delay=0;
cristy16ea1392012-03-21 20:38:41 +00005686 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005687
cristy3ed852e2009-09-05 21:47:34 +00005688 if (logging != MagickFalse)
5689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005690 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005691 (double) mng_info->clip.left,(double) mng_info->clip.right,
5692 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005693 }
5694#endif
5695 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5696 continue;
5697 }
5698 if (memcmp(type,mng_CLIP,4) == 0)
5699 {
5700 unsigned int
5701 first_object,
5702 last_object;
5703
5704 /*
5705 Read CLIP.
5706 */
5707 first_object=(p[0] << 8) | p[1];
5708 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005709
cristy3ed852e2009-09-05 21:47:34 +00005710 for (i=(int) first_object; i <= (int) last_object; i++)
5711 {
5712 if (mng_info->exists[i] && !mng_info->frozen[i])
5713 {
5714 MngBox
5715 box;
5716
5717 box=mng_info->object_clip[i];
5718 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5719 }
5720 }
glennrp47b9dd52010-11-24 18:12:06 +00005721
cristy3ed852e2009-09-05 21:47:34 +00005722 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5723 continue;
5724 }
5725 if (memcmp(type,mng_SAVE,4) == 0)
5726 {
5727 for (i=1; i < MNG_MAX_OBJECTS; i++)
5728 if (mng_info->exists[i])
5729 {
5730 mng_info->frozen[i]=MagickTrue;
5731#ifdef MNG_OBJECT_BUFFERS
5732 if (mng_info->ob[i] != (MngBuffer *) NULL)
5733 mng_info->ob[i]->frozen=MagickTrue;
5734#endif
5735 }
glennrp0fe50b42010-11-16 03:52:51 +00005736
cristy3ed852e2009-09-05 21:47:34 +00005737 if (length)
5738 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005739
cristy3ed852e2009-09-05 21:47:34 +00005740 continue;
5741 }
5742
5743 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5744 {
glennrp47b9dd52010-11-24 18:12:06 +00005745 /* Read DISC or SEEK. */
5746
cristy3ed852e2009-09-05 21:47:34 +00005747 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5748 {
5749 for (i=1; i < MNG_MAX_OBJECTS; i++)
5750 MngInfoDiscardObject(mng_info,i);
5751 }
glennrp0fe50b42010-11-16 03:52:51 +00005752
cristy3ed852e2009-09-05 21:47:34 +00005753 else
5754 {
cristybb503372010-05-27 20:51:26 +00005755 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005756 j;
5757
cristybb503372010-05-27 20:51:26 +00005758 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005759 {
5760 i=p[j] << 8 | p[j+1];
5761 MngInfoDiscardObject(mng_info,i);
5762 }
5763 }
glennrp0fe50b42010-11-16 03:52:51 +00005764
cristy3ed852e2009-09-05 21:47:34 +00005765 if (length)
5766 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005767
cristy3ed852e2009-09-05 21:47:34 +00005768 continue;
5769 }
glennrp47b9dd52010-11-24 18:12:06 +00005770
cristy3ed852e2009-09-05 21:47:34 +00005771 if (memcmp(type,mng_MOVE,4) == 0)
5772 {
cristybb503372010-05-27 20:51:26 +00005773 size_t
cristy3ed852e2009-09-05 21:47:34 +00005774 first_object,
5775 last_object;
5776
glennrp47b9dd52010-11-24 18:12:06 +00005777 /* read MOVE */
5778
cristy3ed852e2009-09-05 21:47:34 +00005779 first_object=(p[0] << 8) | p[1];
5780 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005781 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005782 {
5783 if (mng_info->exists[i] && !mng_info->frozen[i])
5784 {
5785 MngPair
5786 new_pair;
5787
5788 MngPair
5789 old_pair;
5790
5791 old_pair.a=mng_info->x_off[i];
5792 old_pair.b=mng_info->y_off[i];
5793 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5794 mng_info->x_off[i]=new_pair.a;
5795 mng_info->y_off[i]=new_pair.b;
5796 }
5797 }
glennrp47b9dd52010-11-24 18:12:06 +00005798
cristy3ed852e2009-09-05 21:47:34 +00005799 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5800 continue;
5801 }
5802
5803 if (memcmp(type,mng_LOOP,4) == 0)
5804 {
cristybb503372010-05-27 20:51:26 +00005805 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005806 loop_level=chunk[0];
5807 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005808
5809 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005810 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005811
cristy3ed852e2009-09-05 21:47:34 +00005812 if (logging != MagickFalse)
5813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005814 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5815 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 if (loop_iters == 0)
5818 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005819
cristy3ed852e2009-09-05 21:47:34 +00005820 else
5821 {
5822 mng_info->loop_jump[loop_level]=TellBlob(image);
5823 mng_info->loop_count[loop_level]=loop_iters;
5824 }
glennrp0fe50b42010-11-16 03:52:51 +00005825
cristy3ed852e2009-09-05 21:47:34 +00005826 mng_info->loop_iteration[loop_level]=0;
5827 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5828 continue;
5829 }
glennrp47b9dd52010-11-24 18:12:06 +00005830
cristy3ed852e2009-09-05 21:47:34 +00005831 if (memcmp(type,mng_ENDL,4) == 0)
5832 {
5833 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristy3ed852e2009-09-05 21:47:34 +00005835 if (skipping_loop > 0)
5836 {
5837 if (skipping_loop == loop_level)
5838 {
5839 /*
5840 Found end of zero-iteration loop.
5841 */
5842 skipping_loop=(-1);
5843 mng_info->loop_active[loop_level]=0;
5844 }
5845 }
glennrp47b9dd52010-11-24 18:12:06 +00005846
cristy3ed852e2009-09-05 21:47:34 +00005847 else
5848 {
5849 if (mng_info->loop_active[loop_level] == 1)
5850 {
5851 mng_info->loop_count[loop_level]--;
5852 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005853
cristy3ed852e2009-09-05 21:47:34 +00005854 if (logging != MagickFalse)
5855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005856 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005857 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005858 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005859
cristy3ed852e2009-09-05 21:47:34 +00005860 if (mng_info->loop_count[loop_level] != 0)
5861 {
5862 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5863 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005864
cristy3ed852e2009-09-05 21:47:34 +00005865 if (offset < 0)
5866 ThrowReaderException(CorruptImageError,
5867 "ImproperImageHeader");
5868 }
glennrp47b9dd52010-11-24 18:12:06 +00005869
cristy3ed852e2009-09-05 21:47:34 +00005870 else
5871 {
5872 short
5873 last_level;
5874
5875 /*
5876 Finished loop.
5877 */
5878 mng_info->loop_active[loop_level]=0;
5879 last_level=(-1);
5880 for (i=0; i < loop_level; i++)
5881 if (mng_info->loop_active[i] == 1)
5882 last_level=(short) i;
5883 loop_level=last_level;
5884 }
5885 }
5886 }
glennrp47b9dd52010-11-24 18:12:06 +00005887
cristy3ed852e2009-09-05 21:47:34 +00005888 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5889 continue;
5890 }
glennrp47b9dd52010-11-24 18:12:06 +00005891
cristy3ed852e2009-09-05 21:47:34 +00005892 if (memcmp(type,mng_CLON,4) == 0)
5893 {
5894 if (mng_info->clon_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005895 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005896 CoderError,"CLON is not implemented yet","`%s'",
5897 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005898
cristy3ed852e2009-09-05 21:47:34 +00005899 mng_info->clon_warning++;
5900 }
glennrp47b9dd52010-11-24 18:12:06 +00005901
cristy3ed852e2009-09-05 21:47:34 +00005902 if (memcmp(type,mng_MAGN,4) == 0)
5903 {
5904 png_uint_16
5905 magn_first,
5906 magn_last,
5907 magn_mb,
5908 magn_ml,
5909 magn_mr,
5910 magn_mt,
5911 magn_mx,
5912 magn_my,
5913 magn_methx,
5914 magn_methy;
5915
5916 if (length > 1)
5917 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005918
cristy3ed852e2009-09-05 21:47:34 +00005919 else
5920 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005921
cristy3ed852e2009-09-05 21:47:34 +00005922 if (length > 3)
5923 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005924
cristy3ed852e2009-09-05 21:47:34 +00005925 else
5926 magn_last=magn_first;
5927#ifndef MNG_OBJECT_BUFFERS
5928 if (magn_first || magn_last)
5929 if (mng_info->magn_warning == 0)
5930 {
cristy16ea1392012-03-21 20:38:41 +00005931 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005932 GetMagickModule(),CoderError,
5933 "MAGN is not implemented yet for nonzero objects",
5934 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005935
cristy3ed852e2009-09-05 21:47:34 +00005936 mng_info->magn_warning++;
5937 }
5938#endif
5939 if (length > 4)
5940 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005941
cristy3ed852e2009-09-05 21:47:34 +00005942 else
5943 magn_methx=0;
5944
5945 if (length > 6)
5946 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005947
cristy3ed852e2009-09-05 21:47:34 +00005948 else
5949 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005950
cristy3ed852e2009-09-05 21:47:34 +00005951 if (magn_mx == 0)
5952 magn_mx=1;
5953
5954 if (length > 8)
5955 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005956
cristy3ed852e2009-09-05 21:47:34 +00005957 else
5958 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005959
cristy3ed852e2009-09-05 21:47:34 +00005960 if (magn_my == 0)
5961 magn_my=1;
5962
5963 if (length > 10)
5964 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005965
cristy3ed852e2009-09-05 21:47:34 +00005966 else
5967 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005968
cristy3ed852e2009-09-05 21:47:34 +00005969 if (magn_ml == 0)
5970 magn_ml=1;
5971
5972 if (length > 12)
5973 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005974
cristy3ed852e2009-09-05 21:47:34 +00005975 else
5976 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005977
cristy3ed852e2009-09-05 21:47:34 +00005978 if (magn_mr == 0)
5979 magn_mr=1;
5980
5981 if (length > 14)
5982 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005983
cristy3ed852e2009-09-05 21:47:34 +00005984 else
5985 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005986
cristy3ed852e2009-09-05 21:47:34 +00005987 if (magn_mt == 0)
5988 magn_mt=1;
5989
5990 if (length > 16)
5991 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005992
cristy3ed852e2009-09-05 21:47:34 +00005993 else
5994 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005995
cristy3ed852e2009-09-05 21:47:34 +00005996 if (magn_mb == 0)
5997 magn_mb=1;
5998
5999 if (length > 17)
6000 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00006001
cristy3ed852e2009-09-05 21:47:34 +00006002 else
6003 magn_methy=magn_methx;
6004
glennrp47b9dd52010-11-24 18:12:06 +00006005
cristy3ed852e2009-09-05 21:47:34 +00006006 if (magn_methx > 5 || magn_methy > 5)
6007 if (mng_info->magn_warning == 0)
6008 {
cristy16ea1392012-03-21 20:38:41 +00006009 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00006010 GetMagickModule(),CoderError,
6011 "Unknown MAGN method in MNG datastream","`%s'",
6012 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006013
cristy3ed852e2009-09-05 21:47:34 +00006014 mng_info->magn_warning++;
6015 }
6016#ifdef MNG_OBJECT_BUFFERS
6017 /* Magnify existing objects in the range magn_first to magn_last */
6018#endif
6019 if (magn_first == 0 || magn_last == 0)
6020 {
6021 /* Save the magnification factors for object 0 */
6022 mng_info->magn_mb=magn_mb;
6023 mng_info->magn_ml=magn_ml;
6024 mng_info->magn_mr=magn_mr;
6025 mng_info->magn_mt=magn_mt;
6026 mng_info->magn_mx=magn_mx;
6027 mng_info->magn_my=magn_my;
6028 mng_info->magn_methx=magn_methx;
6029 mng_info->magn_methy=magn_methy;
6030 }
6031 }
glennrp47b9dd52010-11-24 18:12:06 +00006032
cristy3ed852e2009-09-05 21:47:34 +00006033 if (memcmp(type,mng_PAST,4) == 0)
6034 {
6035 if (mng_info->past_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006036 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006037 CoderError,"PAST is not implemented yet","`%s'",
6038 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006039
cristy3ed852e2009-09-05 21:47:34 +00006040 mng_info->past_warning++;
6041 }
glennrp47b9dd52010-11-24 18:12:06 +00006042
cristy3ed852e2009-09-05 21:47:34 +00006043 if (memcmp(type,mng_SHOW,4) == 0)
6044 {
6045 if (mng_info->show_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006046 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006047 CoderError,"SHOW is not implemented yet","`%s'",
6048 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006049
cristy3ed852e2009-09-05 21:47:34 +00006050 mng_info->show_warning++;
6051 }
glennrp47b9dd52010-11-24 18:12:06 +00006052
cristy3ed852e2009-09-05 21:47:34 +00006053 if (memcmp(type,mng_sBIT,4) == 0)
6054 {
6055 if (length < 4)
6056 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006057
cristy3ed852e2009-09-05 21:47:34 +00006058 else
6059 {
6060 mng_info->global_sbit.gray=p[0];
6061 mng_info->global_sbit.red=p[0];
6062 mng_info->global_sbit.green=p[1];
6063 mng_info->global_sbit.blue=p[2];
6064 mng_info->global_sbit.alpha=p[3];
6065 mng_info->have_global_sbit=MagickTrue;
6066 }
6067 }
6068 if (memcmp(type,mng_pHYs,4) == 0)
6069 {
6070 if (length > 8)
6071 {
6072 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00006073 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00006074 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00006075 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00006076 mng_info->global_phys_unit_type=p[8];
6077 mng_info->have_global_phys=MagickTrue;
6078 }
glennrp47b9dd52010-11-24 18:12:06 +00006079
cristy3ed852e2009-09-05 21:47:34 +00006080 else
6081 mng_info->have_global_phys=MagickFalse;
6082 }
6083 if (memcmp(type,mng_pHYg,4) == 0)
6084 {
6085 if (mng_info->phyg_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006086 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006087 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006088
cristy3ed852e2009-09-05 21:47:34 +00006089 mng_info->phyg_warning++;
6090 }
6091 if (memcmp(type,mng_BASI,4) == 0)
6092 {
6093 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00006094
cristy3ed852e2009-09-05 21:47:34 +00006095 if (mng_info->basi_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00006096 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006097 CoderError,"BASI is not implemented yet","`%s'",
6098 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006099
cristy3ed852e2009-09-05 21:47:34 +00006100 mng_info->basi_warning++;
6101#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00006102 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00006103 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00006104 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00006105 (p[6] << 8) | p[7]);
6106 basi_color_type=p[8];
6107 basi_compression_method=p[9];
6108 basi_filter_type=p[10];
6109 basi_interlace_method=p[11];
6110 if (length > 11)
6111 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00006112
cristy3ed852e2009-09-05 21:47:34 +00006113 else
6114 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00006115
cristy3ed852e2009-09-05 21:47:34 +00006116 if (length > 13)
6117 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00006118
cristy3ed852e2009-09-05 21:47:34 +00006119 else
6120 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00006121
cristy3ed852e2009-09-05 21:47:34 +00006122 if (length > 15)
6123 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00006124
cristy3ed852e2009-09-05 21:47:34 +00006125 else
6126 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00006127
cristy3ed852e2009-09-05 21:47:34 +00006128 if (length > 17)
6129 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00006130
cristy3ed852e2009-09-05 21:47:34 +00006131 else
6132 {
6133 if (basi_sample_depth == 16)
6134 basi_alpha=65535L;
6135 else
6136 basi_alpha=255;
6137 }
glennrp47b9dd52010-11-24 18:12:06 +00006138
cristy3ed852e2009-09-05 21:47:34 +00006139 if (length > 19)
6140 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00006141
cristy3ed852e2009-09-05 21:47:34 +00006142 else
6143 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00006144
cristy3ed852e2009-09-05 21:47:34 +00006145#endif
6146 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6147 continue;
6148 }
glennrp47b9dd52010-11-24 18:12:06 +00006149
cristy3ed852e2009-09-05 21:47:34 +00006150 if (memcmp(type,mng_IHDR,4)
6151#if defined(JNG_SUPPORTED)
6152 && memcmp(type,mng_JHDR,4)
6153#endif
6154 )
6155 {
6156 /* Not an IHDR or JHDR chunk */
6157 if (length)
6158 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00006159
cristy3ed852e2009-09-05 21:47:34 +00006160 continue;
6161 }
6162/* Process IHDR */
6163 if (logging != MagickFalse)
6164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6165 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006166
cristy3ed852e2009-09-05 21:47:34 +00006167 mng_info->exists[object_id]=MagickTrue;
6168 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00006169
cristy3ed852e2009-09-05 21:47:34 +00006170 if (mng_info->invisible[object_id])
6171 {
6172 if (logging != MagickFalse)
6173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6174 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00006175
cristy3ed852e2009-09-05 21:47:34 +00006176 skip_to_iend=MagickTrue;
6177 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6178 continue;
6179 }
6180#if defined(MNG_INSERT_LAYERS)
6181 if (length < 8)
6182 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00006183
cristy8182b072010-05-30 20:10:53 +00006184 image_width=(size_t) mng_get_long(p);
6185 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00006186#endif
6187 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6188
6189 /*
6190 Insert a transparent background layer behind the entire animation
6191 if it is not full screen.
6192 */
6193#if defined(MNG_INSERT_LAYERS)
6194 if (insert_layers && mng_type && first_mng_object)
6195 {
6196 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6197 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00006198 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00006199 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00006200 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00006201 {
cristy16ea1392012-03-21 20:38:41 +00006202 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006203 {
6204 /*
6205 Allocate next image structure.
6206 */
cristy16ea1392012-03-21 20:38:41 +00006207 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006208
cristy3ed852e2009-09-05 21:47:34 +00006209 if (GetNextImageInList(image) == (Image *) NULL)
6210 {
6211 image=DestroyImageList(image);
6212 MngInfoFreeStruct(mng_info,&have_mng_structure);
6213 return((Image *) NULL);
6214 }
glennrp47b9dd52010-11-24 18:12:06 +00006215
cristy3ed852e2009-09-05 21:47:34 +00006216 image=SyncNextImageInList(image);
6217 }
6218 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00006219
cristy3ed852e2009-09-05 21:47:34 +00006220 if (term_chunk_found)
6221 {
6222 image->start_loop=MagickTrue;
6223 image->iterations=mng_iterations;
6224 term_chunk_found=MagickFalse;
6225 }
glennrp47b9dd52010-11-24 18:12:06 +00006226
cristy3ed852e2009-09-05 21:47:34 +00006227 else
6228 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006229
6230 /* Make a background rectangle. */
6231
cristy3ed852e2009-09-05 21:47:34 +00006232 image->delay=0;
6233 image->columns=mng_info->mng_width;
6234 image->rows=mng_info->mng_height;
6235 image->page.width=mng_info->mng_width;
6236 image->page.height=mng_info->mng_height;
6237 image->page.x=0;
6238 image->page.y=0;
6239 image->background_color=mng_background_color;
cristy16ea1392012-03-21 20:38:41 +00006240 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006241 if (logging != MagickFalse)
6242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006243 " Inserted transparent background layer, W=%.20g, H=%.20g",
6244 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00006245 }
6246 }
6247 /*
6248 Insert a background layer behind the upcoming image if
6249 framing_mode is 3, and we haven't already inserted one.
6250 */
6251 if (insert_layers && (mng_info->framing_mode == 3) &&
6252 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6253 (simplicity & 0x08)))
6254 {
cristy16ea1392012-03-21 20:38:41 +00006255 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006256 {
6257 /*
6258 Allocate next image structure.
6259 */
cristy16ea1392012-03-21 20:38:41 +00006260 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006261
cristy3ed852e2009-09-05 21:47:34 +00006262 if (GetNextImageInList(image) == (Image *) NULL)
6263 {
6264 image=DestroyImageList(image);
6265 MngInfoFreeStruct(mng_info,&have_mng_structure);
6266 return((Image *) NULL);
6267 }
glennrp47b9dd52010-11-24 18:12:06 +00006268
cristy3ed852e2009-09-05 21:47:34 +00006269 image=SyncNextImageInList(image);
6270 }
glennrp0fe50b42010-11-16 03:52:51 +00006271
cristy3ed852e2009-09-05 21:47:34 +00006272 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00006273
cristy3ed852e2009-09-05 21:47:34 +00006274 if (term_chunk_found)
6275 {
6276 image->start_loop=MagickTrue;
6277 image->iterations=mng_iterations;
6278 term_chunk_found=MagickFalse;
6279 }
glennrp0fe50b42010-11-16 03:52:51 +00006280
cristy3ed852e2009-09-05 21:47:34 +00006281 else
6282 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006283
cristy3ed852e2009-09-05 21:47:34 +00006284 image->delay=0;
6285 image->columns=subframe_width;
6286 image->rows=subframe_height;
6287 image->page.width=subframe_width;
6288 image->page.height=subframe_height;
6289 image->page.x=mng_info->clip.left;
6290 image->page.y=mng_info->clip.top;
6291 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00006292 image->alpha_trait=UndefinedPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00006293 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006294
cristy3ed852e2009-09-05 21:47:34 +00006295 if (logging != MagickFalse)
6296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006297 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006298 (double) mng_info->clip.left,(double) mng_info->clip.right,
6299 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006300 }
6301#endif /* MNG_INSERT_LAYERS */
6302 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006303
cristy16ea1392012-03-21 20:38:41 +00006304 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006305 {
6306 /*
6307 Allocate next image structure.
6308 */
cristy16ea1392012-03-21 20:38:41 +00006309 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006310
cristy3ed852e2009-09-05 21:47:34 +00006311 if (GetNextImageInList(image) == (Image *) NULL)
6312 {
6313 image=DestroyImageList(image);
6314 MngInfoFreeStruct(mng_info,&have_mng_structure);
6315 return((Image *) NULL);
6316 }
glennrp47b9dd52010-11-24 18:12:06 +00006317
cristy3ed852e2009-09-05 21:47:34 +00006318 image=SyncNextImageInList(image);
6319 }
6320 mng_info->image=image;
6321 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6322 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006323
cristy3ed852e2009-09-05 21:47:34 +00006324 if (status == MagickFalse)
6325 break;
glennrp0fe50b42010-11-16 03:52:51 +00006326
cristy3ed852e2009-09-05 21:47:34 +00006327 if (term_chunk_found)
6328 {
6329 image->start_loop=MagickTrue;
6330 term_chunk_found=MagickFalse;
6331 }
glennrp0fe50b42010-11-16 03:52:51 +00006332
cristy3ed852e2009-09-05 21:47:34 +00006333 else
6334 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006335
cristy3ed852e2009-09-05 21:47:34 +00006336 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6337 {
6338 image->delay=frame_delay;
6339 frame_delay=default_frame_delay;
6340 }
glennrp0fe50b42010-11-16 03:52:51 +00006341
cristy3ed852e2009-09-05 21:47:34 +00006342 else
6343 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006344
cristy3ed852e2009-09-05 21:47:34 +00006345 image->page.width=mng_info->mng_width;
6346 image->page.height=mng_info->mng_height;
6347 image->page.x=mng_info->x_off[object_id];
6348 image->page.y=mng_info->y_off[object_id];
6349 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006350
cristy3ed852e2009-09-05 21:47:34 +00006351 /*
6352 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6353 */
glennrp47b9dd52010-11-24 18:12:06 +00006354
cristy3ed852e2009-09-05 21:47:34 +00006355 if (logging != MagickFalse)
6356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6357 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6358 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006359
cristybb503372010-05-27 20:51:26 +00006360 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006361
cristy3ed852e2009-09-05 21:47:34 +00006362 if (offset < 0)
6363 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6364 }
6365
6366 previous=image;
6367 mng_info->image=image;
6368 mng_info->mng_type=mng_type;
6369 mng_info->object_id=object_id;
6370
6371 if (memcmp(type,mng_IHDR,4) == 0)
6372 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006373
cristy3ed852e2009-09-05 21:47:34 +00006374#if defined(JNG_SUPPORTED)
6375 else
6376 image=ReadOneJNGImage(mng_info,image_info,exception);
6377#endif
6378
6379 if (image == (Image *) NULL)
6380 {
6381 if (IsImageObject(previous) != MagickFalse)
6382 {
6383 (void) DestroyImageList(previous);
6384 (void) CloseBlob(previous);
6385 }
glennrp47b9dd52010-11-24 18:12:06 +00006386
cristy3ed852e2009-09-05 21:47:34 +00006387 MngInfoFreeStruct(mng_info,&have_mng_structure);
6388 return((Image *) NULL);
6389 }
glennrp0fe50b42010-11-16 03:52:51 +00006390
cristy3ed852e2009-09-05 21:47:34 +00006391 if (image->columns == 0 || image->rows == 0)
6392 {
6393 (void) CloseBlob(image);
6394 image=DestroyImageList(image);
6395 MngInfoFreeStruct(mng_info,&have_mng_structure);
6396 return((Image *) NULL);
6397 }
glennrp0fe50b42010-11-16 03:52:51 +00006398
cristy3ed852e2009-09-05 21:47:34 +00006399 mng_info->image=image;
6400
6401 if (mng_type)
6402 {
6403 MngBox
6404 crop_box;
6405
6406 if (mng_info->magn_methx || mng_info->magn_methy)
6407 {
6408 png_uint_32
6409 magnified_height,
6410 magnified_width;
6411
6412 if (logging != MagickFalse)
6413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6414 " Processing MNG MAGN chunk");
6415
6416 if (mng_info->magn_methx == 1)
6417 {
6418 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006419
cristy3ed852e2009-09-05 21:47:34 +00006420 if (image->columns > 1)
6421 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006422
cristy3ed852e2009-09-05 21:47:34 +00006423 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006424 magnified_width += (png_uint_32)
6425 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006426 }
glennrp47b9dd52010-11-24 18:12:06 +00006427
cristy3ed852e2009-09-05 21:47:34 +00006428 else
6429 {
cristy4e5bc842010-06-09 13:56:01 +00006430 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006431
cristy3ed852e2009-09-05 21:47:34 +00006432 if (image->columns > 1)
6433 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006434
cristy3ed852e2009-09-05 21:47:34 +00006435 if (image->columns > 2)
6436 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006437
cristy3ed852e2009-09-05 21:47:34 +00006438 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006439 magnified_width += (png_uint_32)
6440 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006441 }
glennrp47b9dd52010-11-24 18:12:06 +00006442
cristy3ed852e2009-09-05 21:47:34 +00006443 if (mng_info->magn_methy == 1)
6444 {
6445 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006446
cristy3ed852e2009-09-05 21:47:34 +00006447 if (image->rows > 1)
6448 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006449
cristy3ed852e2009-09-05 21:47:34 +00006450 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006451 magnified_height += (png_uint_32)
6452 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006453 }
glennrp47b9dd52010-11-24 18:12:06 +00006454
cristy3ed852e2009-09-05 21:47:34 +00006455 else
6456 {
cristy4e5bc842010-06-09 13:56:01 +00006457 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006458
cristy3ed852e2009-09-05 21:47:34 +00006459 if (image->rows > 1)
6460 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006461
cristy3ed852e2009-09-05 21:47:34 +00006462 if (image->rows > 2)
6463 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006464
cristy3ed852e2009-09-05 21:47:34 +00006465 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006466 magnified_height += (png_uint_32)
6467 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006468 }
glennrp47b9dd52010-11-24 18:12:06 +00006469
cristy3ed852e2009-09-05 21:47:34 +00006470 if (magnified_height > image->rows ||
6471 magnified_width > image->columns)
6472 {
6473 Image
6474 *large_image;
6475
6476 int
6477 yy;
6478
cristy16ea1392012-03-21 20:38:41 +00006479 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006480 *next,
6481 *prev;
6482
6483 png_uint_16
6484 magn_methx,
6485 magn_methy;
6486
cristy16ea1392012-03-21 20:38:41 +00006487 ssize_t
6488 m,
6489 y;
6490
6491 register Quantum
6492 *n,
6493 *q;
6494
6495 register ssize_t
6496 x;
6497
glennrp47b9dd52010-11-24 18:12:06 +00006498 /* Allocate next image structure. */
6499
cristy3ed852e2009-09-05 21:47:34 +00006500 if (logging != MagickFalse)
6501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6502 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006503
cristy16ea1392012-03-21 20:38:41 +00006504 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006505
cristy3ed852e2009-09-05 21:47:34 +00006506 if (GetNextImageInList(image) == (Image *) NULL)
6507 {
6508 image=DestroyImageList(image);
6509 MngInfoFreeStruct(mng_info,&have_mng_structure);
6510 return((Image *) NULL);
6511 }
6512
6513 large_image=SyncNextImageInList(image);
6514
6515 large_image->columns=magnified_width;
6516 large_image->rows=magnified_height;
6517
6518 magn_methx=mng_info->magn_methx;
6519 magn_methy=mng_info->magn_methy;
6520
glennrp3faa9a32011-04-23 14:00:25 +00006521#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006522#define QM unsigned short
6523 if (magn_methx != 1 || magn_methy != 1)
6524 {
6525 /*
6526 Scale pixels to unsigned shorts to prevent
6527 overflow of intermediate values of interpolations
6528 */
cristybb503372010-05-27 20:51:26 +00006529 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006530 {
6531 q=GetAuthenticPixels(image,0,y,image->columns,1,
6532 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006533
cristybb503372010-05-27 20:51:26 +00006534 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006535 {
cristy16ea1392012-03-21 20:38:41 +00006536 SetPixelRed(image,ScaleQuantumToShort(
6537 GetPixelRed(image,q)),q);
6538 SetPixelGreen(image,ScaleQuantumToShort(
6539 GetPixelGreen(image,q)),q);
6540 SetPixelBlue(image,ScaleQuantumToShort(
6541 GetPixelBlue(image,q)),q);
6542 SetPixelAlpha(image,ScaleQuantumToShort(
6543 GetPixelAlpha(image,q)),q);
6544 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006545 }
glennrp47b9dd52010-11-24 18:12:06 +00006546
cristy3ed852e2009-09-05 21:47:34 +00006547 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6548 break;
6549 }
6550 }
6551#else
6552#define QM Quantum
6553#endif
6554
cristy8a46d822012-08-28 23:32:39 +00006555 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006556 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006557
cristy3ed852e2009-09-05 21:47:34 +00006558 else
6559 {
cristy16ea1392012-03-21 20:38:41 +00006560 large_image->background_color.alpha=OpaqueAlpha;
6561 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006562
cristy3ed852e2009-09-05 21:47:34 +00006563 if (magn_methx == 4)
6564 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006565
cristy3ed852e2009-09-05 21:47:34 +00006566 if (magn_methx == 5)
6567 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006568
cristy3ed852e2009-09-05 21:47:34 +00006569 if (magn_methy == 4)
6570 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006571
cristy3ed852e2009-09-05 21:47:34 +00006572 if (magn_methy == 5)
6573 magn_methy=3;
6574 }
6575
6576 /* magnify the rows into the right side of the large image */
6577
6578 if (logging != MagickFalse)
6579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006580 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006581 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006582 yy=0;
cristy16ea1392012-03-21 20:38:41 +00006583 length=(size_t) image->columns*GetPixelChannels(image);
6584 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6585 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006586
cristy16ea1392012-03-21 20:38:41 +00006587 if ((prev == (Quantum *) NULL) ||
6588 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006589 {
6590 image=DestroyImageList(image);
6591 MngInfoFreeStruct(mng_info,&have_mng_structure);
6592 ThrowReaderException(ResourceLimitError,
6593 "MemoryAllocationFailed");
6594 }
glennrp47b9dd52010-11-24 18:12:06 +00006595
cristy3ed852e2009-09-05 21:47:34 +00006596 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6597 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006598
cristybb503372010-05-27 20:51:26 +00006599 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006600 {
6601 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006602 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006603
cristybb503372010-05-27 20:51:26 +00006604 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6605 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006606
cristybb503372010-05-27 20:51:26 +00006607 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6608 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006609
cristybb503372010-05-27 20:51:26 +00006610 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006611 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006612
cristy3ed852e2009-09-05 21:47:34 +00006613 else
cristybb503372010-05-27 20:51:26 +00006614 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006615
cristy3ed852e2009-09-05 21:47:34 +00006616 n=prev;
6617 prev=next;
6618 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006619
cristybb503372010-05-27 20:51:26 +00006620 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006621 {
6622 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6623 exception);
6624 (void) CopyMagickMemory(next,n,length);
6625 }
glennrp47b9dd52010-11-24 18:12:06 +00006626
cristy3ed852e2009-09-05 21:47:34 +00006627 for (i=0; i < m; i++, yy++)
6628 {
cristy16ea1392012-03-21 20:38:41 +00006629 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006630 *pixels;
6631
cristybb503372010-05-27 20:51:26 +00006632 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006633 pixels=prev;
6634 n=next;
6635 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006636 1,exception);
cristy16ea1392012-03-21 20:38:41 +00006637 q+=(large_image->columns-image->columns)*
6638 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006639
cristybb503372010-05-27 20:51:26 +00006640 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006641 {
glennrpfd05d622011-02-25 04:10:33 +00006642 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006643 /*
6644 if (image->storage_class == PseudoClass)
6645 {
6646 }
6647 */
6648
6649 if (magn_methy <= 1)
6650 {
glennrpbb4f99d2011-05-22 11:13:17 +00006651 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006652 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6653 SetPixelGreen(large_image,GetPixelGreen(image,
6654 pixels),q);
6655 SetPixelBlue(large_image,GetPixelBlue(image,
6656 pixels),q);
6657 SetPixelAlpha(large_image,GetPixelAlpha(image,
6658 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006659 }
glennrp47b9dd52010-11-24 18:12:06 +00006660
cristy3ed852e2009-09-05 21:47:34 +00006661 else if (magn_methy == 2 || magn_methy == 4)
6662 {
6663 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006664 {
cristy16ea1392012-03-21 20:38:41 +00006665 SetPixelRed(large_image,GetPixelRed(image,
6666 pixels),q);
6667 SetPixelGreen(large_image,GetPixelGreen(image,
6668 pixels),q);
6669 SetPixelBlue(large_image,GetPixelBlue(image,
6670 pixels),q);
6671 SetPixelAlpha(large_image,GetPixelAlpha(image,
6672 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006673 }
glennrp47b9dd52010-11-24 18:12:06 +00006674
cristy3ed852e2009-09-05 21:47:34 +00006675 else
6676 {
6677 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006678 SetPixelRed(large_image,((QM) (((ssize_t)
6679 (2*i*(GetPixelRed(image,n)
6680 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006681 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006682 +GetPixelRed(image,pixels)))),q);
6683 SetPixelGreen(large_image,((QM) (((ssize_t)
6684 (2*i*(GetPixelGreen(image,n)
6685 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006686 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006687 +GetPixelGreen(image,pixels)))),q);
6688 SetPixelBlue(large_image,((QM) (((ssize_t)
6689 (2*i*(GetPixelBlue(image,n)
6690 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006691 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006692 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006693
cristy8a46d822012-08-28 23:32:39 +00006694 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006695 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6696 (2*i*(GetPixelAlpha(image,n)
6697 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006698 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006699 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006700 }
glennrp47b9dd52010-11-24 18:12:06 +00006701
cristy3ed852e2009-09-05 21:47:34 +00006702 if (magn_methy == 4)
6703 {
6704 /* Replicate nearest */
6705 if (i <= ((m+1) << 1))
cristy16ea1392012-03-21 20:38:41 +00006706 SetPixelAlpha(large_image,GetPixelAlpha(image,
6707 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006708 else
cristy16ea1392012-03-21 20:38:41 +00006709 SetPixelAlpha(large_image,GetPixelAlpha(image,
6710 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006711 }
6712 }
glennrp47b9dd52010-11-24 18:12:06 +00006713
cristy3ed852e2009-09-05 21:47:34 +00006714 else /* if (magn_methy == 3 || magn_methy == 5) */
6715 {
6716 /* Replicate nearest */
6717 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006718 {
cristy16ea1392012-03-21 20:38:41 +00006719 SetPixelRed(large_image,GetPixelRed(image,
6720 pixels),q);
6721 SetPixelGreen(large_image,GetPixelGreen(image,
6722 pixels),q);
6723 SetPixelBlue(large_image,GetPixelBlue(image,
6724 pixels),q);
6725 SetPixelAlpha(large_image,GetPixelAlpha(image,
6726 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006727 }
glennrp47b9dd52010-11-24 18:12:06 +00006728
cristy3ed852e2009-09-05 21:47:34 +00006729 else
glennrpbb4f99d2011-05-22 11:13:17 +00006730 {
cristy16ea1392012-03-21 20:38:41 +00006731 SetPixelRed(large_image,GetPixelRed(image,n),q);
6732 SetPixelGreen(large_image,GetPixelGreen(image,n),
6733 q);
6734 SetPixelBlue(large_image,GetPixelBlue(image,n),
6735 q);
6736 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6737 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006738 }
glennrp47b9dd52010-11-24 18:12:06 +00006739
cristy3ed852e2009-09-05 21:47:34 +00006740 if (magn_methy == 5)
6741 {
cristy16ea1392012-03-21 20:38:41 +00006742 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6743 (GetPixelAlpha(image,n)
6744 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006745 +m))/((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006746 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006747 }
6748 }
cristy16ea1392012-03-21 20:38:41 +00006749 n+=GetPixelChannels(image);
6750 q+=GetPixelChannels(large_image);
6751 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006752 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006753
cristy3ed852e2009-09-05 21:47:34 +00006754 if (SyncAuthenticPixels(large_image,exception) == 0)
6755 break;
glennrp47b9dd52010-11-24 18:12:06 +00006756
cristy3ed852e2009-09-05 21:47:34 +00006757 } /* i */
6758 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006759
cristy16ea1392012-03-21 20:38:41 +00006760 prev=(Quantum *) RelinquishMagickMemory(prev);
6761 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006762
6763 length=image->columns;
6764
6765 if (logging != MagickFalse)
6766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6767 " Delete original image");
6768
6769 DeleteImageFromList(&image);
6770
6771 image=large_image;
6772
6773 mng_info->image=image;
6774
6775 /* magnify the columns */
6776 if (logging != MagickFalse)
6777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006778 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006779
cristybb503372010-05-27 20:51:26 +00006780 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006781 {
cristy16ea1392012-03-21 20:38:41 +00006782 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006783 *pixels;
6784
6785 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00006786 pixels=q+(image->columns-length)*GetPixelChannels(image);
6787 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006788
cristybb503372010-05-27 20:51:26 +00006789 for (x=(ssize_t) (image->columns-length);
6790 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006791 {
cristy16ea1392012-03-21 20:38:41 +00006792 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006793
cristybb503372010-05-27 20:51:26 +00006794 if (x == (ssize_t) (image->columns-length))
6795 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006796
cristybb503372010-05-27 20:51:26 +00006797 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6798 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006799
cristybb503372010-05-27 20:51:26 +00006800 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6801 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006802
cristybb503372010-05-27 20:51:26 +00006803 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006804 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006805
cristy3ed852e2009-09-05 21:47:34 +00006806 else
cristybb503372010-05-27 20:51:26 +00006807 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006808
cristy3ed852e2009-09-05 21:47:34 +00006809 for (i=0; i < m; i++)
6810 {
6811 if (magn_methx <= 1)
6812 {
6813 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006814 SetPixelRed(image,GetPixelRed(image,pixels),q);
6815 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6816 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6817 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006818 }
glennrp47b9dd52010-11-24 18:12:06 +00006819
cristy3ed852e2009-09-05 21:47:34 +00006820 else if (magn_methx == 2 || magn_methx == 4)
6821 {
6822 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006823 {
cristy16ea1392012-03-21 20:38:41 +00006824 SetPixelRed(image,GetPixelRed(image,pixels),q);
6825 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6826 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6827 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006828 }
glennrp47b9dd52010-11-24 18:12:06 +00006829
cristy16ea1392012-03-21 20:38:41 +00006830 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006831 else
6832 {
6833 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006834 SetPixelRed(image,(QM) ((2*i*(
6835 GetPixelRed(image,n)
6836 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006837 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006838 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006839
cristy16ea1392012-03-21 20:38:41 +00006840 SetPixelGreen(image,(QM) ((2*i*(
6841 GetPixelGreen(image,n)
6842 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006843 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006844 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006845
cristy16ea1392012-03-21 20:38:41 +00006846 SetPixelBlue(image,(QM) ((2*i*(
6847 GetPixelBlue(image,n)
6848 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006849 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006850 GetPixelBlue(image,pixels)),q);
cristy8a46d822012-08-28 23:32:39 +00006851 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006852 SetPixelAlpha(image,(QM) ((2*i*(
6853 GetPixelAlpha(image,n)
6854 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006855 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006856 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006857 }
glennrp47b9dd52010-11-24 18:12:06 +00006858
cristy3ed852e2009-09-05 21:47:34 +00006859 if (magn_methx == 4)
6860 {
6861 /* Replicate nearest */
6862 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006863 {
cristy16ea1392012-03-21 20:38:41 +00006864 SetPixelAlpha(image,
6865 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006866 }
cristy3ed852e2009-09-05 21:47:34 +00006867 else
glennrpbb4f99d2011-05-22 11:13:17 +00006868 {
cristy16ea1392012-03-21 20:38:41 +00006869 SetPixelAlpha(image,
6870 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006871 }
cristy3ed852e2009-09-05 21:47:34 +00006872 }
6873 }
glennrp47b9dd52010-11-24 18:12:06 +00006874
cristy3ed852e2009-09-05 21:47:34 +00006875 else /* if (magn_methx == 3 || magn_methx == 5) */
6876 {
6877 /* Replicate nearest */
6878 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006879 {
cristy16ea1392012-03-21 20:38:41 +00006880 SetPixelRed(image,GetPixelRed(image,pixels),q);
6881 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6882 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6883 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006884 }
glennrp47b9dd52010-11-24 18:12:06 +00006885
cristy3ed852e2009-09-05 21:47:34 +00006886 else
glennrpbb4f99d2011-05-22 11:13:17 +00006887 {
cristy16ea1392012-03-21 20:38:41 +00006888 SetPixelRed(image,GetPixelRed(image,n),q);
6889 SetPixelGreen(image,GetPixelGreen(image,n),q);
6890 SetPixelBlue(image,GetPixelBlue(image,n),q);
6891 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006892 }
glennrp47b9dd52010-11-24 18:12:06 +00006893
cristy3ed852e2009-09-05 21:47:34 +00006894 if (magn_methx == 5)
6895 {
6896 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006897 SetPixelAlpha(image,
6898 (QM) ((2*i*( GetPixelAlpha(image,n)
6899 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006900 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006901 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006902 }
6903 }
cristy16ea1392012-03-21 20:38:41 +00006904 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006905 }
cristy16ea1392012-03-21 20:38:41 +00006906 n+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006907 }
glennrp47b9dd52010-11-24 18:12:06 +00006908
cristy3ed852e2009-09-05 21:47:34 +00006909 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6910 break;
6911 }
glennrp3faa9a32011-04-23 14:00:25 +00006912#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006913 if (magn_methx != 1 || magn_methy != 1)
6914 {
6915 /*
6916 Rescale pixels to Quantum
6917 */
cristybb503372010-05-27 20:51:26 +00006918 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006919 {
6920 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006921
cristybb503372010-05-27 20:51:26 +00006922 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006923 {
cristy16ea1392012-03-21 20:38:41 +00006924 SetPixelRed(image,ScaleShortToQuantum(
6925 GetPixelRed(image,q)),q);
6926 SetPixelGreen(image,ScaleShortToQuantum(
6927 GetPixelGreen(image,q)),q);
6928 SetPixelBlue(image,ScaleShortToQuantum(
6929 GetPixelBlue(image,q)),q);
6930 SetPixelAlpha(image,ScaleShortToQuantum(
6931 GetPixelAlpha(image,q)),q);
6932 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006933 }
glennrp47b9dd52010-11-24 18:12:06 +00006934
cristy3ed852e2009-09-05 21:47:34 +00006935 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6936 break;
6937 }
6938 }
6939#endif
6940 if (logging != MagickFalse)
6941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6942 " Finished MAGN processing");
6943 }
6944 }
6945
6946 /*
6947 Crop_box is with respect to the upper left corner of the MNG.
6948 */
6949 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6950 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6951 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6952 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6953 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6954 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6955 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6956 if ((crop_box.left != (mng_info->image_box.left
6957 +mng_info->x_off[object_id])) ||
6958 (crop_box.right != (mng_info->image_box.right
6959 +mng_info->x_off[object_id])) ||
6960 (crop_box.top != (mng_info->image_box.top
6961 +mng_info->y_off[object_id])) ||
6962 (crop_box.bottom != (mng_info->image_box.bottom
6963 +mng_info->y_off[object_id])))
6964 {
6965 if (logging != MagickFalse)
6966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6967 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006968
cristy3ed852e2009-09-05 21:47:34 +00006969 if ((crop_box.left < crop_box.right) &&
6970 (crop_box.top < crop_box.bottom))
6971 {
6972 Image
6973 *im;
6974
6975 RectangleInfo
6976 crop_info;
6977
6978 /*
6979 Crop_info is with respect to the upper left corner of
6980 the image.
6981 */
6982 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6983 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006984 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6985 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006986 image->page.width=image->columns;
6987 image->page.height=image->rows;
6988 image->page.x=0;
6989 image->page.y=0;
6990 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006991
cristy3ed852e2009-09-05 21:47:34 +00006992 if (im != (Image *) NULL)
6993 {
6994 image->columns=im->columns;
6995 image->rows=im->rows;
6996 im=DestroyImage(im);
6997 image->page.width=image->columns;
6998 image->page.height=image->rows;
6999 image->page.x=crop_box.left;
7000 image->page.y=crop_box.top;
7001 }
7002 }
glennrp47b9dd52010-11-24 18:12:06 +00007003
cristy3ed852e2009-09-05 21:47:34 +00007004 else
7005 {
7006 /*
7007 No pixels in crop area. The MNG spec still requires
7008 a layer, though, so make a single transparent pixel in
7009 the top left corner.
7010 */
7011 image->columns=1;
7012 image->rows=1;
7013 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00007014 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007015 image->page.width=1;
7016 image->page.height=1;
7017 image->page.x=0;
7018 image->page.y=0;
7019 }
7020 }
7021#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7022 image=mng_info->image;
7023#endif
7024 }
7025
glennrp2b013e42010-11-24 16:55:50 +00007026#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7027 /* PNG does not handle depths greater than 16 so reduce it even
cristy16ea1392012-03-21 20:38:41 +00007028 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00007029 */
7030 if (image->depth > 16)
7031 image->depth=16;
7032#endif
7033
glennrp3faa9a32011-04-23 14:00:25 +00007034#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00007035 if (image->depth > 8)
7036 {
7037 /* To do: fill low byte properly */
7038 image->depth=16;
7039 }
7040
cristy16ea1392012-03-21 20:38:41 +00007041 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007042 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00007043#endif
glennrpd6afd542010-11-19 01:53:05 +00007044
cristy3ed852e2009-09-05 21:47:34 +00007045 if (image_info->number_scenes != 0)
7046 {
7047 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00007048 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00007049 break;
7050 }
glennrpd6afd542010-11-19 01:53:05 +00007051
cristy3ed852e2009-09-05 21:47:34 +00007052 if (logging != MagickFalse)
7053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7054 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00007055
cristy3ed852e2009-09-05 21:47:34 +00007056 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00007057
cristy3ed852e2009-09-05 21:47:34 +00007058 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00007059
cristy3ed852e2009-09-05 21:47:34 +00007060 if (logging != MagickFalse)
7061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7062 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00007063
cristy3ed852e2009-09-05 21:47:34 +00007064#if defined(MNG_INSERT_LAYERS)
7065 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7066 (mng_info->mng_height))
7067 {
7068 /*
7069 Insert a background layer if nothing else was found.
7070 */
7071 if (logging != MagickFalse)
7072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7073 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00007074
cristy16ea1392012-03-21 20:38:41 +00007075 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00007076 {
7077 /*
7078 Allocate next image structure.
7079 */
cristy16ea1392012-03-21 20:38:41 +00007080 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00007081 if (GetNextImageInList(image) == (Image *) NULL)
7082 {
7083 image=DestroyImageList(image);
7084 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00007085
cristy3ed852e2009-09-05 21:47:34 +00007086 if (logging != MagickFalse)
7087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7088 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00007089
cristy3ed852e2009-09-05 21:47:34 +00007090 return((Image *) NULL);
7091 }
7092 image=SyncNextImageInList(image);
7093 }
7094 image->columns=mng_info->mng_width;
7095 image->rows=mng_info->mng_height;
7096 image->page.width=mng_info->mng_width;
7097 image->page.height=mng_info->mng_height;
7098 image->page.x=0;
7099 image->page.y=0;
7100 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00007101 image->alpha_trait=UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00007102
cristy3ed852e2009-09-05 21:47:34 +00007103 if (image_info->ping == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00007104 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007105
cristy3ed852e2009-09-05 21:47:34 +00007106 mng_info->image_found++;
7107 }
7108#endif
7109 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00007110
cristy3ed852e2009-09-05 21:47:34 +00007111 if (mng_iterations == 1)
7112 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00007113
cristy3ed852e2009-09-05 21:47:34 +00007114 while (GetPreviousImageInList(image) != (Image *) NULL)
7115 {
7116 image_count++;
7117 if (image_count > 10*mng_info->image_found)
7118 {
7119 if (logging != MagickFalse)
7120 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00007121
cristy16ea1392012-03-21 20:38:41 +00007122 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007123 CoderError,"Linked list is corrupted, beginning of list not found",
7124 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007125
cristy3ed852e2009-09-05 21:47:34 +00007126 return((Image *) NULL);
7127 }
glennrp0fe50b42010-11-16 03:52:51 +00007128
cristy3ed852e2009-09-05 21:47:34 +00007129 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00007130
cristy3ed852e2009-09-05 21:47:34 +00007131 if (GetNextImageInList(image) == (Image *) NULL)
7132 {
7133 if (logging != MagickFalse)
7134 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00007135
cristy16ea1392012-03-21 20:38:41 +00007136 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007137 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7138 image_info->filename);
7139 }
7140 }
glennrp47b9dd52010-11-24 18:12:06 +00007141
cristy3ed852e2009-09-05 21:47:34 +00007142 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7143 GetNextImageInList(image) ==
7144 (Image *) NULL)
7145 {
7146 if (logging != MagickFalse)
7147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7148 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00007149
cristy16ea1392012-03-21 20:38:41 +00007150 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007151 CoderError,"image->next for first image is NULL but shouldn't be.",
7152 "`%s'",image_info->filename);
7153 }
glennrp47b9dd52010-11-24 18:12:06 +00007154
cristy3ed852e2009-09-05 21:47:34 +00007155 if (mng_info->image_found == 0)
7156 {
7157 if (logging != MagickFalse)
7158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7159 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00007160
cristy16ea1392012-03-21 20:38:41 +00007161 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007162 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007163
cristy3ed852e2009-09-05 21:47:34 +00007164 if (image != (Image *) NULL)
7165 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007166
cristy3ed852e2009-09-05 21:47:34 +00007167 MngInfoFreeStruct(mng_info,&have_mng_structure);
7168 return((Image *) NULL);
7169 }
7170
7171 if (mng_info->ticks_per_second)
7172 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7173 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00007174
cristy3ed852e2009-09-05 21:47:34 +00007175 else
7176 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00007177
cristy3ed852e2009-09-05 21:47:34 +00007178 /* Find final nonzero image delay */
7179 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00007180
cristy3ed852e2009-09-05 21:47:34 +00007181 while (GetNextImageInList(image) != (Image *) NULL)
7182 {
7183 if (image->delay)
7184 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00007185
cristy3ed852e2009-09-05 21:47:34 +00007186 image=GetNextImageInList(image);
7187 }
glennrp0fe50b42010-11-16 03:52:51 +00007188
cristy3ed852e2009-09-05 21:47:34 +00007189 if (final_delay < final_image_delay)
7190 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00007191
cristy3ed852e2009-09-05 21:47:34 +00007192 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00007193
cristy3ed852e2009-09-05 21:47:34 +00007194 if (logging != MagickFalse)
7195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007196 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7197 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00007198
cristy3ed852e2009-09-05 21:47:34 +00007199 if (logging != MagickFalse)
7200 {
7201 int
7202 scene;
7203
7204 scene=0;
7205 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007206
cristy3ed852e2009-09-05 21:47:34 +00007207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7208 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007209
cristy3ed852e2009-09-05 21:47:34 +00007210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007211 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00007212
cristy3ed852e2009-09-05 21:47:34 +00007213 while (GetNextImageInList(image) != (Image *) NULL)
7214 {
7215 image=GetNextImageInList(image);
7216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007217 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00007218 }
7219 }
7220
7221 image=GetFirstImageInList(image);
7222#ifdef MNG_COALESCE_LAYERS
7223 if (insert_layers)
7224 {
7225 Image
7226 *next_image,
7227 *next;
7228
cristybb503372010-05-27 20:51:26 +00007229 size_t
cristy3ed852e2009-09-05 21:47:34 +00007230 scene;
7231
7232 if (logging != MagickFalse)
7233 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00007234
cristy3ed852e2009-09-05 21:47:34 +00007235 scene=image->scene;
cristy16ea1392012-03-21 20:38:41 +00007236 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00007237
cristy3ed852e2009-09-05 21:47:34 +00007238 if (next_image == (Image *) NULL)
7239 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00007240
cristy3ed852e2009-09-05 21:47:34 +00007241 image=DestroyImageList(image);
7242 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00007243
cristy3ed852e2009-09-05 21:47:34 +00007244 for (next=image; next != (Image *) NULL; next=next_image)
7245 {
7246 next->page.width=mng_info->mng_width;
7247 next->page.height=mng_info->mng_height;
7248 next->page.x=0;
7249 next->page.y=0;
7250 next->scene=scene++;
7251 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00007252
cristy3ed852e2009-09-05 21:47:34 +00007253 if (next_image == (Image *) NULL)
7254 break;
glennrp47b9dd52010-11-24 18:12:06 +00007255
cristy3ed852e2009-09-05 21:47:34 +00007256 if (next->delay == 0)
7257 {
7258 scene--;
7259 next_image->previous=GetPreviousImageInList(next);
7260 if (GetPreviousImageInList(next) == (Image *) NULL)
7261 image=next_image;
7262 else
7263 next->previous->next=next_image;
7264 next=DestroyImage(next);
7265 }
7266 }
7267 }
7268#endif
7269
7270 while (GetNextImageInList(image) != (Image *) NULL)
7271 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007272
cristy3ed852e2009-09-05 21:47:34 +00007273 image->dispose=BackgroundDispose;
7274
7275 if (logging != MagickFalse)
7276 {
7277 int
7278 scene;
7279
7280 scene=0;
7281 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007282
cristy3ed852e2009-09-05 21:47:34 +00007283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7284 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007285
cristy3ed852e2009-09-05 21:47:34 +00007286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007287 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7288 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007289
cristy3ed852e2009-09-05 21:47:34 +00007290 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007291 {
7292 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007293
cristyf2faecf2010-05-28 19:19:36 +00007294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007295 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7296 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007297 }
7298 }
glennrp47b9dd52010-11-24 18:12:06 +00007299
cristy3ed852e2009-09-05 21:47:34 +00007300 image=GetFirstImageInList(image);
7301 MngInfoFreeStruct(mng_info,&have_mng_structure);
7302 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007303
cristy3ed852e2009-09-05 21:47:34 +00007304 if (logging != MagickFalse)
7305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007306
cristy3ed852e2009-09-05 21:47:34 +00007307 return(GetFirstImageInList(image));
7308}
glennrp25c1e2b2010-03-25 01:39:56 +00007309#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007310static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7311{
7312 printf("Your PNG library is too old: You have libpng-%s\n",
7313 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007314
cristy3ed852e2009-09-05 21:47:34 +00007315 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7316 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007317
cristy3ed852e2009-09-05 21:47:34 +00007318 return(Image *) NULL;
7319}
glennrp47b9dd52010-11-24 18:12:06 +00007320
cristy3ed852e2009-09-05 21:47:34 +00007321static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7322{
7323 return(ReadPNGImage(image_info,exception));
7324}
glennrp25c1e2b2010-03-25 01:39:56 +00007325#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007326#endif
7327
7328/*
7329%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7330% %
7331% %
7332% %
7333% R e g i s t e r P N G I m a g e %
7334% %
7335% %
7336% %
7337%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7338%
7339% RegisterPNGImage() adds properties for the PNG image format to
7340% the list of supported formats. The properties include the image format
7341% tag, a method to read and/or write the format, whether the format
7342% supports the saving of more than one frame to the same file or blob,
7343% whether the format supports native in-memory I/O, and a brief
7344% description of the format.
7345%
7346% The format of the RegisterPNGImage method is:
7347%
cristybb503372010-05-27 20:51:26 +00007348% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007349%
7350*/
cristybb503372010-05-27 20:51:26 +00007351ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007352{
7353 char
7354 version[MaxTextExtent];
7355
7356 MagickInfo
7357 *entry;
7358
7359 static const char
7360 *PNGNote=
7361 {
7362 "See http://www.libpng.org/ for details about the PNG format."
7363 },
glennrp47b9dd52010-11-24 18:12:06 +00007364
cristy3ed852e2009-09-05 21:47:34 +00007365 *JNGNote=
7366 {
7367 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7368 "format."
7369 },
glennrp47b9dd52010-11-24 18:12:06 +00007370
cristy3ed852e2009-09-05 21:47:34 +00007371 *MNGNote=
7372 {
7373 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7374 "format."
7375 };
7376
7377 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007378
cristy3ed852e2009-09-05 21:47:34 +00007379#if defined(PNG_LIBPNG_VER_STRING)
7380 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7381 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007382
cristy3ed852e2009-09-05 21:47:34 +00007383 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7384 {
7385 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7386 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7387 MaxTextExtent);
7388 }
7389#endif
glennrp47b9dd52010-11-24 18:12:06 +00007390
cristy3ed852e2009-09-05 21:47:34 +00007391 entry=SetMagickInfo("MNG");
7392 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007393
cristy3ed852e2009-09-05 21:47:34 +00007394#if defined(MAGICKCORE_PNG_DELEGATE)
7395 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7396 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7397#endif
glennrp47b9dd52010-11-24 18:12:06 +00007398
cristy3ed852e2009-09-05 21:47:34 +00007399 entry->magick=(IsImageFormatHandler *) IsMNG;
7400 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007401
cristy3ed852e2009-09-05 21:47:34 +00007402 if (*version != '\0')
7403 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007404
glennrpafc97b12013-08-10 00:39:01 +00007405 entry->mime_type=ConstantString("video/x-mng");
cristy3ed852e2009-09-05 21:47:34 +00007406 entry->module=ConstantString("PNG");
7407 entry->note=ConstantString(MNGNote);
7408 (void) RegisterMagickInfo(entry);
7409
7410 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007411
cristy3ed852e2009-09-05 21:47:34 +00007412#if defined(MAGICKCORE_PNG_DELEGATE)
7413 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7414 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7415#endif
glennrp47b9dd52010-11-24 18:12:06 +00007416
cristy3ed852e2009-09-05 21:47:34 +00007417 entry->magick=(IsImageFormatHandler *) IsPNG;
7418 entry->adjoin=MagickFalse;
7419 entry->description=ConstantString("Portable Network Graphics");
cristyd625c522013-08-09 11:45:57 +00007420 entry->mime_type=ConstantString("image/png");
cristy3ed852e2009-09-05 21:47:34 +00007421 entry->module=ConstantString("PNG");
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
cristy3ed852e2009-09-05 21:47:34 +00007426 entry->note=ConstantString(PNGNote);
7427 (void) RegisterMagickInfo(entry);
7428
7429 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007430
cristy3ed852e2009-09-05 21:47:34 +00007431#if defined(MAGICKCORE_PNG_DELEGATE)
7432 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7433 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7434#endif
glennrp47b9dd52010-11-24 18:12:06 +00007435
cristy3ed852e2009-09-05 21:47:34 +00007436 entry->magick=(IsImageFormatHandler *) IsPNG;
7437 entry->adjoin=MagickFalse;
7438 entry->description=ConstantString(
7439 "8-bit indexed with optional binary transparency");
cristyd625c522013-08-09 11:45:57 +00007440 entry->mime_type=ConstantString("image/png");
cristy3ed852e2009-09-05 21:47:34 +00007441 entry->module=ConstantString("PNG");
7442 (void) RegisterMagickInfo(entry);
7443
7444 entry=SetMagickInfo("PNG24");
7445 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007446
cristy3ed852e2009-09-05 21:47:34 +00007447#if defined(ZLIB_VERSION)
7448 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7449 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007450
cristy3ed852e2009-09-05 21:47:34 +00007451 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7452 {
7453 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7454 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7455 }
7456#endif
glennrp47b9dd52010-11-24 18:12:06 +00007457
cristy3ed852e2009-09-05 21:47:34 +00007458 if (*version != '\0')
7459 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007460
cristy3ed852e2009-09-05 21:47:34 +00007461#if defined(MAGICKCORE_PNG_DELEGATE)
7462 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7463 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7464#endif
glennrp47b9dd52010-11-24 18:12:06 +00007465
cristy3ed852e2009-09-05 21:47:34 +00007466 entry->magick=(IsImageFormatHandler *) IsPNG;
7467 entry->adjoin=MagickFalse;
glennrpfd164d22013-01-26 21:10:22 +00007468 entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
cristyd625c522013-08-09 11:45:57 +00007469 entry->mime_type=ConstantString("image/png");
cristy3ed852e2009-09-05 21:47:34 +00007470 entry->module=ConstantString("PNG");
7471 (void) RegisterMagickInfo(entry);
7472
7473 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007474
cristy3ed852e2009-09-05 21:47:34 +00007475#if defined(MAGICKCORE_PNG_DELEGATE)
7476 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7477 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7478#endif
glennrp47b9dd52010-11-24 18:12:06 +00007479
cristy3ed852e2009-09-05 21:47:34 +00007480 entry->magick=(IsImageFormatHandler *) IsPNG;
7481 entry->adjoin=MagickFalse;
7482 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
cristyd625c522013-08-09 11:45:57 +00007483 entry->mime_type=ConstantString("image/png");
cristy3ed852e2009-09-05 21:47:34 +00007484 entry->module=ConstantString("PNG");
7485 (void) RegisterMagickInfo(entry);
7486
glennrpfd164d22013-01-26 21:10:22 +00007487 entry=SetMagickInfo("PNG48");
7488
7489#if defined(MAGICKCORE_PNG_DELEGATE)
7490 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7491 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7492#endif
7493
7494 entry->magick=(IsImageFormatHandler *) IsPNG;
7495 entry->adjoin=MagickFalse;
7496 entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
cristyd625c522013-08-09 11:45:57 +00007497 entry->mime_type=ConstantString("image/png");
glennrpfd164d22013-01-26 21:10:22 +00007498 entry->module=ConstantString("PNG");
7499 (void) RegisterMagickInfo(entry);
7500
7501 entry=SetMagickInfo("PNG64");
7502
7503#if defined(MAGICKCORE_PNG_DELEGATE)
7504 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7505 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7506#endif
7507
7508 entry->magick=(IsImageFormatHandler *) IsPNG;
7509 entry->adjoin=MagickFalse;
7510 entry->description=ConstantString("opaque or transparent 64-bit RGBA");
cristyd625c522013-08-09 11:45:57 +00007511 entry->mime_type=ConstantString("image/png");
glennrpfd164d22013-01-26 21:10:22 +00007512 entry->module=ConstantString("PNG");
7513 (void) RegisterMagickInfo(entry);
7514
glennrp5830fbc2013-01-27 06:11:45 +00007515 entry=SetMagickInfo("PNG00");
7516
7517#if defined(MAGICKCORE_PNG_DELEGATE)
7518 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7519 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7520#endif
7521
7522 entry->magick=(IsImageFormatHandler *) IsPNG;
7523 entry->adjoin=MagickFalse;
glennrp62708572013-02-15 12:51:48 +00007524 entry->description=ConstantString(
7525 "PNG inheriting bit-depth and color-type from original");
cristyd625c522013-08-09 11:45:57 +00007526 entry->mime_type=ConstantString("image/png");
glennrp5830fbc2013-01-27 06:11:45 +00007527 entry->module=ConstantString("PNG");
7528 (void) RegisterMagickInfo(entry);
7529
cristy3ed852e2009-09-05 21:47:34 +00007530 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007531
cristy3ed852e2009-09-05 21:47:34 +00007532#if defined(JNG_SUPPORTED)
7533#if defined(MAGICKCORE_PNG_DELEGATE)
7534 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7535 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7536#endif
7537#endif
glennrp47b9dd52010-11-24 18:12:06 +00007538
cristy3ed852e2009-09-05 21:47:34 +00007539 entry->magick=(IsImageFormatHandler *) IsJNG;
7540 entry->adjoin=MagickFalse;
7541 entry->description=ConstantString("JPEG Network Graphics");
glennrp7fee3292013-08-09 15:25:52 +00007542 entry->mime_type=ConstantString("image/x-jng");
cristy3ed852e2009-09-05 21:47:34 +00007543 entry->module=ConstantString("PNG");
7544 entry->note=ConstantString(JNGNote);
7545 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007546
glennrp868fff32014-03-16 22:09:06 +00007547#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
cristy3d162a92014-02-16 14:05:06 +00007548 ping_semaphore=AcquireSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007549#endif
glennrp47b9dd52010-11-24 18:12:06 +00007550
cristy3ed852e2009-09-05 21:47:34 +00007551 return(MagickImageCoderSignature);
7552}
7553
7554/*
7555%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7556% %
7557% %
7558% %
7559% U n r e g i s t e r P N G I m a g e %
7560% %
7561% %
7562% %
7563%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7564%
7565% UnregisterPNGImage() removes format registrations made by the
7566% PNG module from the list of supported formats.
7567%
7568% The format of the UnregisterPNGImage method is:
7569%
7570% UnregisterPNGImage(void)
7571%
7572*/
7573ModuleExport void UnregisterPNGImage(void)
7574{
7575 (void) UnregisterMagickInfo("MNG");
7576 (void) UnregisterMagickInfo("PNG");
7577 (void) UnregisterMagickInfo("PNG8");
7578 (void) UnregisterMagickInfo("PNG24");
7579 (void) UnregisterMagickInfo("PNG32");
glennrpfd164d22013-01-26 21:10:22 +00007580 (void) UnregisterMagickInfo("PNG48");
7581 (void) UnregisterMagickInfo("PNG64");
glennrp5830fbc2013-01-27 06:11:45 +00007582 (void) UnregisterMagickInfo("PNG00");
cristy3ed852e2009-09-05 21:47:34 +00007583 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007584
glennrp868fff32014-03-16 22:09:06 +00007585#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007586 if (ping_semaphore != (SemaphoreInfo *) NULL)
cristy3d162a92014-02-16 14:05:06 +00007587 RelinquishSemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007588#endif
7589}
7590
7591#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007592#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007593/*
7594%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7595% %
7596% %
7597% %
7598% W r i t e M N G I m a g e %
7599% %
7600% %
7601% %
7602%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7603%
7604% WriteMNGImage() writes an image in the Portable Network Graphics
7605% Group's "Multiple-image Network Graphics" encoded image format.
7606%
7607% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7608%
7609% The format of the WriteMNGImage method is:
7610%
cristy16ea1392012-03-21 20:38:41 +00007611% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7612% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007613%
7614% A description of each parameter follows.
7615%
7616% o image_info: the image info.
7617%
7618% o image: The image.
7619%
cristy16ea1392012-03-21 20:38:41 +00007620% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007621%
7622% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7623% "To do" under ReadPNGImage):
7624%
cristy3ed852e2009-09-05 21:47:34 +00007625% Preserve all unknown and not-yet-handled known chunks found in input
7626% PNG file and copy them into output PNG files according to the PNG
7627% copying rules.
7628%
7629% Write the iCCP chunk at MNG level when (icc profile length > 0)
7630%
7631% Improve selection of color type (use indexed-colour or indexed-colour
7632% with tRNS when 256 or fewer unique RGBA values are present).
7633%
7634% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7635% This will be complicated if we limit ourselves to generating MNG-LC
7636% files. For now we ignore disposal method 3 and simply overlay the next
7637% image on it.
7638%
7639% Check for identical PLTE's or PLTE/tRNS combinations and use a
7640% global MNG PLTE or PLTE/tRNS combination when appropriate.
7641% [mostly done 15 June 1999 but still need to take care of tRNS]
7642%
7643% Check for identical sRGB and replace with a global sRGB (and remove
7644% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7645% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7646% local gAMA/cHRM with local sRGB if appropriate).
7647%
7648% Check for identical sBIT chunks and write global ones.
7649%
7650% Provide option to skip writing the signature tEXt chunks.
7651%
7652% Use signatures to detect identical objects and reuse the first
7653% instance of such objects instead of writing duplicate objects.
7654%
7655% Use a smaller-than-32k value of compression window size when
7656% appropriate.
7657%
7658% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7659% ancillary text chunks and save profiles.
7660%
7661% Provide an option to force LC files (to ensure exact framing rate)
7662% instead of VLC.
7663%
7664% Provide an option to force VLC files instead of LC, even when offsets
7665% are present. This will involve expanding the embedded images with a
7666% transparent region at the top and/or left.
7667*/
7668
cristy3ed852e2009-09-05 21:47:34 +00007669static void
glennrpcf002022011-01-30 02:38:15 +00007670Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007671 png_info *ping_info, unsigned char *profile_type, unsigned char
7672 *profile_description, unsigned char *profile_data, png_uint_32 length)
7673{
cristy3ed852e2009-09-05 21:47:34 +00007674 png_textp
7675 text;
7676
cristybb503372010-05-27 20:51:26 +00007677 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007678 i;
7679
7680 unsigned char
7681 *sp;
7682
7683 png_charp
7684 dp;
7685
7686 png_uint_32
7687 allocated_length,
7688 description_length;
7689
7690 unsigned char
7691 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007692
cristy3ed852e2009-09-05 21:47:34 +00007693 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7694 return;
7695
7696 if (image_info->verbose)
7697 {
glennrp0fe50b42010-11-16 03:52:51 +00007698 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7699 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007700 }
glennrp0fe50b42010-11-16 03:52:51 +00007701
glennrpecab7d72013-05-14 22:50:32 +00007702#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +00007703 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7704#else
7705 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7706#endif
cristy3ed852e2009-09-05 21:47:34 +00007707 description_length=(png_uint_32) strlen((const char *) profile_description);
7708 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7709 + description_length);
glennrpecab7d72013-05-14 22:50:32 +00007710#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +00007711 text[0].text=(png_charp) png_malloc(ping,
7712 (png_alloc_size_t) allocated_length);
7713 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7714#else
7715 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7716 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7717#endif
cristy3ed852e2009-09-05 21:47:34 +00007718 text[0].key[0]='\0';
7719 (void) ConcatenateMagickString(text[0].key,
7720 "Raw profile type ",MaxTextExtent);
7721 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7722 sp=profile_data;
7723 dp=text[0].text;
7724 *dp++='\n';
7725 (void) CopyMagickString(dp,(const char *) profile_description,
7726 allocated_length);
7727 dp+=description_length;
7728 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007729 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007730 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007731 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007732
cristybb503372010-05-27 20:51:26 +00007733 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007734 {
7735 if (i%36 == 0)
7736 *dp++='\n';
7737 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7738 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7739 }
glennrp47b9dd52010-11-24 18:12:06 +00007740
cristy3ed852e2009-09-05 21:47:34 +00007741 *dp++='\n';
7742 *dp='\0';
7743 text[0].text_length=(png_size_t) (dp-text[0].text);
7744 text[0].compression=image_info->compression == NoCompression ||
7745 (image_info->compression == UndefinedCompression &&
7746 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007747
cristy3ed852e2009-09-05 21:47:34 +00007748 if (text[0].text_length <= allocated_length)
7749 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007750
cristy3ed852e2009-09-05 21:47:34 +00007751 png_free(ping,text[0].text);
7752 png_free(ping,text[0].key);
7753 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007754}
7755
glennrpcf002022011-01-30 02:38:15 +00007756static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007757 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007758{
7759 char
7760 *name;
7761
7762 const StringInfo
7763 *profile;
7764
7765 unsigned char
7766 *data;
7767
7768 png_uint_32 length;
7769
7770 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007771
7772 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7773 {
cristy3ed852e2009-09-05 21:47:34 +00007774 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007775
cristy3ed852e2009-09-05 21:47:34 +00007776 if (profile != (const StringInfo *) NULL)
7777 {
7778 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007779 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007780
glennrp47b9dd52010-11-24 18:12:06 +00007781 if (LocaleNCompare(name,string,11) == 0)
7782 {
7783 if (logging != MagickFalse)
7784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7785 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007786
glennrpcf002022011-01-30 02:38:15 +00007787 ping_profile=CloneStringInfo(profile);
7788 data=GetStringInfoDatum(ping_profile),
7789 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007790 data[4]=data[3];
7791 data[3]=data[2];
7792 data[2]=data[1];
7793 data[1]=data[0];
7794 (void) WriteBlobMSBULong(image,length-5); /* data length */
7795 (void) WriteBlob(image,length-1,data+1);
7796 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007797 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007798 }
cristy3ed852e2009-09-05 21:47:34 +00007799 }
glennrp47b9dd52010-11-24 18:12:06 +00007800
cristy3ed852e2009-09-05 21:47:34 +00007801 name=GetNextImageProfile(image);
7802 }
glennrp47b9dd52010-11-24 18:12:06 +00007803
cristy3ed852e2009-09-05 21:47:34 +00007804 return(MagickTrue);
7805}
7806
dirkfd6fd072014-10-24 21:10:08 +00007807#if defined(PNG_tIME_SUPPORTED)
7808static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
7809 const char *date,ExceptionInfo *exception)
7810{
7811 unsigned int
7812 day,
7813 hour,
7814 minute,
7815 month,
7816 second,
7817 year;
7818
7819 png_time
7820 ptime;
7821
7822 time_t
7823 ttime;
7824
7825 if (date != (const char *) NULL)
7826 {
7827 if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
7828 &second) != 6)
7829 {
7830 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7831 "Invalid date format specified for png:tIME","`%s'",
7832 image->filename);
7833 return;
7834 }
7835 ptime.year=(png_uint_16) year;
7836 ptime.month=(png_byte) month;
7837 ptime.day=(png_byte) day;
7838 ptime.hour=(png_byte) hour;
7839 ptime.minute=(png_byte) minute;
7840 ptime.second=(png_byte) second;
7841 }
7842 else
7843 {
7844 time(&ttime);
7845 png_convert_from_time_t(&ptime,ttime);
7846 }
7847 png_set_tIME(ping,info,&ptime);
7848}
7849#endif
glennrpb9cfe272010-12-21 15:08:06 +00007850
cristy3ed852e2009-09-05 21:47:34 +00007851/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007852static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +00007853 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007854{
cristy09973322013-06-30 21:06:30 +00007855 char
7856 im_vers[32],
7857 libpng_runv[32],
7858 libpng_vers[32],
7859 zlib_runv[32],
7860 zlib_vers[32];
7861
cristy16ea1392012-03-21 20:38:41 +00007862 Image
7863 *image;
7864
7865 ImageInfo
7866 *image_info;
7867
cristy3ed852e2009-09-05 21:47:34 +00007868 char
7869 s[2];
7870
7871 const char
7872 *name,
7873 *property,
7874 *value;
7875
7876 const StringInfo
7877 *profile;
7878
cristy3ed852e2009-09-05 21:47:34 +00007879 int
cristy3ed852e2009-09-05 21:47:34 +00007880 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007881 pass;
7882
glennrpe9c26dc2010-05-30 01:56:35 +00007883 png_byte
7884 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007885
glennrp39992b42010-11-14 00:03:43 +00007886 png_color
7887 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007888
glennrp5af765f2010-03-30 11:12:18 +00007889 png_color_16
7890 ping_background,
7891 ping_trans_color;
7892
cristy3ed852e2009-09-05 21:47:34 +00007893 png_info
7894 *ping_info;
7895
7896 png_struct
7897 *ping;
7898
glennrp5af765f2010-03-30 11:12:18 +00007899 png_uint_32
7900 ping_height,
7901 ping_width;
7902
cristybb503372010-05-27 20:51:26 +00007903 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007904 y;
7905
7906 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007907 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007908 logging,
glennrp58e01762011-01-07 15:28:54 +00007909 matte,
7910
glennrpda8f3a72011-02-27 23:54:12 +00007911 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007912 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007913 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007914 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007915 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007916 ping_have_bKGD,
glennrp918b9dc2013-04-03 13:41:41 +00007917 ping_have_iCCP,
glennrp991d11d2010-11-12 21:55:28 +00007918 ping_have_pHYs,
glennrp918b9dc2013-04-03 13:41:41 +00007919 ping_have_sRGB,
glennrp991d11d2010-11-12 21:55:28 +00007920 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007921
7922 ping_exclude_bKGD,
7923 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007924 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007925 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007926 ping_exclude_gAMA,
7927 ping_exclude_iCCP,
7928 /* ping_exclude_iTXt, */
7929 ping_exclude_oFFs,
7930 ping_exclude_pHYs,
7931 ping_exclude_sRGB,
7932 ping_exclude_tEXt,
dirkfd6fd072014-10-24 21:10:08 +00007933 ping_exclude_tIME,
glennrpe4e2d792011-02-21 12:11:27 +00007934 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007935 ping_exclude_vpAg,
7936 ping_exclude_zCCP, /* hex-encoded iCCP */
7937 ping_exclude_zTXt,
7938
glennrp8d3d6e52011-04-19 04:39:51 +00007939 ping_preserve_colormap,
glennrpecab7d72013-05-14 22:50:32 +00007940 ping_preserve_iCCP,
glennrp0e8ea192010-12-24 18:00:33 +00007941 ping_need_colortype_warning,
7942
glennrp82b3c532011-03-22 19:20:54 +00007943 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007944 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007945 tried_333,
7946 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007947
cristy09973322013-06-30 21:06:30 +00007948 MemoryInfo
cristyaf1534a2013-06-30 21:12:25 +00007949 *volatile pixel_info;
cristy09973322013-06-30 21:06:30 +00007950
cristy3ed852e2009-09-05 21:47:34 +00007951 QuantumInfo
7952 *quantum_info;
7953
cristy16ea1392012-03-21 20:38:41 +00007954 PNGErrorInfo
7955 error_info;
7956
cristybb503372010-05-27 20:51:26 +00007957 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007958 i,
7959 x;
7960
7961 unsigned char
cristy09973322013-06-30 21:06:30 +00007962 *ping_pixels;
glennrpd0cae252013-03-15 22:30:41 +00007963
glennrp5af765f2010-03-30 11:12:18 +00007964 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007965 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007966 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007967 ping_color_type,
7968 ping_interlace_method,
7969 ping_compression_method,
7970 ping_filter_method,
7971 ping_num_trans;
7972
cristybb503372010-05-27 20:51:26 +00007973 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007974 image_depth,
7975 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007976
cristybb503372010-05-27 20:51:26 +00007977 size_t
cristy3ed852e2009-09-05 21:47:34 +00007978 quality,
7979 rowbytes,
7980 save_image_depth;
7981
glennrpdfd70802010-11-14 01:23:35 +00007982 int
glennrpfd05d622011-02-25 04:10:33 +00007983 j,
glennrpf09bded2011-01-08 01:15:59 +00007984 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007985 number_opaque,
7986 number_semitransparent,
7987 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007988 ping_pHYs_unit_type;
7989
7990 png_uint_32
7991 ping_pHYs_x_resolution,
7992 ping_pHYs_y_resolution;
7993
cristy3ed852e2009-09-05 21:47:34 +00007994 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007995 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007996
cristy16ea1392012-03-21 20:38:41 +00007997 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7998 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7999 if (image_info == (ImageInfo *) NULL)
8000 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00008001
glennrpd0cae252013-03-15 22:30:41 +00008002 /* Define these outside of the following "if logging()" block so they will
8003 * show in debuggers.
8004 */
8005 *im_vers='\0';
8006 (void) ConcatenateMagickString(im_vers,
8007 MagickLibVersionText,MaxTextExtent);
8008 (void) ConcatenateMagickString(im_vers,
8009 MagickLibAddendum,MaxTextExtent);
glennrpec0ddbc2013-03-16 12:40:12 +00008010
glennrpd0cae252013-03-15 22:30:41 +00008011 *libpng_vers='\0';
8012 (void) ConcatenateMagickString(libpng_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00008013 PNG_LIBPNG_VER_STRING,32);
8014 *libpng_runv='\0';
8015 (void) ConcatenateMagickString(libpng_runv,
8016 png_get_libpng_ver(NULL),32);
8017
glennrpd0cae252013-03-15 22:30:41 +00008018 *zlib_vers='\0';
8019 (void) ConcatenateMagickString(zlib_vers,
glennrpec0ddbc2013-03-16 12:40:12 +00008020 ZLIB_VERSION,32);
8021 *zlib_runv='\0';
8022 (void) ConcatenateMagickString(zlib_runv,
8023 zlib_version,32);
8024
glennrpd0cae252013-03-15 22:30:41 +00008025 if (logging)
8026 {
8027 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8028 im_vers);
8029 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8030 libpng_vers);
glennrpec0ddbc2013-03-16 12:40:12 +00008031 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8032 {
8033 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8034 libpng_runv);
8035 }
glennrpd0cae252013-03-15 22:30:41 +00008036 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8037 zlib_vers);
glennrpec0ddbc2013-03-16 12:40:12 +00008038 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8039 {
8040 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8041 zlib_runv);
8042 }
glennrpd0cae252013-03-15 22:30:41 +00008043 }
8044
glennrp5af765f2010-03-30 11:12:18 +00008045 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00008046 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00008047 ping_color_type=0,
8048 ping_interlace_method=0,
8049 ping_compression_method=0,
8050 ping_filter_method=0,
8051 ping_num_trans = 0;
8052
8053 ping_background.red = 0;
8054 ping_background.green = 0;
8055 ping_background.blue = 0;
8056 ping_background.gray = 0;
8057 ping_background.index = 0;
8058
8059 ping_trans_color.red=0;
8060 ping_trans_color.green=0;
8061 ping_trans_color.blue=0;
8062 ping_trans_color.gray=0;
8063
glennrpdfd70802010-11-14 01:23:35 +00008064 ping_pHYs_unit_type = 0;
8065 ping_pHYs_x_resolution = 0;
8066 ping_pHYs_y_resolution = 0;
8067
glennrpda8f3a72011-02-27 23:54:12 +00008068 ping_have_blob=MagickFalse;
glennrpf70c4d22013-03-19 15:26:48 +00008069 ping_have_cheap_transparency=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00008070 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00008071 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00008072 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00008073 ping_have_bKGD=MagickFalse;
glennrp918b9dc2013-04-03 13:41:41 +00008074 ping_have_iCCP=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00008075 ping_have_pHYs=MagickFalse;
glennrp918b9dc2013-04-03 13:41:41 +00008076 ping_have_sRGB=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00008077 ping_have_tRNS=MagickFalse;
8078
glennrp0e8ea192010-12-24 18:00:33 +00008079 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8080 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00008081 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00008082 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00008083 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00008084 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8085 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8086 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8087 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8088 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8089 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
dirkfd6fd072014-10-24 21:10:08 +00008090 ping_exclude_tIME=mng_info->ping_exclude_tIME;
glennrpdde35db2011-02-21 12:06:32 +00008091 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00008092 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8093 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8094 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8095
glennrp8d3d6e52011-04-19 04:39:51 +00008096 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrpecab7d72013-05-14 22:50:32 +00008097 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
glennrp0e8ea192010-12-24 18:00:33 +00008098 ping_need_colortype_warning = MagickFalse;
8099
cristy0d57eec2011-09-04 22:13:56 +00008100 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8101 * i.e., eliminate the ICC profile and set image->rendering_intent.
8102 * Note that this will not involve any changes to the actual pixels
8103 * but merely passes information to applications that read the resulting
8104 * PNG image.
glennrpecab7d72013-05-14 22:50:32 +00008105 *
8106 * To do: recognize other variants of the sRGB profile, using the CRC to
8107 * verify all recognized variants including the 7 already known.
8108 *
8109 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8110 *
8111 * Use something other than image->rendering_intent to record the fact
8112 * that the sRGB profile was found.
8113 *
8114 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8115 * profile. Record the Blackpoint Compensation, if any.
cristy0d57eec2011-09-04 22:13:56 +00008116 */
glennrpecab7d72013-05-14 22:50:32 +00008117 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
cristy0d57eec2011-09-04 22:13:56 +00008118 {
8119 char
8120 *name;
8121
8122 const StringInfo
8123 *profile;
8124
8125 ResetImageProfileIterator(image);
8126 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8127 {
8128 profile=GetImageProfile(image,name);
8129
8130 if (profile != (StringInfo *) NULL)
8131 {
8132 if ((LocaleCompare(name,"ICC") == 0) ||
glennrpecab7d72013-05-14 22:50:32 +00008133 (LocaleCompare(name,"ICM") == 0))
glennrpee7b4c02011-10-04 01:21:09 +00008134
glennrpecab7d72013-05-14 22:50:32 +00008135 {
8136 int
8137 icheck,
8138 got_crc=0;
8139
glennrpee7b4c02011-10-04 01:21:09 +00008140
8141 png_uint_32
8142 length,
glennrpecab7d72013-05-14 22:50:32 +00008143 profile_crc=0;
glennrpee7b4c02011-10-04 01:21:09 +00008144
cristy0d57eec2011-09-04 22:13:56 +00008145 unsigned char
8146 *data;
8147
glennrp29a106e2011-09-06 17:11:42 +00008148 length=(png_uint_32) GetStringInfoLength(profile);
8149
glennrpecab7d72013-05-14 22:50:32 +00008150 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
cristy0d57eec2011-09-04 22:13:56 +00008151 {
glennrpecab7d72013-05-14 22:50:32 +00008152 if (length == sRGB_info[icheck].len)
glennrp29a106e2011-09-06 17:11:42 +00008153 {
glennrpecab7d72013-05-14 22:50:32 +00008154 if (got_crc == 0)
8155 {
8156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00008157 " Got a %lu-byte ICC profile (potentially sRGB)",
8158 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00008159
glennrpecab7d72013-05-14 22:50:32 +00008160 data=GetStringInfoDatum(profile);
8161 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00008162
glennrpecab7d72013-05-14 22:50:32 +00008163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8164 " with crc=%8x",(unsigned int) profile_crc);
8165 got_crc++;
8166 }
glennrpee7b4c02011-10-04 01:21:09 +00008167
glennrpecab7d72013-05-14 22:50:32 +00008168 if (profile_crc == sRGB_info[icheck].crc)
glennrpee7b4c02011-10-04 01:21:09 +00008169 {
8170 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpecab7d72013-05-14 22:50:32 +00008171 " It is sRGB with rendering intent = %s",
8172 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8173 sRGB_info[icheck].intent));
glennrpee7b4c02011-10-04 01:21:09 +00008174 if (image->rendering_intent==UndefinedIntent)
glennrpecab7d72013-05-14 22:50:32 +00008175 {
8176 image->rendering_intent=
8177 Magick_RenderingIntent_from_PNG_RenderingIntent(
8178 sRGB_info[icheck].intent);
8179 }
8180 ping_exclude_iCCP = MagickTrue;
8181 ping_exclude_zCCP = MagickTrue;
8182 ping_have_sRGB = MagickTrue;
glennrpee7b4c02011-10-04 01:21:09 +00008183 break;
8184 }
glennrp29a106e2011-09-06 17:11:42 +00008185 }
glennrp29a106e2011-09-06 17:11:42 +00008186 }
glennrpecab7d72013-05-14 22:50:32 +00008187 if (sRGB_info[icheck].len == 0)
glennrp29a106e2011-09-06 17:11:42 +00008188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpecab7d72013-05-14 22:50:32 +00008189 " Got a %lu-byte ICC profile not recognized as sRGB",
glennrp29a106e2011-09-06 17:11:42 +00008190 (unsigned long) length);
8191 }
cristy0d57eec2011-09-04 22:13:56 +00008192 }
8193 name=GetNextImageProfile(image);
8194 }
8195 }
8196
glennrp8bb3a022010-12-13 20:40:04 +00008197 number_opaque = 0;
8198 number_semitransparent = 0;
8199 number_transparent = 0;
8200
glennrpfd05d622011-02-25 04:10:33 +00008201 if (logging != MagickFalse)
8202 {
8203 if (image->storage_class == UndefinedClass)
8204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8205 " storage_class=UndefinedClass");
8206 if (image->storage_class == DirectClass)
8207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8208 " storage_class=DirectClass");
8209 if (image->storage_class == PseudoClass)
8210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8211 " storage_class=PseudoClass");
8212 }
glennrp28af3712011-04-06 18:07:30 +00008213
glennrp750105b2012-04-25 16:20:45 +00008214 if (image->storage_class == PseudoClass &&
glennrp7e65e932011-08-19 02:31:16 +00008215 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
glennrpfd164d22013-01-26 21:10:22 +00008216 mng_info->write_png48 || mng_info->write_png64 ||
8217 (mng_info->write_png_colortype != 1 &&
8218 mng_info->write_png_colortype != 5)))
glennrp7e65e932011-08-19 02:31:16 +00008219 {
cristy16ea1392012-03-21 20:38:41 +00008220 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00008221 image->storage_class = DirectClass;
8222 }
8223
glennrpc6c391a2011-04-27 02:23:56 +00008224 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00008225 {
glennrpc6c391a2011-04-27 02:23:56 +00008226 if (image->storage_class != PseudoClass && image->colormap != NULL)
8227 {
8228 /* Free the bogus colormap; it can cause trouble later */
8229 if (logging != MagickFalse)
8230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8231 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00008232 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00008233 image->colormap=NULL;
8234 }
glennrp28af3712011-04-06 18:07:30 +00008235 }
glennrpbb4f99d2011-05-22 11:13:17 +00008236
glennrpc28acd62013-12-05 20:21:45 +00008237 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00008238 (void) TransformImageColorspace(image,sRGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00008239
glennrp3241bd02010-12-12 04:36:28 +00008240 /*
8241 Sometimes we get PseudoClass images whose RGB values don't match
8242 the colors in the colormap. This code syncs the RGB values.
8243 */
8244 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristy16ea1392012-03-21 20:38:41 +00008245 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00008246
glennrpa6a06632011-01-19 15:15:34 +00008247#if (MAGICKCORE_QUANTUM_DEPTH == 8)
8248 if (image->depth > 8)
8249 {
8250 if (logging != MagickFalse)
8251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8252 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8253
8254 image->depth=8;
8255 }
8256#endif
8257
glennrp8e58efd2011-05-20 12:16:29 +00008258 /* Respect the -depth option */
dirkcd979952014-10-24 20:46:54 +00008259 if (image->depth < 4)
glennrpcc95c3f2011-04-18 16:46:48 +00008260 {
cristy16ea1392012-03-21 20:38:41 +00008261 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00008262 *r;
8263
dirkaac49632014-10-24 20:29:33 +00008264 if (image->depth > 2)
glennrp8e58efd2011-05-20 12:16:29 +00008265 {
8266 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00008267 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008268
8269 for (y=0; y < (ssize_t) image->rows; y++)
8270 {
cristy16ea1392012-03-21 20:38:41 +00008271 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008272
cristy16ea1392012-03-21 20:38:41 +00008273 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008274 break;
8275
8276 for (x=0; x < (ssize_t) image->columns; x++)
8277 {
cristy16ea1392012-03-21 20:38:41 +00008278 LBR04PixelRGBA(r);
8279 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008280 }
glennrpbb4f99d2011-05-22 11:13:17 +00008281
glennrp8e58efd2011-05-20 12:16:29 +00008282 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8283 break;
8284 }
8285
8286 if (image->storage_class == PseudoClass && image->colormap != NULL)
8287 {
cristy3e08f112011-05-24 13:19:30 +00008288 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008289 {
glennrp91d99252011-06-25 14:30:13 +00008290 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008291 }
8292 }
8293 }
glennrp8e58efd2011-05-20 12:16:29 +00008294 else if (image->depth > 1)
8295 {
8296 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00008297 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008298
8299 for (y=0; y < (ssize_t) image->rows; y++)
8300 {
cristy16ea1392012-03-21 20:38:41 +00008301 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008302
cristy16ea1392012-03-21 20:38:41 +00008303 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008304 break;
8305
8306 for (x=0; x < (ssize_t) image->columns; x++)
8307 {
cristy16ea1392012-03-21 20:38:41 +00008308 LBR02PixelRGBA(r);
8309 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008310 }
glennrpbb4f99d2011-05-22 11:13:17 +00008311
glennrp8e58efd2011-05-20 12:16:29 +00008312 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8313 break;
8314 }
8315
8316 if (image->storage_class == PseudoClass && image->colormap != NULL)
8317 {
cristy3e08f112011-05-24 13:19:30 +00008318 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008319 {
glennrp91d99252011-06-25 14:30:13 +00008320 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008321 }
8322 }
8323 }
8324 else
8325 {
8326 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00008327 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008328
8329 for (y=0; y < (ssize_t) image->rows; y++)
8330 {
cristy16ea1392012-03-21 20:38:41 +00008331 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008332
cristy16ea1392012-03-21 20:38:41 +00008333 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008334 break;
8335
8336 for (x=0; x < (ssize_t) image->columns; x++)
8337 {
cristy16ea1392012-03-21 20:38:41 +00008338 LBR01PixelRGBA(r);
8339 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008340 }
glennrpbb4f99d2011-05-22 11:13:17 +00008341
glennrp8e58efd2011-05-20 12:16:29 +00008342 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8343 break;
8344 }
8345
8346 if (image->storage_class == PseudoClass && image->colormap != NULL)
8347 {
cristy3e08f112011-05-24 13:19:30 +00008348 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008349 {
glennrp91d99252011-06-25 14:30:13 +00008350 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008351 }
8352 }
8353 }
glennrp9d0ea4d2011-04-22 18:35:57 +00008354 }
8355
glennrp67b9c1a2011-04-22 18:47:36 +00008356 /* To do: set to next higher multiple of 8 */
8357 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00008358 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00008359
glennrp2b013e42010-11-24 16:55:50 +00008360#if (MAGICKCORE_QUANTUM_DEPTH > 16)
8361 /* PNG does not handle depths greater than 16 so reduce it even
8362 * if lossy
8363 */
glennrp8e58efd2011-05-20 12:16:29 +00008364 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00008365 image->depth=16;
8366#endif
8367
glennrp3faa9a32011-04-23 14:00:25 +00008368#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00008369 if (image->depth > 8)
8370 {
8371 /* To do: fill low byte properly */
8372 image->depth=16;
8373 }
8374
glennrpc722dd82011-02-24 05:13:21 +00008375 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristy16ea1392012-03-21 20:38:41 +00008376 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00008377 image->depth = 8;
8378#endif
8379
glennrpd0ee5a22014-03-28 14:28:47 +00008380 image_colors = (int) image->colors;
8381 number_opaque = (int) image->colors;
8382 number_transparent = 0;
8383 number_semitransparent = 0;
8384
glennrp197c8e62014-04-22 23:45:20 +00008385 if (mng_info->write_png_colortype &&
glennrpa8036d62012-11-04 01:46:06 +00008386 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8387 mng_info->write_png_colortype < 4 &&
8388 image->alpha_trait != BlendPixelTrait)))
8389 {
8390 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8391 * are not going to need the result.
8392 */
glennrpa8036d62012-11-04 01:46:06 +00008393 if (mng_info->write_png_colortype == 1 ||
8394 mng_info->write_png_colortype == 5)
8395 ping_have_color=MagickFalse;
glennrpa8036d62012-11-04 01:46:06 +00008396
8397 if (image->alpha_trait == BlendPixelTrait)
8398 {
8399 number_transparent = 2;
8400 number_semitransparent = 1;
8401 }
glennrpa8036d62012-11-04 01:46:06 +00008402 }
8403
glennrp197c8e62014-04-22 23:45:20 +00008404 if (mng_info->write_png_colortype < 7)
glennrpa8036d62012-11-04 01:46:06 +00008405 {
8406 /* BUILD_PALETTE
8407 *
8408 * Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00008409 * we reduce the transparency to binary and run again, then if there
8410 * 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 +00008411 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8412 * palette. Then (To do) we take care of a final reduction that is only
8413 * needed if there are still 256 colors present and one of them has both
8414 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00008415 */
glennrp82b3c532011-03-22 19:20:54 +00008416
glennrp8ca51ad2011-05-12 21:22:32 +00008417 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008418 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00008419 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008420
glennrp8ca51ad2011-05-12 21:22:32 +00008421 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00008422 {
glennrpa8036d62012-11-04 01:46:06 +00008423 /*
glennrpd71e86a2011-02-24 01:28:37 +00008424 * Sometimes we get DirectClass images that have 256 colors or fewer.
8425 * This code will build a colormap.
8426 *
8427 * Also, sometimes we get PseudoClass images with an out-of-date
8428 * colormap. This code will replace the colormap with a new one.
8429 * Sometimes we get PseudoClass images that have more than 256 colors.
8430 * This code will delete the colormap and change the image to
8431 * DirectClass.
8432 *
cristy8a46d822012-08-28 23:32:39 +00008433 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008434 * even though it sometimes contains left-over non-opaque values.
8435 *
8436 * Also we gather some information (number of opaque, transparent,
8437 * and semitransparent pixels, and whether the image has any non-gray
8438 * pixels or only black-and-white pixels) that we might need later.
8439 *
8440 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8441 * we need to check for bogus non-opaque values, at least.
8442 */
glennrp3c218112010-11-27 15:31:26 +00008443
glennrpd71e86a2011-02-24 01:28:37 +00008444 int
8445 n;
glennrp3c218112010-11-27 15:31:26 +00008446
cristy16ea1392012-03-21 20:38:41 +00008447 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008448 opaque[260],
8449 semitransparent[260],
8450 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008451
cristy16ea1392012-03-21 20:38:41 +00008452 register const Quantum
8453 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008454
cristy16ea1392012-03-21 20:38:41 +00008455 register Quantum
8456 *q,
glennrpfd05d622011-02-25 04:10:33 +00008457 *r;
8458
glennrpd71e86a2011-02-24 01:28:37 +00008459 if (logging != MagickFalse)
8460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8461 " Enter BUILD_PALETTE:");
8462
8463 if (logging != MagickFalse)
8464 {
glennrp03812ae2010-12-24 01:31:34 +00008465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008466 " image->columns=%.20g",(double) image->columns);
8467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8468 " image->rows=%.20g",(double) image->rows);
8469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00008470 " image->alpha_trait=%.20g",(double) image->alpha_trait);
glennrpd71e86a2011-02-24 01:28:37 +00008471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8472 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008473
glennrpfd05d622011-02-25 04:10:33 +00008474 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008475 {
8476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008477 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008479 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008480
glennrpd71e86a2011-02-24 01:28:37 +00008481 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008482 {
glennrpd71e86a2011-02-24 01:28:37 +00008483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8484 " %d (%d,%d,%d,%d)",
8485 (int) i,
8486 (int) image->colormap[i].red,
8487 (int) image->colormap[i].green,
8488 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008489 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008490 }
glennrp2cc891a2010-12-24 13:44:32 +00008491
glennrpd71e86a2011-02-24 01:28:37 +00008492 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8493 {
8494 if (i > 255)
8495 {
8496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8497 " %d (%d,%d,%d,%d)",
8498 (int) i,
8499 (int) image->colormap[i].red,
8500 (int) image->colormap[i].green,
8501 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008502 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008503 }
8504 }
glennrp03812ae2010-12-24 01:31:34 +00008505 }
glennrp7ddcc222010-12-11 05:01:05 +00008506
glennrpd71e86a2011-02-24 01:28:37 +00008507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8508 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008509
glennrpd71e86a2011-02-24 01:28:37 +00008510 if (image->colors == 0)
cristy16ea1392012-03-21 20:38:41 +00008511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8512 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008513
glennrp8d3d6e52011-04-19 04:39:51 +00008514 if (ping_preserve_colormap == MagickFalse)
8515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8516 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008517 }
8518
glennrpd71e86a2011-02-24 01:28:37 +00008519 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008520 number_opaque = 0;
8521 number_semitransparent = 0;
8522 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008523
8524 for (y=0; y < (ssize_t) image->rows; y++)
8525 {
8526 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8527
cristy16ea1392012-03-21 20:38:41 +00008528 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008529 break;
8530
8531 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008532 {
cristy8a46d822012-08-28 23:32:39 +00008533 if (image->alpha_trait != BlendPixelTrait ||
cristy16ea1392012-03-21 20:38:41 +00008534 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008535 {
8536 if (number_opaque < 259)
8537 {
8538 if (number_opaque == 0)
8539 {
cristy16ea1392012-03-21 20:38:41 +00008540 GetPixelInfoPixel(image, q, opaque);
8541 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008542 number_opaque=1;
8543 }
glennrp2cc891a2010-12-24 13:44:32 +00008544
glennrpd71e86a2011-02-24 01:28:37 +00008545 for (i=0; i< (ssize_t) number_opaque; i++)
8546 {
cristy16ea1392012-03-21 20:38:41 +00008547 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008548 break;
8549 }
glennrp7ddcc222010-12-11 05:01:05 +00008550
cristy16ea1392012-03-21 20:38:41 +00008551 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008552 {
8553 number_opaque++;
cristy16ea1392012-03-21 20:38:41 +00008554 GetPixelInfoPixel(image, q, opaque+i);
8555 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008556 }
8557 }
8558 }
cristy16ea1392012-03-21 20:38:41 +00008559 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008560 {
8561 if (number_transparent < 259)
8562 {
8563 if (number_transparent == 0)
8564 {
cristy16ea1392012-03-21 20:38:41 +00008565 GetPixelInfoPixel(image, q, transparent);
8566 ping_trans_color.red=(unsigned short)
8567 GetPixelRed(image,q);
8568 ping_trans_color.green=(unsigned short)
8569 GetPixelGreen(image,q);
8570 ping_trans_color.blue=(unsigned short)
8571 GetPixelBlue(image,q);
8572 ping_trans_color.gray=(unsigned short)
cristy972d1c42012-07-14 23:29:14 +00008573 GetPixelGray(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008574 number_transparent = 1;
8575 }
8576
8577 for (i=0; i< (ssize_t) number_transparent; i++)
8578 {
cristy16ea1392012-03-21 20:38:41 +00008579 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008580 break;
8581 }
8582
8583 if (i == (ssize_t) number_transparent &&
8584 number_transparent < 259)
8585 {
8586 number_transparent++;
cristy16ea1392012-03-21 20:38:41 +00008587 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008588 }
8589 }
8590 }
8591 else
8592 {
8593 if (number_semitransparent < 259)
8594 {
8595 if (number_semitransparent == 0)
8596 {
cristy16ea1392012-03-21 20:38:41 +00008597 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008598 number_semitransparent = 1;
8599 }
8600
8601 for (i=0; i< (ssize_t) number_semitransparent; i++)
8602 {
cristy16ea1392012-03-21 20:38:41 +00008603 if (IsPixelEquivalent(image,q, semitransparent+i)
8604 && GetPixelAlpha(image,q) ==
8605 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008606 break;
8607 }
8608
8609 if (i == (ssize_t) number_semitransparent &&
8610 number_semitransparent < 259)
8611 {
8612 number_semitransparent++;
cristy16ea1392012-03-21 20:38:41 +00008613 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008614 }
8615 }
8616 }
cristy16ea1392012-03-21 20:38:41 +00008617 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008618 }
8619 }
8620
cristy4054bfb2011-08-29 23:41:39 +00008621 if (mng_info->write_png8 == MagickFalse &&
8622 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008623 {
8624 /* Add the background color to the palette, if it
8625 * isn't already there.
8626 */
glennrpc6c391a2011-04-27 02:23:56 +00008627 if (logging != MagickFalse)
8628 {
8629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8630 " Check colormap for background (%d,%d,%d)",
8631 (int) image->background_color.red,
8632 (int) image->background_color.green,
8633 (int) image->background_color.blue);
8634 }
glennrpd71e86a2011-02-24 01:28:37 +00008635 for (i=0; i<number_opaque; i++)
8636 {
glennrpca7ad3a2011-04-26 04:44:54 +00008637 if (opaque[i].red == image->background_color.red &&
8638 opaque[i].green == image->background_color.green &&
8639 opaque[i].blue == image->background_color.blue)
8640 break;
glennrpd71e86a2011-02-24 01:28:37 +00008641 }
glennrpd71e86a2011-02-24 01:28:37 +00008642 if (number_opaque < 259 && i == number_opaque)
8643 {
glennrp8e045c82011-04-27 16:40:27 +00008644 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008645 ping_background.index = i;
glennrp388a8c82012-06-27 13:41:51 +00008646 number_opaque++;
glennrpc6c391a2011-04-27 02:23:56 +00008647 if (logging != MagickFalse)
8648 {
8649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8650 " background_color index is %d",(int) i);
8651 }
8652
glennrpd71e86a2011-02-24 01:28:37 +00008653 }
glennrpa080bc32011-03-11 18:03:44 +00008654 else if (logging != MagickFalse)
8655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8656 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008657 }
8658
8659 image_colors=number_opaque+number_transparent+number_semitransparent;
8660
8661 if (logging != MagickFalse)
8662 {
8663 if (image_colors > 256)
8664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8665 " image has more than 256 colors");
8666
8667 else
8668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8669 " image has %d colors",image_colors);
8670 }
8671
glennrp8d3d6e52011-04-19 04:39:51 +00008672 if (ping_preserve_colormap != MagickFalse)
8673 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008674
glennrpfd05d622011-02-25 04:10:33 +00008675 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008676 {
8677 ping_have_color=MagickFalse;
8678 ping_have_non_bw=MagickFalse;
8679
glennrp45d4c342013-12-05 21:24:54 +00008680 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
glennrp0fa25802012-07-20 14:01:06 +00008681 {
glennrp96bc6202013-12-03 19:21:04 +00008682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8683 "incompatible colorspace");
glennrp0fa25802012-07-20 14:01:06 +00008684 ping_have_color=MagickTrue;
glennrp98b95772012-11-29 01:32:00 +00008685 ping_have_non_bw=MagickTrue;
glennrp0fa25802012-07-20 14:01:06 +00008686 }
8687
glennrpd71e86a2011-02-24 01:28:37 +00008688 if(image_colors > 256)
8689 {
8690 for (y=0; y < (ssize_t) image->rows; y++)
8691 {
8692 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8693
cristy16ea1392012-03-21 20:38:41 +00008694 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008695 break;
8696
glennrpe5e6b802011-07-20 14:44:40 +00008697 s=q;
8698 for (x=0; x < (ssize_t) image->columns; x++)
8699 {
cristy16ea1392012-03-21 20:38:41 +00008700 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8701 GetPixelRed(image,s) != GetPixelBlue(image,s))
glennrpe5e6b802011-07-20 14:44:40 +00008702 {
8703 ping_have_color=MagickTrue;
8704 ping_have_non_bw=MagickTrue;
8705 break;
8706 }
cristy16ea1392012-03-21 20:38:41 +00008707 s+=GetPixelChannels(image);
glennrpe5e6b802011-07-20 14:44:40 +00008708 }
8709
8710 if (ping_have_color != MagickFalse)
8711 break;
8712
glennrpd71e86a2011-02-24 01:28:37 +00008713 /* Worst case is black-and-white; we are looking at every
8714 * pixel twice.
8715 */
8716
glennrpd71e86a2011-02-24 01:28:37 +00008717 if (ping_have_non_bw == MagickFalse)
8718 {
8719 s=q;
8720 for (x=0; x < (ssize_t) image->columns; x++)
8721 {
cristy16ea1392012-03-21 20:38:41 +00008722 if (GetPixelRed(image,s) != 0 &&
8723 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008724 {
8725 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008726 break;
glennrpd71e86a2011-02-24 01:28:37 +00008727 }
cristy16ea1392012-03-21 20:38:41 +00008728 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008729 }
glennrpe5e6b802011-07-20 14:44:40 +00008730 }
glennrpd71e86a2011-02-24 01:28:37 +00008731 }
glennrpbb4f99d2011-05-22 11:13:17 +00008732 }
8733 }
glennrpd71e86a2011-02-24 01:28:37 +00008734
8735 if (image_colors < 257)
8736 {
cristy16ea1392012-03-21 20:38:41 +00008737 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008738 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008739
glennrpd71e86a2011-02-24 01:28:37 +00008740 /*
8741 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008742 */
8743
glennrpd71e86a2011-02-24 01:28:37 +00008744 if (logging != MagickFalse)
8745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8746 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008747
glennrpd71e86a2011-02-24 01:28:37 +00008748 /* Sort palette, transparent first */;
8749
8750 n = 0;
8751
8752 for (i=0; i<number_transparent; i++)
8753 colormap[n++] = transparent[i];
8754
8755 for (i=0; i<number_semitransparent; i++)
8756 colormap[n++] = semitransparent[i];
8757
8758 for (i=0; i<number_opaque; i++)
8759 colormap[n++] = opaque[i];
8760
glennrpc6c391a2011-04-27 02:23:56 +00008761 ping_background.index +=
8762 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008763
glennrpd71e86a2011-02-24 01:28:37 +00008764 /* image_colors < 257; search the colormap instead of the pixels
8765 * to get ping_have_color and ping_have_non_bw
8766 */
8767 for (i=0; i<n; i++)
8768 {
8769 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008770 {
glennrpd71e86a2011-02-24 01:28:37 +00008771 if (colormap[i].red != colormap[i].green ||
8772 colormap[i].red != colormap[i].blue)
8773 {
8774 ping_have_color=MagickTrue;
8775 ping_have_non_bw=MagickTrue;
8776 break;
8777 }
8778 }
8779
8780 if (ping_have_non_bw == MagickFalse)
8781 {
8782 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008783 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008784 }
glennrp8bb3a022010-12-13 20:40:04 +00008785 }
8786
glennrpd71e86a2011-02-24 01:28:37 +00008787 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8788 (number_transparent == 0 && number_semitransparent == 0)) &&
8789 (((mng_info->write_png_colortype-1) ==
8790 PNG_COLOR_TYPE_PALETTE) ||
8791 (mng_info->write_png_colortype == 0)))
8792 {
glennrp6185c532011-01-14 17:58:40 +00008793 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008794 {
glennrpd71e86a2011-02-24 01:28:37 +00008795 if (n != (ssize_t) image_colors)
8796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8797 " image_colors (%d) and n (%d) don't match",
8798 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008799
glennrpd71e86a2011-02-24 01:28:37 +00008800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8801 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008802 }
glennrp03812ae2010-12-24 01:31:34 +00008803
glennrpd71e86a2011-02-24 01:28:37 +00008804 image->colors = image_colors;
8805
cristy16ea1392012-03-21 20:38:41 +00008806 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008807 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008808 ThrowWriterException(ResourceLimitError,
8809 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008810
8811 for (i=0; i< (ssize_t) image_colors; i++)
8812 image->colormap[i] = colormap[i];
8813
8814 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008815 {
glennrpd71e86a2011-02-24 01:28:37 +00008816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8817 " image->colors=%d (%d)",
8818 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008819
glennrpd71e86a2011-02-24 01:28:37 +00008820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8821 " Update the pixel indexes");
8822 }
glennrp6185c532011-01-14 17:58:40 +00008823
glennrpfd05d622011-02-25 04:10:33 +00008824 /* Sync the pixel indices with the new colormap */
8825
glennrpd71e86a2011-02-24 01:28:37 +00008826 for (y=0; y < (ssize_t) image->rows; y++)
8827 {
cristy16ea1392012-03-21 20:38:41 +00008828 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008829
cristy16ea1392012-03-21 20:38:41 +00008830 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008831 break;
glennrp6185c532011-01-14 17:58:40 +00008832
glennrpd71e86a2011-02-24 01:28:37 +00008833 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008834 {
glennrpd71e86a2011-02-24 01:28:37 +00008835 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008836 {
cristy8a46d822012-08-28 23:32:39 +00008837 if ((image->alpha_trait != BlendPixelTrait ||
cristy16ea1392012-03-21 20:38:41 +00008838 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8839 image->colormap[i].red == GetPixelRed(image,q) &&
8840 image->colormap[i].green == GetPixelGreen(image,q) &&
8841 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008842 {
cristy16ea1392012-03-21 20:38:41 +00008843 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008844 break;
glennrp6185c532011-01-14 17:58:40 +00008845 }
glennrp6185c532011-01-14 17:58:40 +00008846 }
cristy16ea1392012-03-21 20:38:41 +00008847 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008848 }
glennrp6185c532011-01-14 17:58:40 +00008849
glennrpd71e86a2011-02-24 01:28:37 +00008850 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8851 break;
8852 }
8853 }
8854 }
8855
8856 if (logging != MagickFalse)
8857 {
8858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8859 " image->colors=%d", (int) image->colors);
8860
8861 if (image->colormap != NULL)
8862 {
8863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008864 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008865
8866 for (i=0; i < (ssize_t) image->colors; i++)
8867 {
cristy72988482011-03-29 16:34:38 +00008868 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008869 {
8870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8871 " %d (%d,%d,%d,%d)",
8872 (int) i,
8873 (int) image->colormap[i].red,
8874 (int) image->colormap[i].green,
8875 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008876 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008877 }
glennrp6185c532011-01-14 17:58:40 +00008878 }
8879 }
glennrp03812ae2010-12-24 01:31:34 +00008880
glennrpd71e86a2011-02-24 01:28:37 +00008881 if (number_transparent < 257)
8882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8883 " number_transparent = %d",
8884 number_transparent);
8885 else
glennrp03812ae2010-12-24 01:31:34 +00008886
glennrpd71e86a2011-02-24 01:28:37 +00008887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8888 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008889
glennrpd71e86a2011-02-24 01:28:37 +00008890 if (number_opaque < 257)
8891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8892 " number_opaque = %d",
8893 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008894
glennrpd71e86a2011-02-24 01:28:37 +00008895 else
8896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8897 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008898
glennrpd71e86a2011-02-24 01:28:37 +00008899 if (number_semitransparent < 257)
8900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8901 " number_semitransparent = %d",
8902 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008903
glennrpd71e86a2011-02-24 01:28:37 +00008904 else
8905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8906 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008907
glennrpd71e86a2011-02-24 01:28:37 +00008908 if (ping_have_non_bw == MagickFalse)
8909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8910 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008911
glennrpd71e86a2011-02-24 01:28:37 +00008912 else if (ping_have_color == MagickFalse)
8913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8914 " All pixels and the background are gray");
8915
8916 else
8917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8918 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008919
glennrp03812ae2010-12-24 01:31:34 +00008920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8921 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008922 }
glennrpfd05d622011-02-25 04:10:33 +00008923
glennrpc8c2f062011-02-25 19:00:33 +00008924 if (mng_info->write_png8 == MagickFalse)
8925 break;
glennrpfd05d622011-02-25 04:10:33 +00008926
glennrpc8c2f062011-02-25 19:00:33 +00008927 /* Make any reductions necessary for the PNG8 format */
8928 if (image_colors <= 256 &&
8929 image_colors != 0 && image->colormap != NULL &&
8930 number_semitransparent == 0 &&
8931 number_transparent <= 1)
8932 break;
8933
8934 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008935 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8936 * transparent color so if more than one is transparent we merge
8937 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008938 */
glennrp130fc452011-08-20 03:43:18 +00008939 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008940 {
8941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8942 " Thresholding the alpha channel to binary");
8943
8944 for (y=0; y < (ssize_t) image->rows; y++)
8945 {
cristy16ea1392012-03-21 20:38:41 +00008946 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008947
cristy16ea1392012-03-21 20:38:41 +00008948 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008949 break;
8950
8951 for (x=0; x < (ssize_t) image->columns; x++)
8952 {
cristy16ea1392012-03-21 20:38:41 +00008953 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008954 {
cristy16ea1392012-03-21 20:38:41 +00008955 SetPixelInfoPixel(image,&image->background_color,r);
8956 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008957 }
8958 else
cristy16ea1392012-03-21 20:38:41 +00008959 SetPixelAlpha(image,OpaqueAlpha,r);
8960 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008961 }
glennrpbb4f99d2011-05-22 11:13:17 +00008962
glennrpc8c2f062011-02-25 19:00:33 +00008963 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8964 break;
8965
8966 if (image_colors != 0 && image_colors <= 256 &&
8967 image->colormap != NULL)
8968 for (i=0; i<image_colors; i++)
cristy16ea1392012-03-21 20:38:41 +00008969 image->colormap[i].alpha =
8970 (image->colormap[i].alpha > TransparentAlpha/2 ?
8971 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008972 }
8973 continue;
8974 }
8975
8976 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008977 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8978 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8979 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008980 */
glennrpd3371642011-03-22 19:42:23 +00008981 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8982 {
8983 if (logging != MagickFalse)
8984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8985 " Quantizing the background color to 4-4-4");
8986
8987 tried_444 = MagickTrue;
8988
glennrp91d99252011-06-25 14:30:13 +00008989 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008990
8991 if (logging != MagickFalse)
8992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8993 " Quantizing the pixel colors to 4-4-4");
8994
8995 if (image->colormap == NULL)
8996 {
8997 for (y=0; y < (ssize_t) image->rows; y++)
8998 {
cristy16ea1392012-03-21 20:38:41 +00008999 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00009000
cristy16ea1392012-03-21 20:38:41 +00009001 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00009002 break;
9003
9004 for (x=0; x < (ssize_t) image->columns; x++)
9005 {
cristy16ea1392012-03-21 20:38:41 +00009006 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00009007 LBR04PixelRGB(r);
cristy16ea1392012-03-21 20:38:41 +00009008 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00009009 }
glennrpbb4f99d2011-05-22 11:13:17 +00009010
glennrpd3371642011-03-22 19:42:23 +00009011 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9012 break;
9013 }
9014 }
9015
9016 else /* Should not reach this; colormap already exists and
9017 must be <= 256 */
9018 {
9019 if (logging != MagickFalse)
9020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9021 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00009022
glennrpd3371642011-03-22 19:42:23 +00009023 for (i=0; i<image_colors; i++)
9024 {
glennrp91d99252011-06-25 14:30:13 +00009025 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00009026 }
9027 }
9028 continue;
9029 }
9030
glennrp82b3c532011-03-22 19:20:54 +00009031 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9032 {
9033 if (logging != MagickFalse)
9034 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9035 " Quantizing the background color to 3-3-3");
9036
9037 tried_333 = MagickTrue;
9038
glennrp91d99252011-06-25 14:30:13 +00009039 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00009040
9041 if (logging != MagickFalse)
9042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009043 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00009044
9045 if (image->colormap == NULL)
9046 {
9047 for (y=0; y < (ssize_t) image->rows; y++)
9048 {
cristy16ea1392012-03-21 20:38:41 +00009049 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00009050
cristy16ea1392012-03-21 20:38:41 +00009051 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00009052 break;
9053
9054 for (x=0; x < (ssize_t) image->columns; x++)
9055 {
cristy16ea1392012-03-21 20:38:41 +00009056 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9057 LBR03RGB(r);
9058 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00009059 }
glennrpbb4f99d2011-05-22 11:13:17 +00009060
glennrp82b3c532011-03-22 19:20:54 +00009061 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9062 break;
9063 }
9064 }
9065
9066 else /* Should not reach this; colormap already exists and
9067 must be <= 256 */
9068 {
9069 if (logging != MagickFalse)
9070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009071 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00009072 for (i=0; i<image_colors; i++)
9073 {
glennrp91d99252011-06-25 14:30:13 +00009074 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00009075 }
glennrpd3371642011-03-22 19:42:23 +00009076 }
9077 continue;
glennrp82b3c532011-03-22 19:20:54 +00009078 }
glennrpc8c2f062011-02-25 19:00:33 +00009079
glennrp8ca51ad2011-05-12 21:22:32 +00009080 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00009081 {
9082 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00009083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00009084 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00009085
glennrp8ca51ad2011-05-12 21:22:32 +00009086 tried_332 = MagickTrue;
9087
glennrp3faa9a32011-04-23 14:00:25 +00009088 /* Red and green were already done so we only quantize the blue
9089 * channel
9090 */
9091
glennrp91d99252011-06-25 14:30:13 +00009092 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00009093
glennrpc8c2f062011-02-25 19:00:33 +00009094 if (logging != MagickFalse)
9095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009096 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00009097
glennrpc8c2f062011-02-25 19:00:33 +00009098 if (image->colormap == NULL)
9099 {
9100 for (y=0; y < (ssize_t) image->rows; y++)
9101 {
cristy16ea1392012-03-21 20:38:41 +00009102 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00009103
cristy16ea1392012-03-21 20:38:41 +00009104 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00009105 break;
9106
9107 for (x=0; x < (ssize_t) image->columns; x++)
9108 {
cristy16ea1392012-03-21 20:38:41 +00009109 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00009110 LBR02PixelBlue(r);
cristy16ea1392012-03-21 20:38:41 +00009111 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00009112 }
glennrpbb4f99d2011-05-22 11:13:17 +00009113
glennrpc8c2f062011-02-25 19:00:33 +00009114 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9115 break;
9116 }
9117 }
glennrpfd05d622011-02-25 04:10:33 +00009118
glennrpc8c2f062011-02-25 19:00:33 +00009119 else /* Should not reach this; colormap already exists and
9120 must be <= 256 */
9121 {
9122 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00009123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00009124 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00009125 for (i=0; i<image_colors; i++)
9126 {
glennrp91d99252011-06-25 14:30:13 +00009127 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00009128 }
9129 }
9130 continue;
9131 }
glennrp8ca51ad2011-05-12 21:22:32 +00009132
9133 if (image_colors == 0 || image_colors > 256)
9134 {
glennrp34ef7202013-09-27 17:36:53 +00009135 /* Take care of special case with 256 opaque colors + 1 transparent
glennrp8ca51ad2011-05-12 21:22:32 +00009136 * color. We don't need to quantize to 2-3-2-1; we only need to
9137 * eliminate one color, so we'll merge the two darkest red
9138 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9139 */
glennrp34ef7202013-09-27 17:36:53 +00009140 if (logging != MagickFalse)
9141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9142 " Merging two dark red background colors to 3-3-2-1");
9143
glennrp8ca51ad2011-05-12 21:22:32 +00009144 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9145 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9146 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9147 {
9148 image->background_color.red=ScaleCharToQuantum(0x24);
9149 }
glennrpbb4f99d2011-05-22 11:13:17 +00009150
glennrp34ef7202013-09-27 17:36:53 +00009151 if (logging != MagickFalse)
9152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9153 " Merging two dark red pixel colors to 3-3-2-1");
9154
glennrp8ca51ad2011-05-12 21:22:32 +00009155 if (image->colormap == NULL)
9156 {
9157 for (y=0; y < (ssize_t) image->rows; y++)
9158 {
cristy16ea1392012-03-21 20:38:41 +00009159 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00009160
cristy16ea1392012-03-21 20:38:41 +00009161 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00009162 break;
glennrpbb4f99d2011-05-22 11:13:17 +00009163
glennrp8ca51ad2011-05-12 21:22:32 +00009164 for (x=0; x < (ssize_t) image->columns; x++)
9165 {
cristy16ea1392012-03-21 20:38:41 +00009166 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9167 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9168 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9169 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00009170 {
cristy16ea1392012-03-21 20:38:41 +00009171 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00009172 }
cristy16ea1392012-03-21 20:38:41 +00009173 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00009174 }
glennrpbb4f99d2011-05-22 11:13:17 +00009175
glennrp8ca51ad2011-05-12 21:22:32 +00009176 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9177 break;
glennrpbb4f99d2011-05-22 11:13:17 +00009178
glennrp8ca51ad2011-05-12 21:22:32 +00009179 }
9180 }
9181
9182 else
9183 {
9184 for (i=0; i<image_colors; i++)
9185 {
9186 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9187 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9188 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9189 {
9190 image->colormap[i].red=ScaleCharToQuantum(0x24);
9191 }
9192 }
9193 }
9194 }
glennrpd71e86a2011-02-24 01:28:37 +00009195 }
glennrpa8036d62012-11-04 01:46:06 +00009196 }
glennrpfd05d622011-02-25 04:10:33 +00009197 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00009198
glennrpfd05d622011-02-25 04:10:33 +00009199 /* If we are excluding the tRNS chunk and there is transparency,
9200 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9201 * PNG.
glennrp8d579662011-02-23 02:05:02 +00009202 */
glennrp0e8ea192010-12-24 18:00:33 +00009203 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9204 (number_transparent != 0 || number_semitransparent != 0))
9205 {
glennrpd17915c2011-04-29 14:24:22 +00009206 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00009207
9208 if (ping_have_color == MagickFalse)
9209 mng_info->write_png_colortype = 5;
9210
9211 else
9212 mng_info->write_png_colortype = 7;
9213
glennrp8d579662011-02-23 02:05:02 +00009214 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00009215 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00009216 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00009217
glennrp0e8ea192010-12-24 18:00:33 +00009218 }
9219
glennrpfd05d622011-02-25 04:10:33 +00009220 /* See if cheap transparency is possible. It is only possible
9221 * when there is a single transparent color, no semitransparent
9222 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00009223 * as the transparent color. We only need this information if
9224 * we are writing a PNG with colortype 0 or 2, and we have not
9225 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00009226 */
glennrp5a39f372011-02-25 04:52:16 +00009227 if (number_transparent == 1 &&
9228 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00009229 {
9230 ping_have_cheap_transparency = MagickTrue;
9231
9232 if (number_semitransparent != 0)
9233 ping_have_cheap_transparency = MagickFalse;
9234
9235 else if (image_colors == 0 || image_colors > 256 ||
9236 image->colormap == NULL)
9237 {
cristy16ea1392012-03-21 20:38:41 +00009238 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00009239 *q;
9240
glennrpfd05d622011-02-25 04:10:33 +00009241 for (y=0; y < (ssize_t) image->rows; y++)
9242 {
9243 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9244
cristy16ea1392012-03-21 20:38:41 +00009245 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00009246 break;
9247
9248 for (x=0; x < (ssize_t) image->columns; x++)
9249 {
cristy16ea1392012-03-21 20:38:41 +00009250 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9251 (unsigned short) GetPixelRed(image,q) ==
9252 ping_trans_color.red &&
9253 (unsigned short) GetPixelGreen(image,q) ==
9254 ping_trans_color.green &&
9255 (unsigned short) GetPixelBlue(image,q) ==
9256 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00009257 {
9258 ping_have_cheap_transparency = MagickFalse;
9259 break;
9260 }
9261
cristy16ea1392012-03-21 20:38:41 +00009262 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00009263 }
glennrpbb4f99d2011-05-22 11:13:17 +00009264
glennrpfd05d622011-02-25 04:10:33 +00009265 if (ping_have_cheap_transparency == MagickFalse)
9266 break;
9267 }
9268 }
9269 else
9270 {
glennrp67b9c1a2011-04-22 18:47:36 +00009271 /* Assuming that image->colormap[0] is the one transparent color
9272 * and that all others are opaque.
9273 */
glennrpfd05d622011-02-25 04:10:33 +00009274 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00009275 for (i=1; i<image_colors; i++)
9276 if (image->colormap[i].red == image->colormap[0].red &&
9277 image->colormap[i].green == image->colormap[0].green &&
9278 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00009279 {
glennrp67b9c1a2011-04-22 18:47:36 +00009280 ping_have_cheap_transparency = MagickFalse;
9281 break;
glennrpfd05d622011-02-25 04:10:33 +00009282 }
9283 }
glennrpbb4f99d2011-05-22 11:13:17 +00009284
glennrpfd05d622011-02-25 04:10:33 +00009285 if (logging != MagickFalse)
9286 {
9287 if (ping_have_cheap_transparency == MagickFalse)
9288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9289 " Cheap transparency is not possible.");
9290
9291 else
9292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9293 " Cheap transparency is possible.");
9294 }
9295 }
9296 else
9297 ping_have_cheap_transparency = MagickFalse;
9298
glennrp8640fb52010-11-23 15:48:26 +00009299 image_depth=image->depth;
9300
glennrp26c990a2010-11-23 02:23:20 +00009301 quantum_info = (QuantumInfo *) NULL;
9302 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00009303 image_colors=(int) image->colors;
cristyb0a657e2012-08-29 00:45:37 +00009304 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
glennrp26c990a2010-11-23 02:23:20 +00009305
glennrp48c20622014-04-23 01:00:37 +00009306 if (mng_info->write_png_colortype < 5)
glennrp197c8e62014-04-22 23:45:20 +00009307 mng_info->IsPalette=image->storage_class == PseudoClass &&
9308 image_colors <= 256 && image->colormap != NULL;
9309 else
9310 mng_info->IsPalette = MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00009311
glennrp52a479c2011-02-26 21:14:38 +00009312 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9313 (image->colors == 0 || image->colormap == NULL))
9314 {
cristy16ea1392012-03-21 20:38:41 +00009315 image_info=DestroyImageInfo(image_info);
9316 image=DestroyImage(image);
9317 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00009318 "Cannot write PNG8 or color-type 3; colormap is NULL",
cristy16ea1392012-03-21 20:38:41 +00009319 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00009320 return(MagickFalse);
9321 }
9322
cristy3ed852e2009-09-05 21:47:34 +00009323 /*
9324 Allocate the PNG structures
9325 */
9326#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00009327 error_info.image=image;
9328 error_info.exception=exception;
9329 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009330 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9331 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00009332
cristy3ed852e2009-09-05 21:47:34 +00009333#else
cristy16ea1392012-03-21 20:38:41 +00009334 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009335 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00009336
cristy3ed852e2009-09-05 21:47:34 +00009337#endif
9338 if (ping == (png_struct *) NULL)
9339 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009340
cristy3ed852e2009-09-05 21:47:34 +00009341 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00009342
cristy3ed852e2009-09-05 21:47:34 +00009343 if (ping_info == (png_info *) NULL)
9344 {
9345 png_destroy_write_struct(&ping,(png_info **) NULL);
9346 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9347 }
glennrp0fe50b42010-11-16 03:52:51 +00009348
cristy3ed852e2009-09-05 21:47:34 +00009349 png_set_write_fn(ping,image,png_put_data,png_flush_data);
cristy09973322013-06-30 21:06:30 +00009350 pixel_info=(MemoryInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00009351
glennrp5af765f2010-03-30 11:12:18 +00009352 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009353 {
9354 /*
9355 PNG write failed.
9356 */
9357#ifdef PNG_DEBUG
9358 if (image_info->verbose)
9359 (void) printf("PNG write has failed.\n");
9360#endif
9361 png_destroy_write_struct(&ping,&ping_info);
glennrp868fff32014-03-16 22:09:06 +00009362#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00009363 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009364#endif
glennrpedaa0382012-04-12 14:16:21 +00009365
cristy09973322013-06-30 21:06:30 +00009366 if (pixel_info != (MemoryInfo *) NULL)
9367 pixel_info=RelinquishVirtualMemory(pixel_info);
glennrpedaa0382012-04-12 14:16:21 +00009368
9369 if (quantum_info != (QuantumInfo *) NULL)
9370 quantum_info=DestroyQuantumInfo(quantum_info);
9371
cristy16ea1392012-03-21 20:38:41 +00009372 if (ping_have_blob != MagickFalse)
9373 (void) CloseBlob(image);
9374 image_info=DestroyImageInfo(image_info);
9375 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009376 return(MagickFalse);
9377 }
glennrpedaa0382012-04-12 14:16:21 +00009378
9379 /* { For navigation to end of SETJMP-protected block. Within this
9380 * block, use png_error() instead of Throwing an Exception, to ensure
9381 * that libpng is able to clean up, and that the semaphore is unlocked.
9382 */
9383
glennrp868fff32014-03-16 22:09:06 +00009384#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpedaa0382012-04-12 14:16:21 +00009385 LockSemaphoreInfo(ping_semaphore);
9386#endif
9387
glennrp943b7d32013-04-21 00:40:38 +00009388#ifdef PNG_BENIGN_ERRORS_SUPPORTED
glennrpa3a5f952013-04-17 12:58:15 +00009389 /* Allow benign errors */
9390 png_set_benign_errors(ping, 1);
9391#endif
9392
cristy3ed852e2009-09-05 21:47:34 +00009393 /*
9394 Prepare PNG for writing.
9395 */
glennrp9bf97b62012-06-06 21:03:14 +00009396
cristy3ed852e2009-09-05 21:47:34 +00009397#if defined(PNG_MNG_FEATURES_SUPPORTED)
9398 if (mng_info->write_mng)
glennrp25024a62012-06-07 11:38:34 +00009399 {
cristy3ed852e2009-09-05 21:47:34 +00009400 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp25024a62012-06-07 11:38:34 +00009401# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9402 /* Disable new libpng-1.5.10 feature when writing a MNG because
9403 * zero-length PLTE is OK
9404 */
9405 png_set_check_for_invalid_index (ping, 0);
9406# endif
9407 }
glennrp2b013e42010-11-24 16:55:50 +00009408
cristy3ed852e2009-09-05 21:47:34 +00009409#else
9410# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9411 if (mng_info->write_mng)
9412 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00009413
cristy3ed852e2009-09-05 21:47:34 +00009414# endif
9415#endif
glennrp2b013e42010-11-24 16:55:50 +00009416
cristy3ed852e2009-09-05 21:47:34 +00009417 x=0;
glennrp2b013e42010-11-24 16:55:50 +00009418
cristy4e5bc842010-06-09 13:56:01 +00009419 ping_width=(png_uint_32) image->columns;
9420 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00009421
cristy3ed852e2009-09-05 21:47:34 +00009422 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9423 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009424
glennrpfd164d22013-01-26 21:10:22 +00009425 if (mng_info->write_png48 || mng_info->write_png64)
9426 image_depth=16;
9427
cristy3ed852e2009-09-05 21:47:34 +00009428 if (mng_info->write_png_depth != 0)
9429 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009430
cristy3ed852e2009-09-05 21:47:34 +00009431 /* Adjust requested depth to next higher valid depth if necessary */
9432 if (image_depth > 8)
9433 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009434
cristy3ed852e2009-09-05 21:47:34 +00009435 if ((image_depth > 4) && (image_depth < 8))
9436 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009437
cristy3ed852e2009-09-05 21:47:34 +00009438 if (image_depth == 3)
9439 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00009440
cristy3ed852e2009-09-05 21:47:34 +00009441 if (logging != MagickFalse)
9442 {
9443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009444 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00009445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009446 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00009447 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00009448 " image_matte=%.20g",(double) image->alpha_trait);
cristy3ed852e2009-09-05 21:47:34 +00009449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009450 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00009451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009452 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00009453 }
glennrp8640fb52010-11-23 15:48:26 +00009454
cristy3ed852e2009-09-05 21:47:34 +00009455 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00009456 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00009457
glennrp26f37912010-12-23 16:22:42 +00009458
cristy3ed852e2009-09-05 21:47:34 +00009459#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00009460 if (ping_exclude_pHYs == MagickFalse)
9461 {
cristy16ea1392012-03-21 20:38:41 +00009462 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00009463 (!mng_info->write_mng || !mng_info->equal_physs))
9464 {
glennrp0fe50b42010-11-16 03:52:51 +00009465 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9467 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00009468
9469 if (image->units == PixelsPerInchResolution)
9470 {
glennrpdfd70802010-11-14 01:23:35 +00009471 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009472 ping_pHYs_x_resolution=
cristy16ea1392012-03-21 20:38:41 +00009473 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00009474 ping_pHYs_y_resolution=
cristy16ea1392012-03-21 20:38:41 +00009475 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009476 }
glennrpdfd70802010-11-14 01:23:35 +00009477
cristy3ed852e2009-09-05 21:47:34 +00009478 else if (image->units == PixelsPerCentimeterResolution)
9479 {
glennrpdfd70802010-11-14 01:23:35 +00009480 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy16ea1392012-03-21 20:38:41 +00009481 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9482 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009483 }
glennrp991d11d2010-11-12 21:55:28 +00009484
cristy3ed852e2009-09-05 21:47:34 +00009485 else
9486 {
glennrpdfd70802010-11-14 01:23:35 +00009487 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy16ea1392012-03-21 20:38:41 +00009488 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9489 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009490 }
glennrp991d11d2010-11-12 21:55:28 +00009491
glennrp823b55c2011-03-14 18:46:46 +00009492 if (logging != MagickFalse)
9493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9494 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9495 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9496 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009497 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009498 }
glennrp26f37912010-12-23 16:22:42 +00009499 }
cristy3ed852e2009-09-05 21:47:34 +00009500#endif
glennrpa521b2f2010-10-29 04:11:03 +00009501
glennrp26f37912010-12-23 16:22:42 +00009502 if (ping_exclude_bKGD == MagickFalse)
9503 {
glennrpa521b2f2010-10-29 04:11:03 +00009504 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009505 {
glennrpa521b2f2010-10-29 04:11:03 +00009506 unsigned int
9507 mask;
cristy3ed852e2009-09-05 21:47:34 +00009508
glennrpa521b2f2010-10-29 04:11:03 +00009509 mask=0xffff;
9510 if (ping_bit_depth == 8)
9511 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009512
glennrpa521b2f2010-10-29 04:11:03 +00009513 if (ping_bit_depth == 4)
9514 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009515
glennrpa521b2f2010-10-29 04:11:03 +00009516 if (ping_bit_depth == 2)
9517 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009518
glennrpa521b2f2010-10-29 04:11:03 +00009519 if (ping_bit_depth == 1)
9520 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009521
glennrpa521b2f2010-10-29 04:11:03 +00009522 ping_background.red=(png_uint_16)
9523 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009524
glennrpa521b2f2010-10-29 04:11:03 +00009525 ping_background.green=(png_uint_16)
9526 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009527
glennrpa521b2f2010-10-29 04:11:03 +00009528 ping_background.blue=(png_uint_16)
9529 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009530
9531 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009532 }
cristy3ed852e2009-09-05 21:47:34 +00009533
glennrp0fe50b42010-11-16 03:52:51 +00009534 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009535 {
9536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9537 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9539 " background_color index is %d",
9540 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009541
9542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9543 " ping_bit_depth=%d",ping_bit_depth);
9544 }
glennrp0fe50b42010-11-16 03:52:51 +00009545
9546 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009547 }
glennrp0fe50b42010-11-16 03:52:51 +00009548
cristy3ed852e2009-09-05 21:47:34 +00009549 /*
9550 Select the color type.
9551 */
9552 matte=image_matte;
9553 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009554
glennrp1273f7b2011-02-24 03:20:30 +00009555 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009556 {
glennrpfd05d622011-02-25 04:10:33 +00009557 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009558 for reducing the sample depth from 8. */
9559
glennrp0fe50b42010-11-16 03:52:51 +00009560 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009561
glennrp8bb3a022010-12-13 20:40:04 +00009562 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009563
9564 /*
9565 Set image palette.
9566 */
9567 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9568
glennrp0fe50b42010-11-16 03:52:51 +00009569 if (logging != MagickFalse)
9570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9571 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009572 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009573
9574 for (i=0; i < (ssize_t) number_colors; i++)
9575 {
9576 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9577 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9578 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9579 if (logging != MagickFalse)
9580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009581#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009582 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009583#else
9584 " %5ld (%5d,%5d,%5d)",
9585#endif
glennrp0fe50b42010-11-16 03:52:51 +00009586 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9587
9588 }
glennrp2b013e42010-11-24 16:55:50 +00009589
glennrp8bb3a022010-12-13 20:40:04 +00009590 ping_have_PLTE=MagickTrue;
9591 image_depth=ping_bit_depth;
9592 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009593
glennrp58e01762011-01-07 15:28:54 +00009594 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009595 {
glennrp0fe50b42010-11-16 03:52:51 +00009596 /*
9597 Identify which colormap entry is transparent.
9598 */
9599 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009600 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009601
glennrp8bb3a022010-12-13 20:40:04 +00009602 for (i=0; i < (ssize_t) number_transparent; i++)
9603 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009604
glennrp0fe50b42010-11-16 03:52:51 +00009605
glennrp2cc891a2010-12-24 13:44:32 +00009606 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009607 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009608
9609 if (ping_num_trans == 0)
9610 ping_have_tRNS=MagickFalse;
9611
glennrp8bb3a022010-12-13 20:40:04 +00009612 else
9613 ping_have_tRNS=MagickTrue;
9614 }
glennrp0fe50b42010-11-16 03:52:51 +00009615
glennrp1273f7b2011-02-24 03:20:30 +00009616 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009617 {
glennrp1273f7b2011-02-24 03:20:30 +00009618 /*
9619 * Identify which colormap entry is the background color.
9620 */
9621
glennrp4f25bd02011-01-01 18:51:28 +00009622 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9623 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9624 break;
glennrp0fe50b42010-11-16 03:52:51 +00009625
glennrp4f25bd02011-01-01 18:51:28 +00009626 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009627
9628 if (logging != MagickFalse)
9629 {
9630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631 " background_color index is %d",
9632 (int) ping_background.index);
9633 }
glennrp4f25bd02011-01-01 18:51:28 +00009634 }
cristy3ed852e2009-09-05 21:47:34 +00009635 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009636
glennrpfd164d22013-01-26 21:10:22 +00009637 else if (mng_info->write_png_colortype == 1)
9638 {
9639 image_matte=MagickFalse;
9640 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9641 }
9642
9643 else if (mng_info->write_png24 || mng_info->write_png48 ||
9644 mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009645 {
9646 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009647 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009648 }
glennrp0fe50b42010-11-16 03:52:51 +00009649
glennrpfd164d22013-01-26 21:10:22 +00009650 else if (mng_info->write_png32 || mng_info->write_png64 ||
9651 mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009652 {
9653 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009654 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009655 }
glennrp0fe50b42010-11-16 03:52:51 +00009656
glennrp8bb3a022010-12-13 20:40:04 +00009657 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009658 {
glennrp5af765f2010-03-30 11:12:18 +00009659 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009660
glennrp8bb3a022010-12-13 20:40:04 +00009661 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009662 {
glennrp5af765f2010-03-30 11:12:18 +00009663 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009664
glennrp5af765f2010-03-30 11:12:18 +00009665 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9666 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009667 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009668
glennrp8bb3a022010-12-13 20:40:04 +00009669 else
9670 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009671
9672 if (logging != MagickFalse)
9673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9674 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009675 }
glennrp0fe50b42010-11-16 03:52:51 +00009676
glennrp7c4c9e62011-03-21 20:23:32 +00009677 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009678 {
9679 if (logging != MagickFalse)
9680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009681 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009682
glennrpd6bf1612010-12-17 17:28:54 +00009683 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009684 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009685
glennrpd6bf1612010-12-17 17:28:54 +00009686 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009687 {
glennrp5af765f2010-03-30 11:12:18 +00009688 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009689 image_matte=MagickFalse;
9690 }
glennrp0fe50b42010-11-16 03:52:51 +00009691
glennrpd6bf1612010-12-17 17:28:54 +00009692 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009693 {
glennrp5af765f2010-03-30 11:12:18 +00009694 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009695 image_matte=MagickTrue;
9696 }
glennrp0fe50b42010-11-16 03:52:51 +00009697
glennrp5aa37f62011-01-02 03:07:57 +00009698 if (image_info->type == PaletteType ||
9699 image_info->type == PaletteMatteType)
9700 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9701
glennrp7c4c9e62011-03-21 20:23:32 +00009702 if (mng_info->write_png_colortype == 0 &&
glennrp261f64e2014-08-09 15:44:51 +00009703 image_info->type == UndefinedType)
cristy3ed852e2009-09-05 21:47:34 +00009704 {
glennrp5aa37f62011-01-02 03:07:57 +00009705 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009706 {
glennrp5aa37f62011-01-02 03:07:57 +00009707 if (image_matte == MagickFalse)
9708 {
9709 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9710 image_matte=MagickFalse;
9711 }
glennrp0fe50b42010-11-16 03:52:51 +00009712
glennrp0b206f52011-01-07 04:55:32 +00009713 else
glennrp5aa37f62011-01-02 03:07:57 +00009714 {
9715 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9716 image_matte=MagickTrue;
9717 }
9718 }
9719 else
glennrp8bb3a022010-12-13 20:40:04 +00009720 {
glennrp5aa37f62011-01-02 03:07:57 +00009721 if (image_matte == MagickFalse)
9722 {
9723 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9724 image_matte=MagickFalse;
9725 }
glennrp8bb3a022010-12-13 20:40:04 +00009726
glennrp0b206f52011-01-07 04:55:32 +00009727 else
glennrp5aa37f62011-01-02 03:07:57 +00009728 {
9729 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9730 image_matte=MagickTrue;
9731 }
9732 }
glennrp0fe50b42010-11-16 03:52:51 +00009733 }
glennrp5aa37f62011-01-02 03:07:57 +00009734
cristy3ed852e2009-09-05 21:47:34 +00009735 }
glennrp0fe50b42010-11-16 03:52:51 +00009736
cristy3ed852e2009-09-05 21:47:34 +00009737 if (logging != MagickFalse)
9738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009739 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009740
glennrp5af765f2010-03-30 11:12:18 +00009741 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009742 {
9743 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9744 ping_color_type == PNG_COLOR_TYPE_RGB ||
9745 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9746 ping_bit_depth=8;
9747 }
cristy3ed852e2009-09-05 21:47:34 +00009748
glennrpd6bf1612010-12-17 17:28:54 +00009749 old_bit_depth=ping_bit_depth;
9750
glennrp5af765f2010-03-30 11:12:18 +00009751 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009752 {
cristy8a46d822012-08-28 23:32:39 +00009753 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00009754 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009755 }
glennrp8640fb52010-11-23 15:48:26 +00009756
glennrp5af765f2010-03-30 11:12:18 +00009757 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009758 {
cristy35ef8242010-06-03 16:24:13 +00009759 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009760 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009761
9762 if (image->colors == 0)
9763 {
glennrp0fe50b42010-11-16 03:52:51 +00009764 /* DO SOMETHING */
glennrpedaa0382012-04-12 14:16:21 +00009765 png_error(ping,"image has 0 colors");
glennrp0f111982010-07-07 20:18:33 +00009766 }
9767
cristy35ef8242010-06-03 16:24:13 +00009768 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009769 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009770 }
glennrp2b013e42010-11-24 16:55:50 +00009771
glennrpd6bf1612010-12-17 17:28:54 +00009772 if (logging != MagickFalse)
9773 {
9774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9775 " Number of colors: %.20g",(double) image_colors);
9776
9777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9778 " Tentative PNG bit depth: %d",ping_bit_depth);
9779 }
9780
9781 if (ping_bit_depth < (int) mng_info->write_png_depth)
9782 ping_bit_depth = mng_info->write_png_depth;
9783 }
glennrp2cc891a2010-12-24 13:44:32 +00009784
glennrp5af765f2010-03-30 11:12:18 +00009785 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009786
cristy3ed852e2009-09-05 21:47:34 +00009787 if (logging != MagickFalse)
9788 {
9789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +00009790 " Tentative PNG color type: %s (%.20g)",
9791 PngColorTypeToString(ping_color_type),
9792 (double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009793
cristy3ed852e2009-09-05 21:47:34 +00009794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009795 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009796
cristy3ed852e2009-09-05 21:47:34 +00009797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009798 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009799
cristy3ed852e2009-09-05 21:47:34 +00009800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009801
glennrp8640fb52010-11-23 15:48:26 +00009802 " image->depth: %.20g",(double) image->depth);
9803
9804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009805 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009806 }
9807
glennrp58e01762011-01-07 15:28:54 +00009808 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009809 {
glennrp4f25bd02011-01-01 18:51:28 +00009810 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009811 {
glennrp7c4c9e62011-03-21 20:23:32 +00009812 if (mng_info->write_png_colortype == 0)
9813 {
9814 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009815
glennrp7c4c9e62011-03-21 20:23:32 +00009816 if (ping_have_color != MagickFalse)
9817 ping_color_type=PNG_COLOR_TYPE_RGBA;
9818 }
glennrp4f25bd02011-01-01 18:51:28 +00009819
9820 /*
9821 * Determine if there is any transparent color.
9822 */
9823 if (number_transparent + number_semitransparent == 0)
9824 {
9825 /*
9826 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9827 */
glennrpa6a06632011-01-19 15:15:34 +00009828
glennrp4f25bd02011-01-01 18:51:28 +00009829 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009830
9831 if (mng_info->write_png_colortype == 0)
9832 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009833 }
9834
9835 else
9836 {
9837 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009838 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009839
9840 mask=0xffff;
9841
9842 if (ping_bit_depth == 8)
9843 mask=0x00ff;
9844
9845 if (ping_bit_depth == 4)
9846 mask=0x000f;
9847
9848 if (ping_bit_depth == 2)
9849 mask=0x0003;
9850
9851 if (ping_bit_depth == 1)
9852 mask=0x0001;
9853
9854 ping_trans_color.red=(png_uint_16)
9855 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9856
9857 ping_trans_color.green=(png_uint_16)
9858 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9859
9860 ping_trans_color.blue=(png_uint_16)
9861 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9862
9863 ping_trans_color.gray=(png_uint_16)
cristy16ea1392012-03-21 20:38:41 +00009864 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009865 image->colormap)) & mask);
9866
9867 ping_trans_color.index=(png_byte) 0;
9868
9869 ping_have_tRNS=MagickTrue;
9870 }
9871
9872 if (ping_have_tRNS != MagickFalse)
9873 {
9874 /*
glennrpfd05d622011-02-25 04:10:33 +00009875 * Determine if there is one and only one transparent color
9876 * and if so if it is fully transparent.
9877 */
9878 if (ping_have_cheap_transparency == MagickFalse)
9879 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009880 }
9881
9882 if (ping_have_tRNS != MagickFalse)
9883 {
glennrp7c4c9e62011-03-21 20:23:32 +00009884 if (mng_info->write_png_colortype == 0)
9885 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009886
9887 if (image_depth == 8)
9888 {
9889 ping_trans_color.red&=0xff;
9890 ping_trans_color.green&=0xff;
9891 ping_trans_color.blue&=0xff;
9892 ping_trans_color.gray&=0xff;
9893 }
9894 }
9895 }
cristy3ed852e2009-09-05 21:47:34 +00009896 else
9897 {
cristy3ed852e2009-09-05 21:47:34 +00009898 if (image_depth == 8)
9899 {
glennrp5af765f2010-03-30 11:12:18 +00009900 ping_trans_color.red&=0xff;
9901 ping_trans_color.green&=0xff;
9902 ping_trans_color.blue&=0xff;
9903 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009904 }
9905 }
9906 }
glennrp8640fb52010-11-23 15:48:26 +00009907
cristy3ed852e2009-09-05 21:47:34 +00009908 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009909
glennrp2e09f552010-11-14 00:38:48 +00009910 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009911 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009912
glennrp39992b42010-11-14 00:03:43 +00009913 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009914 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009915 ping_have_color == MagickFalse &&
9916 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009917 {
cristy35ef8242010-06-03 16:24:13 +00009918 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009919
cristy3ed852e2009-09-05 21:47:34 +00009920 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009921 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009922
glennrp7c4c9e62011-03-21 20:23:32 +00009923 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009924 {
glennrp5af765f2010-03-30 11:12:18 +00009925 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009926
cristy3ed852e2009-09-05 21:47:34 +00009927 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009928 {
9929 if (logging != MagickFalse)
9930 {
9931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9932 " Scaling ping_trans_color (0)");
9933 }
9934 ping_trans_color.gray*=0x0101;
9935 }
cristy3ed852e2009-09-05 21:47:34 +00009936 }
glennrp0fe50b42010-11-16 03:52:51 +00009937
cristy3ed852e2009-09-05 21:47:34 +00009938 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9939 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009940
glennrp136ee3a2011-04-27 15:47:45 +00009941 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009942 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009943 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009944
cristy3ed852e2009-09-05 21:47:34 +00009945 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009946 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009947
cristy3ed852e2009-09-05 21:47:34 +00009948 else
9949 {
glennrp5af765f2010-03-30 11:12:18 +00009950 ping_bit_depth=8;
9951 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009952 {
9953 if(!mng_info->write_png_depth)
9954 {
glennrp5af765f2010-03-30 11:12:18 +00009955 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009956
cristy35ef8242010-06-03 16:24:13 +00009957 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009958 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009959 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009960 }
9961 }
glennrp2b013e42010-11-24 16:55:50 +00009962
glennrp0fe50b42010-11-16 03:52:51 +00009963 else if (ping_color_type ==
9964 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009965 mng_info->IsPalette)
9966 {
cristy3ed852e2009-09-05 21:47:34 +00009967 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009968
cristy3ed852e2009-09-05 21:47:34 +00009969 int
9970 depth_4_ok=MagickTrue,
9971 depth_2_ok=MagickTrue,
9972 depth_1_ok=MagickTrue;
9973
cristybb503372010-05-27 20:51:26 +00009974 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009975 {
9976 unsigned char
9977 intensity;
9978
9979 intensity=ScaleQuantumToChar(image->colormap[i].red);
9980
9981 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9982 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9983 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9984 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009985 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009986 depth_1_ok=MagickFalse;
9987 }
glennrp2b013e42010-11-24 16:55:50 +00009988
cristy3ed852e2009-09-05 21:47:34 +00009989 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009990 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009991
cristy3ed852e2009-09-05 21:47:34 +00009992 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009993 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009994
cristy3ed852e2009-09-05 21:47:34 +00009995 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009996 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009997 }
9998 }
glennrp2b013e42010-11-24 16:55:50 +00009999
glennrp5af765f2010-03-30 11:12:18 +000010000 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +000010001 }
glennrp0fe50b42010-11-16 03:52:51 +000010002
cristy3ed852e2009-09-05 21:47:34 +000010003 else
glennrp0fe50b42010-11-16 03:52:51 +000010004
cristy3ed852e2009-09-05 21:47:34 +000010005 if (mng_info->IsPalette)
10006 {
glennrp17a14852010-05-10 03:01:59 +000010007 number_colors=image_colors;
10008
cristy3ed852e2009-09-05 21:47:34 +000010009 if (image_depth <= 8)
10010 {
cristy3ed852e2009-09-05 21:47:34 +000010011 /*
10012 Set image palette.
10013 */
glennrp5af765f2010-03-30 11:12:18 +000010014 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +000010015
glennrp3d627862013-02-26 00:19:34 +000010016 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +000010017 {
cristybb503372010-05-27 20:51:26 +000010018 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000010019 {
10020 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10021 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10022 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10023 }
glennrp0fe50b42010-11-16 03:52:51 +000010024
glennrp3b51f0e2010-11-27 18:14:08 +000010025 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +000010027 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +000010028 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010029
glennrp39992b42010-11-14 00:03:43 +000010030 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010031 }
glennrp0fe50b42010-11-16 03:52:51 +000010032
cristy3ed852e2009-09-05 21:47:34 +000010033 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +000010034 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +000010035 {
cristybefe4d22010-06-07 01:18:58 +000010036 size_t
10037 one;
10038
glennrp5af765f2010-03-30 11:12:18 +000010039 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +000010040 one=1;
glennrp0fe50b42010-11-16 03:52:51 +000010041
cristy16ea1392012-03-21 20:38:41 +000010042 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +000010043 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +000010044 }
glennrp0fe50b42010-11-16 03:52:51 +000010045
glennrp5af765f2010-03-30 11:12:18 +000010046 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +000010047
glennrp58e01762011-01-07 15:28:54 +000010048 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010049 {
glennrp0fe50b42010-11-16 03:52:51 +000010050 /*
glennrpd6bf1612010-12-17 17:28:54 +000010051 * Set up trans_colors array.
10052 */
glennrp0fe50b42010-11-16 03:52:51 +000010053 assert(number_colors <= 256);
10054
glennrpd6bf1612010-12-17 17:28:54 +000010055 ping_num_trans=(unsigned short) (number_transparent +
10056 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +000010057
10058 if (ping_num_trans == 0)
10059 ping_have_tRNS=MagickFalse;
10060
glennrpd6bf1612010-12-17 17:28:54 +000010061 else
glennrp0fe50b42010-11-16 03:52:51 +000010062 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010063 if (logging != MagickFalse)
10064 {
10065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10066 " Scaling ping_trans_color (1)");
10067 }
glennrpd6bf1612010-12-17 17:28:54 +000010068 ping_have_tRNS=MagickTrue;
10069
10070 for (i=0; i < ping_num_trans; i++)
10071 {
glennrp750105b2012-04-25 16:20:45 +000010072 ping_trans_alpha[i]= (png_byte)
cristy16ea1392012-03-21 20:38:41 +000010073 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +000010074 }
glennrp0fe50b42010-11-16 03:52:51 +000010075 }
10076 }
cristy3ed852e2009-09-05 21:47:34 +000010077 }
10078 }
glennrp0fe50b42010-11-16 03:52:51 +000010079
cristy3ed852e2009-09-05 21:47:34 +000010080 else
10081 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010082
cristy3ed852e2009-09-05 21:47:34 +000010083 if (image_depth < 8)
10084 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000010085
cristy3ed852e2009-09-05 21:47:34 +000010086 if ((save_image_depth == 16) && (image_depth == 8))
10087 {
glennrp4f25bd02011-01-01 18:51:28 +000010088 if (logging != MagickFalse)
10089 {
10090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10091 " Scaling ping_trans_color from (%d,%d,%d)",
10092 (int) ping_trans_color.red,
10093 (int) ping_trans_color.green,
10094 (int) ping_trans_color.blue);
10095 }
10096
glennrp5af765f2010-03-30 11:12:18 +000010097 ping_trans_color.red*=0x0101;
10098 ping_trans_color.green*=0x0101;
10099 ping_trans_color.blue*=0x0101;
10100 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +000010101
10102 if (logging != MagickFalse)
10103 {
10104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10105 " to (%d,%d,%d)",
10106 (int) ping_trans_color.red,
10107 (int) ping_trans_color.green,
10108 (int) ping_trans_color.blue);
10109 }
cristy3ed852e2009-09-05 21:47:34 +000010110 }
10111 }
10112
cristy4383ec82011-01-05 15:42:32 +000010113 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10114 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +000010115
cristy3ed852e2009-09-05 21:47:34 +000010116 /*
10117 Adjust background and transparency samples in sub-8-bit grayscale files.
10118 */
glennrp5af765f2010-03-30 11:12:18 +000010119 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +000010120 PNG_COLOR_TYPE_GRAY)
10121 {
10122 png_uint_16
10123 maxval;
10124
cristy35ef8242010-06-03 16:24:13 +000010125 size_t
10126 one=1;
10127
cristy22ffd972010-06-03 16:51:47 +000010128 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +000010129
glennrp4f25bd02011-01-01 18:51:28 +000010130 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000010131 {
cristy3ed852e2009-09-05 21:47:34 +000010132
cristy16ea1392012-03-21 20:38:41 +000010133 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10134 (ScaleQuantumToShort(((GetPixelInfoIntensity(
10135 &image->background_color))) +.5)));
10136
cristy3ed852e2009-09-05 21:47:34 +000010137 if (logging != MagickFalse)
10138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +000010139 " Setting up bKGD chunk (2)");
10140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10141 " background_color index is %d",
10142 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +000010143
glennrp991d11d2010-11-12 21:55:28 +000010144 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010145 }
cristy3ed852e2009-09-05 21:47:34 +000010146
glennrp3e3e20f2011-06-09 04:21:43 +000010147 if (logging != MagickFalse)
10148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10149 " Scaling ping_trans_color.gray from %d",
10150 (int)ping_trans_color.gray);
10151
glennrp9be9b1c2011-06-09 12:21:45 +000010152 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +000010153 ping_trans_color.gray)+.5);
10154
10155 if (logging != MagickFalse)
10156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10157 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +000010158 }
glennrp17a14852010-05-10 03:01:59 +000010159
glennrp26f37912010-12-23 16:22:42 +000010160 if (ping_exclude_bKGD == MagickFalse)
10161 {
glennrp1273f7b2011-02-24 03:20:30 +000010162 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +000010163 {
10164 /*
10165 Identify which colormap entry is the background color.
10166 */
10167
glennrp17a14852010-05-10 03:01:59 +000010168 number_colors=image_colors;
10169
glennrpa521b2f2010-10-29 04:11:03 +000010170 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10171 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +000010172 break;
10173
10174 ping_background.index=(png_byte) i;
10175
glennrp3b51f0e2010-11-27 18:14:08 +000010176 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +000010177 {
10178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +000010179 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +000010180 }
glennrp0fe50b42010-11-16 03:52:51 +000010181
cristy13d07042010-11-21 20:56:18 +000010182 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +000010183 {
10184 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +000010185
10186 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010187 {
10188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10189 " background =(%d,%d,%d)",
10190 (int) ping_background.red,
10191 (int) ping_background.green,
10192 (int) ping_background.blue);
10193 }
10194 }
glennrpa521b2f2010-10-29 04:11:03 +000010195
glennrpd6bf1612010-12-17 17:28:54 +000010196 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +000010197 {
glennrp3b51f0e2010-11-27 18:14:08 +000010198 if (logging != MagickFalse)
10199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10200 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +000010201 ping_have_bKGD = MagickFalse;
10202 }
glennrp17a14852010-05-10 03:01:59 +000010203 }
glennrp26f37912010-12-23 16:22:42 +000010204 }
glennrp17a14852010-05-10 03:01:59 +000010205
cristy3ed852e2009-09-05 21:47:34 +000010206 if (logging != MagickFalse)
10207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +000010208 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10209 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010210 /*
10211 Initialize compression level and filtering.
10212 */
10213 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010214 {
10215 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10216 " Setting up deflate compression");
10217
10218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10219 " Compression buffer size: 32768");
10220 }
10221
cristy3ed852e2009-09-05 21:47:34 +000010222 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +000010223
cristy3ed852e2009-09-05 21:47:34 +000010224 if (logging != MagickFalse)
10225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10226 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +000010227
cristy4054bfb2011-08-29 23:41:39 +000010228 png_set_compression_mem_level(ping, 9);
10229
glennrp10d739e2011-06-29 18:00:52 +000010230 /* Untangle the "-quality" setting:
10231
10232 Undefined is 0; the default is used.
10233 Default is 75
10234
10235 10's digit:
10236
glennrpef804f52013-09-24 00:38:27 +000010237 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
glennrp10d739e2011-06-29 18:00:52 +000010238 zlib default compression level
10239
10240 1-9: the zlib compression level
10241
10242 1's digit:
10243
10244 0-4: the PNG filter method
10245
10246 5: libpng adaptive filtering if compression level > 5
10247 libpng filter type "none" if compression level <= 5
10248 or if image is grayscale or palette
glennrp750105b2012-04-25 16:20:45 +000010249
glennrp10d739e2011-06-29 18:00:52 +000010250 6: libpng adaptive filtering
10251
10252 7: "LOCO" filtering (intrapixel differing) if writing
glennrp85dfe1a2013-09-24 17:30:39 +000010253 a MNG, otherwise "none". Did not work in IM-6.7.0-9
glennrp10d739e2011-06-29 18:00:52 +000010254 and earlier because of a missing "else".
10255
glennrp85dfe1a2013-09-24 17:30:39 +000010256 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10257 filtering. Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +000010258
glennrpef804f52013-09-24 00:38:27 +000010259 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
glennrp18682582011-06-30 18:11:47 +000010260 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +000010261
10262 Note that using the -quality option, not all combinations of
10263 PNG filter type, zlib compression level, and zlib compression
cristy16ea1392012-03-21 20:38:41 +000010264 strategy are possible. This will be addressed soon in a
10265 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +000010266
10267 */
10268
dirk29dd80e2013-10-31 23:11:11 +000010269 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10270 image_info->quality;
glennrp0fe50b42010-11-16 03:52:51 +000010271
glennrp18682582011-06-30 18:11:47 +000010272 if (quality <= 9)
10273 {
10274 if (mng_info->write_png_compression_strategy == 0)
10275 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10276 }
glennrp750105b2012-04-25 16:20:45 +000010277
glennrp18682582011-06-30 18:11:47 +000010278 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +000010279 {
10280 int
10281 level;
10282
cristybb503372010-05-27 20:51:26 +000010283 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +000010284
glennrp18682582011-06-30 18:11:47 +000010285 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +000010286 }
glennrp0fe50b42010-11-16 03:52:51 +000010287
glennrp18682582011-06-30 18:11:47 +000010288 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +000010289 {
glennrp18682582011-06-30 18:11:47 +000010290 if ((quality %10) == 8 || (quality %10) == 9)
glennrpa24b2452012-06-27 11:38:38 +000010291#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10292 mng_info->write_png_compression_strategy=Z_RLE+1;
10293#else
10294 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10295#endif
cristy3ed852e2009-09-05 21:47:34 +000010296 }
glennrp0fe50b42010-11-16 03:52:51 +000010297
glennrp18682582011-06-30 18:11:47 +000010298 if (mng_info->write_png_compression_filter == 0)
10299 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10300
cristy3ed852e2009-09-05 21:47:34 +000010301 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010302 {
glennrp18682582011-06-30 18:11:47 +000010303 if (mng_info->write_png_compression_level)
10304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10305 " Compression level: %d",
10306 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +000010307
glennrp18682582011-06-30 18:11:47 +000010308 if (mng_info->write_png_compression_strategy)
10309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10310 " Compression strategy: %d",
10311 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +000010312
glennrp18682582011-06-30 18:11:47 +000010313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10314 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +000010315
cristy4054bfb2011-08-29 23:41:39 +000010316 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +000010317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10318 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +000010319 else if (mng_info->write_png_compression_filter == 0 ||
10320 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +000010321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10322 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +000010323 else
10324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10325 " Base filter method: %d",
10326 (int) mng_info->write_png_compression_filter-1);
10327 }
glennrp2b013e42010-11-24 16:55:50 +000010328
glennrp18682582011-06-30 18:11:47 +000010329 if (mng_info->write_png_compression_level != 0)
10330 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10331
10332 if (mng_info->write_png_compression_filter == 6)
10333 {
10334 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10335 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10336 (quality < 50))
10337 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10338 else
10339 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10340 }
cristy4054bfb2011-08-29 23:41:39 +000010341 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +000010342 mng_info->write_png_compression_filter == 10)
10343 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10344
10345 else if (mng_info->write_png_compression_filter == 8)
10346 {
10347#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10348 if (mng_info->write_mng)
10349 {
10350 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10351 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10352 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10353 }
10354#endif
cristy4054bfb2011-08-29 23:41:39 +000010355 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +000010356 }
10357
10358 else if (mng_info->write_png_compression_filter == 9)
10359 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10360
10361 else if (mng_info->write_png_compression_filter != 0)
10362 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10363 mng_info->write_png_compression_filter-1);
10364
10365 if (mng_info->write_png_compression_strategy != 0)
10366 png_set_compression_strategy(ping,
10367 mng_info->write_png_compression_strategy-1);
10368
glennrpdec72c92013-02-26 17:42:47 +000010369 ping_interlace_method=image_info->interlace != NoInterlace;
10370
10371 if (mng_info->write_mng)
10372 png_set_sig_bytes(ping,8);
10373
10374 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10375
10376 if (mng_info->write_png_colortype != 0)
10377 {
10378 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10379 if (ping_have_color != MagickFalse)
10380 {
10381 ping_color_type = PNG_COLOR_TYPE_RGB;
10382
10383 if (ping_bit_depth < 8)
10384 ping_bit_depth=8;
10385 }
10386
10387 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10388 if (ping_have_color != MagickFalse)
10389 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10390 }
10391
10392 if (ping_need_colortype_warning != MagickFalse ||
10393 ((mng_info->write_png_depth &&
10394 (int) mng_info->write_png_depth != ping_bit_depth) ||
10395 (mng_info->write_png_colortype &&
10396 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10397 mng_info->write_png_colortype != 7 &&
10398 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10399 {
10400 if (logging != MagickFalse)
10401 {
10402 if (ping_need_colortype_warning != MagickFalse)
10403 {
10404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10405 " Image has transparency but tRNS chunk was excluded");
10406 }
10407
10408 if (mng_info->write_png_depth)
10409 {
10410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10411 " Defined png:bit-depth=%u, Computed depth=%u",
10412 mng_info->write_png_depth,
10413 ping_bit_depth);
10414 }
10415
10416 if (mng_info->write_png_colortype)
10417 {
10418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10419 " Defined png:color-type=%u, Computed color type=%u",
10420 mng_info->write_png_colortype-1,
10421 ping_color_type);
10422 }
10423 }
10424
10425 png_warning(ping,
10426 "Cannot write image with defined png:bit-depth or png:color-type.");
10427 }
10428
10429 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
10430 {
10431 /* Add an opaque matte channel */
10432 image->alpha_trait = BlendPixelTrait;
10433 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10434
10435 if (logging != MagickFalse)
10436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10437 " Added an opaque matte channel");
10438 }
10439
10440 if (number_transparent != 0 || number_semitransparent != 0)
10441 {
10442 if (ping_color_type < 4)
10443 {
10444 ping_have_tRNS=MagickTrue;
10445 if (logging != MagickFalse)
10446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10447 " Setting ping_have_tRNS=MagickTrue.");
10448 }
10449 }
10450
10451 if (logging != MagickFalse)
10452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10453 " Writing PNG header chunks");
10454
10455 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10456 ping_bit_depth,ping_color_type,
10457 ping_interlace_method,ping_compression_method,
10458 ping_filter_method);
10459
10460 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10461 {
10462 png_set_PLTE(ping,ping_info,palette,number_colors);
10463
10464 if (logging != MagickFalse)
10465 {
10466 for (i=0; i< (ssize_t) number_colors; i++)
10467 {
10468 if (i < ping_num_trans)
10469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10470 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10471 (int) i,
10472 (int) palette[i].red,
10473 (int) palette[i].green,
10474 (int) palette[i].blue,
10475 (int) i,
10476 (int) ping_trans_alpha[i]);
10477 else
10478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10479 " PLTE[%d] = (%d,%d,%d)",
10480 (int) i,
10481 (int) palette[i].red,
10482 (int) palette[i].green,
10483 (int) palette[i].blue);
10484 }
10485 }
10486 }
10487
cristy0d57eec2011-09-04 22:13:56 +000010488 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10489 if (ping_exclude_sRGB != MagickFalse ||
glennrp3d627862013-02-26 00:19:34 +000010490 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy0d57eec2011-09-04 22:13:56 +000010491 {
10492 if ((ping_exclude_tEXt == MagickFalse ||
10493 ping_exclude_zTXt == MagickFalse) &&
10494 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +000010495 {
10496 ResetImageProfileIterator(image);
10497 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +000010498 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010499 profile=GetImageProfile(image,name);
10500
10501 if (profile != (StringInfo *) NULL)
10502 {
glennrp5af765f2010-03-30 11:12:18 +000010503#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +000010504 if ((LocaleCompare(name,"ICC") == 0) ||
10505 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +000010506 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010507
10508 if (ping_exclude_iCCP == MagickFalse)
10509 {
glennrpecab7d72013-05-14 22:50:32 +000010510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10511 " Setting up iCCP chunk");
10512
cristy16ea1392012-03-21 20:38:41 +000010513 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +000010514#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +000010515 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +000010516#else
10517 (png_const_bytep) GetStringInfoDatum(profile),
10518#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010519 (png_uint_32) GetStringInfoLength(profile));
glennrp918b9dc2013-04-03 13:41:41 +000010520 ping_have_iCCP = MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010521 }
glennrp26f37912010-12-23 16:22:42 +000010522 }
glennrp0fe50b42010-11-16 03:52:51 +000010523
glennrpc8cbc5d2011-01-01 00:12:34 +000010524 else
cristy3ed852e2009-09-05 21:47:34 +000010525#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010526 if (ping_exclude_zCCP == MagickFalse)
10527 {
glennrpecab7d72013-05-14 22:50:32 +000010528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10529 " Setting up zTXT chunk with uuencoded ICC");
glennrpcf002022011-01-30 02:38:15 +000010530 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +000010531 (unsigned char *) name,(unsigned char *) name,
10532 GetStringInfoDatum(profile),
10533 (png_uint_32) GetStringInfoLength(profile));
glennrpecab7d72013-05-14 22:50:32 +000010534 ping_have_iCCP = MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010535 }
10536 }
glennrp0b206f52011-01-07 04:55:32 +000010537
glennrpc8cbc5d2011-01-01 00:12:34 +000010538 if (logging != MagickFalse)
10539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10540 " Setting up text chunk with %s profile",name);
10541
10542 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +000010543 }
cristy0d57eec2011-09-04 22:13:56 +000010544 }
cristy3ed852e2009-09-05 21:47:34 +000010545 }
10546
10547#if defined(PNG_WRITE_sRGB_SUPPORTED)
10548 if ((mng_info->have_write_global_srgb == 0) &&
glennrpecab7d72013-05-14 22:50:32 +000010549 ping_have_iCCP != MagickTrue &&
10550 (ping_have_sRGB != MagickFalse ||
10551 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +000010552 {
glennrp26f37912010-12-23 16:22:42 +000010553 if (ping_exclude_sRGB == MagickFalse)
10554 {
10555 /*
10556 Note image rendering intent.
10557 */
10558 if (logging != MagickFalse)
10559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10560 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +000010561
glennrp26f37912010-12-23 16:22:42 +000010562 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +000010563 Magick_RenderingIntent_to_PNG_RenderingIntent(
10564 image->rendering_intent)));
glennrp918b9dc2013-04-03 13:41:41 +000010565
10566 ping_have_sRGB = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000010567 }
cristy3ed852e2009-09-05 21:47:34 +000010568 }
glennrp26f37912010-12-23 16:22:42 +000010569
glennrp5af765f2010-03-30 11:12:18 +000010570 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +000010571#endif
10572 {
glennrp2cc891a2010-12-24 13:44:32 +000010573 if (ping_exclude_gAMA == MagickFalse &&
glennrp918b9dc2013-04-03 13:41:41 +000010574 ping_have_iCCP == MagickFalse &&
10575 ping_have_sRGB == MagickFalse &&
glennrp2cc891a2010-12-24 13:44:32 +000010576 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +000010577 (image->gamma < .45 || image->gamma > .46)))
10578 {
cristy3ed852e2009-09-05 21:47:34 +000010579 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10580 {
10581 /*
10582 Note image gamma.
10583 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10584 */
10585 if (logging != MagickFalse)
10586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10587 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010588
cristy3ed852e2009-09-05 21:47:34 +000010589 png_set_gAMA(ping,ping_info,image->gamma);
10590 }
glennrp26f37912010-12-23 16:22:42 +000010591 }
glennrp2b013e42010-11-24 16:55:50 +000010592
glennrp918b9dc2013-04-03 13:41:41 +000010593 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010594 {
glennrp26f37912010-12-23 16:22:42 +000010595 if ((mng_info->have_write_global_chrm == 0) &&
10596 (image->chromaticity.red_primary.x != 0.0))
10597 {
10598 /*
10599 Note image chromaticity.
glennrp918b9dc2013-04-03 13:41:41 +000010600 Note: if cHRM+gAMA == sRGB write sRGB instead.
glennrp26f37912010-12-23 16:22:42 +000010601 */
10602 PrimaryInfo
10603 bp,
10604 gp,
10605 rp,
10606 wp;
cristy3ed852e2009-09-05 21:47:34 +000010607
glennrp26f37912010-12-23 16:22:42 +000010608 wp=image->chromaticity.white_point;
10609 rp=image->chromaticity.red_primary;
10610 gp=image->chromaticity.green_primary;
10611 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010612
glennrp26f37912010-12-23 16:22:42 +000010613 if (logging != MagickFalse)
10614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10615 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010616
glennrp26f37912010-12-23 16:22:42 +000010617 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10618 bp.x,bp.y);
10619 }
10620 }
cristy3ed852e2009-09-05 21:47:34 +000010621 }
glennrpdfd70802010-11-14 01:23:35 +000010622
glennrp26f37912010-12-23 16:22:42 +000010623 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010624 {
glennrp26f37912010-12-23 16:22:42 +000010625 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010626 {
glennrp26f37912010-12-23 16:22:42 +000010627 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010628 if (logging)
10629 {
10630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10631 " Setting up bKGD chunk");
10632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10633 " background color = (%d,%d,%d)",
10634 (int) ping_background.red,
10635 (int) ping_background.green,
10636 (int) ping_background.blue);
10637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10638 " index = %d, gray=%d",
10639 (int) ping_background.index,
10640 (int) ping_background.gray);
10641 }
10642 }
glennrp26f37912010-12-23 16:22:42 +000010643 }
10644
10645 if (ping_exclude_pHYs == MagickFalse)
10646 {
10647 if (ping_have_pHYs != MagickFalse)
10648 {
10649 png_set_pHYs(ping,ping_info,
10650 ping_pHYs_x_resolution,
10651 ping_pHYs_y_resolution,
10652 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010653
10654 if (logging)
10655 {
10656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10657 " Setting up pHYs chunk");
10658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10659 " x_resolution=%lu",
10660 (unsigned long) ping_pHYs_x_resolution);
10661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10662 " y_resolution=%lu",
10663 (unsigned long) ping_pHYs_y_resolution);
10664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10665 " unit_type=%lu",
10666 (unsigned long) ping_pHYs_unit_type);
10667 }
glennrp26f37912010-12-23 16:22:42 +000010668 }
glennrpdfd70802010-11-14 01:23:35 +000010669 }
10670
10671#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010672 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010673 {
glennrp26f37912010-12-23 16:22:42 +000010674 if (image->page.x || image->page.y)
10675 {
10676 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10677 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010678
glennrp26f37912010-12-23 16:22:42 +000010679 if (logging != MagickFalse)
10680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10681 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10682 (int) image->page.x, (int) image->page.y);
10683 }
glennrpdfd70802010-11-14 01:23:35 +000010684 }
10685#endif
10686
dirkfd6fd072014-10-24 21:10:08 +000010687#if defined(PNG_tIME_SUPPORTED)
10688 if (ping_exclude_tIME == MagickFalse)
10689 {
10690 const char
10691 *timestamp;
10692
10693 timestamp=GetImageOption(image_info,"png:tIME");
10694 if (timestamp != (const char *) NULL)
10695 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
10696 else
10697 {
10698 if (image->taint == MagickFalse)
10699 timestamp=GetImageProperty(image,"png:tIME",exception);
10700 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
10701 }
10702 }
10703#endif
10704
glennrpda8f3a72011-02-27 23:54:12 +000010705 if (mng_info->need_blob != MagickFalse)
10706 {
cristy16ea1392012-03-21 20:38:41 +000010707 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010708 MagickFalse)
10709 png_error(ping,"WriteBlob Failed");
10710
10711 ping_have_blob=MagickTrue;
10712 }
10713
cristy3ed852e2009-09-05 21:47:34 +000010714 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010715
glennrp39992b42010-11-14 00:03:43 +000010716 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010717 {
glennrp3b51f0e2010-11-27 18:14:08 +000010718 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010719 {
10720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10721 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10722 }
10723
10724 if (ping_color_type == 3)
10725 (void) png_set_tRNS(ping, ping_info,
10726 ping_trans_alpha,
10727 ping_num_trans,
10728 NULL);
10729
10730 else
10731 {
10732 (void) png_set_tRNS(ping, ping_info,
10733 NULL,
10734 0,
10735 &ping_trans_color);
10736
glennrp3b51f0e2010-11-27 18:14:08 +000010737 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010738 {
10739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010740 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010741 (int) ping_trans_color.red,
10742 (int) ping_trans_color.green,
10743 (int) ping_trans_color.blue);
10744 }
10745 }
glennrp991d11d2010-11-12 21:55:28 +000010746 }
10747
cristy3ed852e2009-09-05 21:47:34 +000010748 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010749 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010750
cristy3ed852e2009-09-05 21:47:34 +000010751 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010752
cristy3ed852e2009-09-05 21:47:34 +000010753 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010754 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010755
glennrp26f37912010-12-23 16:22:42 +000010756 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010757 {
glennrp4f25bd02011-01-01 18:51:28 +000010758 if ((image->page.width != 0 && image->page.width != image->columns) ||
10759 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010760 {
10761 unsigned char
10762 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010763
glennrp26f37912010-12-23 16:22:42 +000010764 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10765 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010766 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010767 PNGLong(chunk+4,(png_uint_32) image->page.width);
10768 PNGLong(chunk+8,(png_uint_32) image->page.height);
10769 chunk[12]=0; /* unit = pixels */
10770 (void) WriteBlob(image,13,chunk);
10771 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10772 }
cristy3ed852e2009-09-05 21:47:34 +000010773 }
10774
10775#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010776 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010777#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010778 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010779#undef PNG_HAVE_IDAT
10780#endif
10781
10782 png_set_packing(ping);
10783 /*
10784 Allocate memory.
10785 */
10786 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010787 if (image_depth > 8)
10788 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010789 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010790 {
glennrpb4a13412010-05-05 12:47:19 +000010791 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010792 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010793 break;
glennrp0fe50b42010-11-16 03:52:51 +000010794
glennrpb4a13412010-05-05 12:47:19 +000010795 case PNG_COLOR_TYPE_GRAY_ALPHA:
10796 rowbytes*=2;
10797 break;
glennrp0fe50b42010-11-16 03:52:51 +000010798
glennrpb4a13412010-05-05 12:47:19 +000010799 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010800 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010801 break;
glennrp0fe50b42010-11-16 03:52:51 +000010802
glennrpb4a13412010-05-05 12:47:19 +000010803 default:
10804 break;
cristy3ed852e2009-09-05 21:47:34 +000010805 }
glennrp3b51f0e2010-11-27 18:14:08 +000010806
10807 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010808 {
10809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10810 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010811
glennrpb4a13412010-05-05 12:47:19 +000010812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010813 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010814 }
cristy09973322013-06-30 21:06:30 +000010815 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10816 if (pixel_info == (MemoryInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010817 png_error(ping,"Allocation of memory for pixels failed");
cristy09973322013-06-30 21:06:30 +000010818 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
glennrp0fe50b42010-11-16 03:52:51 +000010819
cristy3ed852e2009-09-05 21:47:34 +000010820 /*
10821 Initialize image scanlines.
10822 */
cristyed552522009-10-16 14:04:35 +000010823 quantum_info=AcquireQuantumInfo(image_info,image);
10824 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010825 png_error(ping,"Memory allocation for quantum_info failed");
cristy3ed852e2009-09-05 21:47:34 +000010826 quantum_info->format=UndefinedQuantumFormat;
10827 quantum_info->depth=image_depth;
glennrp4b840d72012-11-22 16:01:16 +000010828 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
cristy3ed852e2009-09-05 21:47:34 +000010829 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010830
cristy3ed852e2009-09-05 21:47:34 +000010831 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrpfd164d22013-01-26 21:10:22 +000010832 !mng_info->write_png48 && !mng_info->write_png64 &&
glennrp8bb3a022010-12-13 20:40:04 +000010833 !mng_info->write_png32) &&
10834 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010835 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010836 image_matte == MagickFalse &&
10837 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010838 {
glennrp8bb3a022010-12-13 20:40:04 +000010839 /* Palette, Bilevel, or Opaque Monochrome */
cristy16ea1392012-03-21 20:38:41 +000010840 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010841 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010842
cristy3ed852e2009-09-05 21:47:34 +000010843 quantum_info->depth=8;
10844 for (pass=0; pass < num_passes; pass++)
10845 {
10846 /*
10847 Convert PseudoClass image to a PNG monochrome image.
10848 */
cristybb503372010-05-27 20:51:26 +000010849 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010850 {
glennrpd71e86a2011-02-24 01:28:37 +000010851 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10853 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010854
cristy16ea1392012-03-21 20:38:41 +000010855 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010856
cristy16ea1392012-03-21 20:38:41 +000010857 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010858 break;
glennrp0fe50b42010-11-16 03:52:51 +000010859
cristy3ed852e2009-09-05 21:47:34 +000010860 if (mng_info->IsPalette)
10861 {
cristy16ea1392012-03-21 20:38:41 +000010862 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10863 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010864 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10865 mng_info->write_png_depth &&
10866 mng_info->write_png_depth != old_bit_depth)
10867 {
10868 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010869 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010870 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010871 >> (8-old_bit_depth));
10872 }
10873 }
glennrp0fe50b42010-11-16 03:52:51 +000010874
cristy3ed852e2009-09-05 21:47:34 +000010875 else
10876 {
cristy16ea1392012-03-21 20:38:41 +000010877 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10878 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010879 }
glennrp0fe50b42010-11-16 03:52:51 +000010880
cristy3ed852e2009-09-05 21:47:34 +000010881 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010882 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010883 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010884 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010885
glennrp3b51f0e2010-11-27 18:14:08 +000010886 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10888 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010889
glennrpcf002022011-01-30 02:38:15 +000010890 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010891 }
10892 if (image->previous == (Image *) NULL)
10893 {
10894 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10895 if (status == MagickFalse)
10896 break;
10897 }
10898 }
10899 }
glennrp0fe50b42010-11-16 03:52:51 +000010900
glennrp8bb3a022010-12-13 20:40:04 +000010901 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010902 {
glennrp0fe50b42010-11-16 03:52:51 +000010903 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrpfd164d22013-01-26 21:10:22 +000010904 !mng_info->write_png48 && !mng_info->write_png64 &&
10905 !mng_info->write_png32) && (image_matte != MagickFalse ||
10906 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10907 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010908 {
cristy16ea1392012-03-21 20:38:41 +000010909 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010910 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010911
glennrp8bb3a022010-12-13 20:40:04 +000010912 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010913 {
glennrp8bb3a022010-12-13 20:40:04 +000010914
cristybb503372010-05-27 20:51:26 +000010915 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010916 {
cristy16ea1392012-03-21 20:38:41 +000010917 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010918
cristy16ea1392012-03-21 20:38:41 +000010919 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010920 break;
glennrp2cc891a2010-12-24 13:44:32 +000010921
glennrp5af765f2010-03-30 11:12:18 +000010922 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010923 {
glennrp8bb3a022010-12-13 20:40:04 +000010924 if (mng_info->IsPalette)
cristy16ea1392012-03-21 20:38:41 +000010925 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10926 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010927
glennrp8bb3a022010-12-13 20:40:04 +000010928 else
cristy16ea1392012-03-21 20:38:41 +000010929 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10930 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010931
glennrp3b51f0e2010-11-27 18:14:08 +000010932 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010934 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010935 }
glennrp2cc891a2010-12-24 13:44:32 +000010936
glennrp8bb3a022010-12-13 20:40:04 +000010937 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10938 {
10939 if (logging != MagickFalse && y == 0)
10940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10941 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010942
cristy16ea1392012-03-21 20:38:41 +000010943 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10944 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010945 }
glennrp2cc891a2010-12-24 13:44:32 +000010946
glennrp3b51f0e2010-11-27 18:14:08 +000010947 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010949 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010950
glennrpcf002022011-01-30 02:38:15 +000010951 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010952 }
glennrp2cc891a2010-12-24 13:44:32 +000010953
glennrp8bb3a022010-12-13 20:40:04 +000010954 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010955 {
glennrp8bb3a022010-12-13 20:40:04 +000010956 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10957 if (status == MagickFalse)
10958 break;
cristy3ed852e2009-09-05 21:47:34 +000010959 }
cristy3ed852e2009-09-05 21:47:34 +000010960 }
10961 }
glennrp8bb3a022010-12-13 20:40:04 +000010962
10963 else
10964 {
cristy16ea1392012-03-21 20:38:41 +000010965 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010966 *p;
10967
10968 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010969 {
glennrpfd164d22013-01-26 21:10:22 +000010970 if ((image_depth > 8) ||
10971 mng_info->write_png24 ||
glennrp8bb3a022010-12-13 20:40:04 +000010972 mng_info->write_png32 ||
glennrpfd164d22013-01-26 21:10:22 +000010973 mng_info->write_png48 ||
10974 mng_info->write_png64 ||
10975 (!mng_info->write_png8 && !mng_info->IsPalette))
glennrp8bb3a022010-12-13 20:40:04 +000010976 {
10977 for (y=0; y < (ssize_t) image->rows; y++)
10978 {
cristy862a33c2012-05-17 22:49:37 +000010979 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010980
cristy16ea1392012-03-21 20:38:41 +000010981 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010982 break;
glennrp2cc891a2010-12-24 13:44:32 +000010983
glennrp8bb3a022010-12-13 20:40:04 +000010984 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10985 {
10986 if (image->storage_class == DirectClass)
cristy16ea1392012-03-21 20:38:41 +000010987 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10988 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010989
glennrp8bb3a022010-12-13 20:40:04 +000010990 else
cristy16ea1392012-03-21 20:38:41 +000010991 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10992 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010993 }
glennrp2cc891a2010-12-24 13:44:32 +000010994
glennrp8bb3a022010-12-13 20:40:04 +000010995 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10996 {
cristy16ea1392012-03-21 20:38:41 +000010997 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010998 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000010999 exception);
glennrp2cc891a2010-12-24 13:44:32 +000011000
glennrp8bb3a022010-12-13 20:40:04 +000011001 if (logging != MagickFalse && y == 0)
11002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11003 " Writing GRAY_ALPHA PNG pixels (3)");
11004 }
glennrp2cc891a2010-12-24 13:44:32 +000011005
glennrp8bb3a022010-12-13 20:40:04 +000011006 else if (image_matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +000011007 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11008 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011009
glennrp8bb3a022010-12-13 20:40:04 +000011010 else
cristy16ea1392012-03-21 20:38:41 +000011011 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11012 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000011013
glennrp8bb3a022010-12-13 20:40:04 +000011014 if (logging != MagickFalse && y == 0)
11015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11016 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000011017
glennrpcf002022011-01-30 02:38:15 +000011018 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000011019 }
11020 }
glennrp2cc891a2010-12-24 13:44:32 +000011021
glennrp8bb3a022010-12-13 20:40:04 +000011022 else
glennrpfd164d22013-01-26 21:10:22 +000011023 /* not ((image_depth > 8) ||
11024 mng_info->write_png24 || mng_info->write_png32 ||
11025 mng_info->write_png48 || mng_info->write_png64 ||
11026 (!mng_info->write_png8 && !mng_info->IsPalette))
11027 */
glennrp8bb3a022010-12-13 20:40:04 +000011028 {
11029 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11030 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11031 {
11032 if (logging != MagickFalse)
11033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11034 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000011035
glennrp8bb3a022010-12-13 20:40:04 +000011036 quantum_info->depth=8;
11037 image_depth=8;
11038 }
glennrp2cc891a2010-12-24 13:44:32 +000011039
glennrp8bb3a022010-12-13 20:40:04 +000011040 for (y=0; y < (ssize_t) image->rows; y++)
11041 {
11042 if (logging != MagickFalse && y == 0)
11043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11044 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000011045
cristy16ea1392012-03-21 20:38:41 +000011046 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000011047
cristy16ea1392012-03-21 20:38:41 +000011048 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000011049 break;
glennrp2cc891a2010-12-24 13:44:32 +000011050
glennrp8bb3a022010-12-13 20:40:04 +000011051 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000011052 {
glennrp4bf89732011-03-21 13:48:28 +000011053 quantum_info->depth=image->depth;
11054
cristy16ea1392012-03-21 20:38:41 +000011055 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11056 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000011057 }
glennrp2cc891a2010-12-24 13:44:32 +000011058
glennrp8bb3a022010-12-13 20:40:04 +000011059 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11060 {
11061 if (logging != MagickFalse && y == 0)
11062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11063 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000011064
cristy16ea1392012-03-21 20:38:41 +000011065 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000011066 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000011067 exception);
glennrp8bb3a022010-12-13 20:40:04 +000011068 }
glennrp2cc891a2010-12-24 13:44:32 +000011069
glennrp8bb3a022010-12-13 20:40:04 +000011070 else
glennrp8bb3a022010-12-13 20:40:04 +000011071 {
cristy16ea1392012-03-21 20:38:41 +000011072 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11073 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000011074
11075 if (logging != MagickFalse && y <= 2)
11076 {
11077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000011078 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000011079
11080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11081 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11082 (int)ping_pixels[0],(int)ping_pixels[1]);
11083 }
glennrp8bb3a022010-12-13 20:40:04 +000011084 }
glennrpcf002022011-01-30 02:38:15 +000011085 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000011086 }
11087 }
glennrp2cc891a2010-12-24 13:44:32 +000011088
glennrp8bb3a022010-12-13 20:40:04 +000011089 if (image->previous == (Image *) NULL)
11090 {
11091 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11092 if (status == MagickFalse)
11093 break;
11094 }
cristy3ed852e2009-09-05 21:47:34 +000011095 }
glennrp8bb3a022010-12-13 20:40:04 +000011096 }
11097 }
11098
cristyb32b90a2009-09-07 21:45:48 +000011099 if (quantum_info != (QuantumInfo *) NULL)
11100 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000011101
11102 if (logging != MagickFalse)
11103 {
11104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000011105 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000011106
cristy3ed852e2009-09-05 21:47:34 +000011107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011108 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000011109
cristy3ed852e2009-09-05 21:47:34 +000011110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011111 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000011112
cristy3ed852e2009-09-05 21:47:34 +000011113 if (mng_info->write_png_depth)
11114 {
11115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011116 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011117 }
glennrp0fe50b42010-11-16 03:52:51 +000011118
cristy3ed852e2009-09-05 21:47:34 +000011119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000011120 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011121
cristy3ed852e2009-09-05 21:47:34 +000011122 if (mng_info->write_png_colortype)
11123 {
11124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011125 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011126 }
glennrp0fe50b42010-11-16 03:52:51 +000011127
cristy3ed852e2009-09-05 21:47:34 +000011128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000011129 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011130
cristy3ed852e2009-09-05 21:47:34 +000011131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000011132 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000011133 }
11134 /*
glennrpa0ed0092011-04-18 16:36:29 +000011135 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000011136 */
glennrp823b55c2011-03-14 18:46:46 +000011137 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000011138 {
glennrp26f37912010-12-23 16:22:42 +000011139 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000011140 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000011141 while (property != (const char *) NULL)
11142 {
11143 png_textp
11144 text;
glennrp2cc891a2010-12-24 13:44:32 +000011145
cristy16ea1392012-03-21 20:38:41 +000011146 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000011147
glennrpe4d5faf2013-07-22 00:11:04 +000011148 /* Don't write any "png:" or "jpeg:" properties; those are just for
11149 * "identify" or for passing through to another JPEG
11150 */
11151 if ((LocaleNCompare(property,"png:",4) != 0 &&
glennrpa3d5f0e2014-01-30 18:58:51 +000011152 LocaleNCompare(property,"jpeg:",5) != 0) &&
glennrpe4d5faf2013-07-22 00:11:04 +000011153
glennrpa0ed0092011-04-18 16:36:29 +000011154
11155 /* Suppress density and units if we wrote a pHYs chunk */
11156 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000011157 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000011158 LocaleCompare(property,"units") != 0) &&
11159
11160 /* Suppress the IM-generated Date:create and Date:modify */
11161 (ping_exclude_date == MagickFalse ||
11162 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000011163 {
glennrpc70af4a2011-03-07 00:08:23 +000011164 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000011165 {
cristya865ccd2012-07-28 00:33:10 +000011166
glennrpecab7d72013-05-14 22:50:32 +000011167#if PNG_LIBPNG_VER >= 10400
cristya865ccd2012-07-28 00:33:10 +000011168 text=(png_textp) png_malloc(ping,
11169 (png_alloc_size_t) sizeof(png_text));
11170#else
11171 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11172#endif
glennrpc70af4a2011-03-07 00:08:23 +000011173 text[0].key=(char *) property;
11174 text[0].text=(char *) value;
11175 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000011176
glennrpc70af4a2011-03-07 00:08:23 +000011177 if (ping_exclude_tEXt != MagickFalse)
11178 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11179
11180 else if (ping_exclude_zTXt != MagickFalse)
11181 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11182
11183 else
glennrp26f37912010-12-23 16:22:42 +000011184 {
glennrpc70af4a2011-03-07 00:08:23 +000011185 text[0].compression=image_info->compression == NoCompression ||
11186 (image_info->compression == UndefinedCompression &&
11187 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11188 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000011189 }
glennrp2cc891a2010-12-24 13:44:32 +000011190
glennrpc70af4a2011-03-07 00:08:23 +000011191 if (logging != MagickFalse)
11192 {
11193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11194 " Setting up text chunk");
11195
11196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpcbc92152013-02-04 15:46:22 +000011197 " keyword: '%s'",text[0].key);
glennrpc70af4a2011-03-07 00:08:23 +000011198 }
11199
11200 png_set_text(ping,ping_info,text,1);
11201 png_free(ping,text);
11202 }
glennrp26f37912010-12-23 16:22:42 +000011203 }
11204 property=GetNextImageProperty(image);
11205 }
cristy3ed852e2009-09-05 21:47:34 +000011206 }
11207
11208 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011209 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011210
11211 if (logging != MagickFalse)
11212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11213 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000011214
cristy3ed852e2009-09-05 21:47:34 +000011215 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000011216
cristy3ed852e2009-09-05 21:47:34 +000011217 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11218 {
11219 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000011220 (ping_width != mng_info->page.width) ||
11221 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000011222 {
11223 unsigned char
11224 chunk[32];
11225
11226 /*
11227 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11228 */
11229 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11230 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000011231 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000011232 chunk[4]=4;
11233 chunk[5]=0; /* frame name separator (no name) */
11234 chunk[6]=1; /* flag for changing delay, for next frame only */
11235 chunk[7]=0; /* flag for changing frame timeout */
11236 chunk[8]=1; /* flag for changing frame clipping for next frame */
11237 chunk[9]=0; /* flag for changing frame sync_id */
11238 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11239 chunk[14]=0; /* clipping boundaries delta type */
11240 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11241 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000011242 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000011243 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11244 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000011245 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000011246 (void) WriteBlob(image,31,chunk);
11247 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11248 mng_info->old_framing_mode=4;
11249 mng_info->framing_mode=1;
11250 }
glennrp0fe50b42010-11-16 03:52:51 +000011251
cristy3ed852e2009-09-05 21:47:34 +000011252 else
11253 mng_info->framing_mode=3;
11254 }
11255 if (mng_info->write_mng && !mng_info->need_fram &&
11256 ((int) image->dispose == 3))
glennrpedaa0382012-04-12 14:16:21 +000011257 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
glennrp0fe50b42010-11-16 03:52:51 +000011258
cristy3ed852e2009-09-05 21:47:34 +000011259 /*
11260 Free PNG resources.
11261 */
glennrp5af765f2010-03-30 11:12:18 +000011262
cristy3ed852e2009-09-05 21:47:34 +000011263 png_destroy_write_struct(&ping,&ping_info);
11264
cristy09973322013-06-30 21:06:30 +000011265 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +000011266
cristy16ea1392012-03-21 20:38:41 +000011267 if (ping_have_blob != MagickFalse)
11268 (void) CloseBlob(image);
11269
11270 image_info=DestroyImageInfo(image_info);
11271 image=DestroyImage(image);
11272
glennrpb9cfe272010-12-21 15:08:06 +000011273 /* Store bit depth actually written */
11274 s[0]=(char) ping_bit_depth;
11275 s[1]='\0';
11276
cristy16ea1392012-03-21 20:38:41 +000011277 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000011278
cristy3ed852e2009-09-05 21:47:34 +000011279 if (logging != MagickFalse)
11280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11281 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011282
glennrp868fff32014-03-16 22:09:06 +000011283#ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
glennrpedaa0382012-04-12 14:16:21 +000011284 UnlockSemaphoreInfo(ping_semaphore);
11285#endif
11286
11287 /* } for navigation to beginning of SETJMP-protected block. Revert to
11288 * Throwing an Exception when an error occurs.
11289 */
11290
cristy3ed852e2009-09-05 21:47:34 +000011291 return(MagickTrue);
11292/* End write one PNG image */
glennrpedaa0382012-04-12 14:16:21 +000011293
cristy3ed852e2009-09-05 21:47:34 +000011294}
11295
11296/*
11297%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11298% %
11299% %
11300% %
11301% W r i t e P N G I m a g e %
11302% %
11303% %
11304% %
11305%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11306%
11307% WritePNGImage() writes a Portable Network Graphics (PNG) or
11308% Multiple-image Network Graphics (MNG) image file.
11309%
11310% MNG support written by Glenn Randers-Pehrson, glennrp@image...
11311%
11312% The format of the WritePNGImage method is:
11313%
cristy16ea1392012-03-21 20:38:41 +000011314% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11315% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011316%
11317% A description of each parameter follows:
11318%
11319% o image_info: the image info.
11320%
11321% o image: The image.
11322%
cristy16ea1392012-03-21 20:38:41 +000011323% o exception: return any errors or warnings in this structure.
11324%
cristy3ed852e2009-09-05 21:47:34 +000011325% Returns MagickTrue on success, MagickFalse on failure.
11326%
11327% Communicating with the PNG encoder:
11328%
11329% While the datastream written is always in PNG format and normally would
11330% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000011331% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000011332%
glennrp5a39f372011-02-25 04:52:16 +000011333% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11334% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000011335% is present, the tRNS chunk must only have values 0 and 255
11336% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000011337% transparent). If other values are present they will be
11338% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000011339% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000011340% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11341% of any resulting fully-transparent pixels is changed to
11342% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000011343%
11344% If you want better quantization or dithering of the colors
11345% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000011346% PNG encoder. The pixels contain 8-bit indices even if
11347% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000011348% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000011349% PNG grayscale type might be slightly more efficient. Please
11350% note that writing to the PNG8 format may result in loss
11351% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000011352%
11353% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11354% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000011355% one of the colors as transparent. The only loss incurred
11356% is reduction of sample depth to 8. If the image has more
11357% than one transparent color, has semitransparent pixels, or
11358% has an opaque pixel with the same RGB components as the
11359% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000011360%
11361% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11362% transparency is permitted, i.e., the alpha sample for
11363% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000011364% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000011365% The only loss in data is the reduction of the sample depth
11366% to 8.
cristy3ed852e2009-09-05 21:47:34 +000011367%
glennrpfd164d22013-01-26 21:10:22 +000011368% o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11369% chunk can be present to convey binary transparency by naming
11370% one of the colors as transparent. If the image has more
11371% than one transparent color, has semitransparent pixels, or
11372% has an opaque pixel with the same RGB components as the
11373% transparent color, an image is not written.
11374%
11375% o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11376% transparency is permitted, i.e., the alpha sample for
11377% each pixel can have any value from 0 to 65535. The alpha
11378% channel is present even if the image is fully opaque.
11379%
glennrp5830fbc2013-01-27 06:11:45 +000011380% o PNG00: A PNG that inherits its colortype and bit-depth from the input
11381% image, if the input was a PNG, is written. If these values
11382% cannot be found, then "PNG00" falls back to the regular "PNG"
11383% format.
11384%
cristy3ed852e2009-09-05 21:47:34 +000011385% o -define: For more precise control of the PNG output, you can use the
11386% Image options "png:bit-depth" and "png:color-type". These
11387% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000011388% from the application programming interfaces. The options
11389% are case-independent and are converted to lowercase before
11390% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000011391%
11392% png:color-type can be 0, 2, 3, 4, or 6.
11393%
11394% When png:color-type is 0 (Grayscale), png:bit-depth can
11395% be 1, 2, 4, 8, or 16.
11396%
11397% When png:color-type is 2 (RGB), png:bit-depth can
11398% be 8 or 16.
11399%
11400% When png:color-type is 3 (Indexed), png:bit-depth can
11401% be 1, 2, 4, or 8. This refers to the number of bits
11402% used to store the index. The color samples always have
11403% bit-depth 8 in indexed PNG files.
11404%
11405% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11406% png:bit-depth can be 8 or 16.
11407%
glennrpfd164d22013-01-26 21:10:22 +000011408% If the image cannot be written without loss with the
11409% requested bit-depth and color-type, a PNG file will not
11410% be written, a warning will be issued, and the encoder will
11411% return MagickFalse.
glennrp5a39f372011-02-25 04:52:16 +000011412%
cristy3ed852e2009-09-05 21:47:34 +000011413% Since image encoders should not be responsible for the "heavy lifting",
11414% the user should make sure that ImageMagick has already reduced the
11415% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000011416% transparency prior to attempting to write the image with depth, color,
cristy16ea1392012-03-21 20:38:41 +000011417% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000011418%
cristy3ed852e2009-09-05 21:47:34 +000011419% Note that another definition, "png:bit-depth-written" exists, but it
11420% is not intended for external use. It is only used internally by the
11421% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11422%
11423% It is possible to request that the PNG encoder write previously-formatted
11424% ancillary chunks in the output PNG file, using the "-profile" commandline
11425% option as shown below or by setting the profile via a programming
11426% interface:
11427%
11428% -profile PNG-chunk-x:<file>
11429%
11430% where x is a location flag and <file> is a file containing the chunk
11431% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000011432% This encoder will compute the chunk length and CRC, so those must not
11433% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000011434%
11435% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11436% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11437% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000011438% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000011439%
glennrpbb8a7332010-11-13 15:17:35 +000011440% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000011441%
glennrp3241bd02010-12-12 04:36:28 +000011442% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000011443%
glennrpd6afd542010-11-19 01:53:05 +000011444% o 32-bit depth is reduced to 16.
11445% o 16-bit depth is reduced to 8 if all pixels contain samples whose
11446% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000011447% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000011448% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000011449% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000011450% o Grayscale images are reduced to 1, 2, or 4 bit depth if
11451% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000011452% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000011453% o If matte channel is present but only one transparent color is
11454% present, RGB+tRNS is written instead of RGBA
11455% o Opaque matte channel is removed (or added, if color-type 4 or 6
11456% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000011457%
cristy3ed852e2009-09-05 21:47:34 +000011458%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11459*/
11460static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy16ea1392012-03-21 20:38:41 +000011461 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011462{
11463 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011464 excluding,
11465 logging,
11466 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011467 status;
11468
11469 MngInfo
11470 *mng_info;
11471
11472 const char
11473 *value;
11474
11475 int
glennrp5c7cf4e2010-12-24 00:30:00 +000011476 source;
11477
cristy3ed852e2009-09-05 21:47:34 +000011478 /*
11479 Open image file.
11480 */
11481 assert(image_info != (const ImageInfo *) NULL);
11482 assert(image_info->signature == MagickSignature);
11483 assert(image != (Image *) NULL);
11484 assert(image->signature == MagickSignature);
11485 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011486 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011487 /*
11488 Allocate a MngInfo structure.
11489 */
11490 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011491 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000011492
cristy3ed852e2009-09-05 21:47:34 +000011493 if (mng_info == (MngInfo *) NULL)
11494 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011495
cristy3ed852e2009-09-05 21:47:34 +000011496 /*
11497 Initialize members of the MngInfo structure.
11498 */
11499 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11500 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000011501 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011502 have_mng_structure=MagickTrue;
11503
11504 /* See if user has requested a specific PNG subformat */
11505
11506 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11507 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11508 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
glennrpfd164d22013-01-26 21:10:22 +000011509 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11510 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
cristy3ed852e2009-09-05 21:47:34 +000011511
cristy092ec8d2013-04-26 13:46:22 +000011512 value=GetImageOption(image_info,"png:format");
glennrp5a4989d2014-04-23 00:34:09 +000011513 if (value == (char *) NULL)
11514 if (LocaleCompare(image_info->magick,"PNG00") == 0)
glennrpb381a262012-02-11 17:49:49 +000011515
glennrp5a4989d2014-04-23 00:34:09 +000011516 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
glennrpb381a262012-02-11 17:49:49 +000011517 {
glennrpf70c4d22013-03-19 15:26:48 +000011518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11519 " Format=%s",value);
11520
glennrpfd164d22013-01-26 21:10:22 +000011521 mng_info->write_png8 = MagickFalse;
11522 mng_info->write_png24 = MagickFalse;
11523 mng_info->write_png32 = MagickFalse;
11524 mng_info->write_png48 = MagickFalse;
11525 mng_info->write_png64 = MagickFalse;
11526
glennrpb381a262012-02-11 17:49:49 +000011527 if (LocaleCompare(value,"png8") == 0)
glennrpb381a262012-02-11 17:49:49 +000011528 mng_info->write_png8 = MagickTrue;
glennrpb381a262012-02-11 17:49:49 +000011529
11530 else if (LocaleCompare(value,"png24") == 0)
glennrpb381a262012-02-11 17:49:49 +000011531 mng_info->write_png24 = MagickTrue;
glennrpb381a262012-02-11 17:49:49 +000011532
11533 else if (LocaleCompare(value,"png32") == 0)
glennrpb381a262012-02-11 17:49:49 +000011534 mng_info->write_png32 = MagickTrue;
glennrpfd164d22013-01-26 21:10:22 +000011535
11536 else if (LocaleCompare(value,"png48") == 0)
11537 mng_info->write_png48 = MagickTrue;
11538
11539 else if (LocaleCompare(value,"png64") == 0)
11540 mng_info->write_png64 = MagickTrue;
glennrp5830fbc2013-01-27 06:11:45 +000011541
glennrp5a4989d2014-04-23 00:34:09 +000011542 else if ((LocaleCompare(value,"png00") == 0) ||
11543 LocaleCompare(image_info->magick,"PNG00") == 0)
glennrp5830fbc2013-01-27 06:11:45 +000011544 {
glennrp3398b5b2013-05-03 23:10:31 +000011545 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11546 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
glennrpf70c4d22013-03-19 15:26:48 +000011547
11548 if (value != (char *) NULL)
11549 {
11550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11551 " png00 inherited bit depth=%s",value);
11552
11553 if (LocaleCompare(value,"1") == 0)
11554 mng_info->write_png_depth = 1;
11555
glennrp5a4989d2014-04-23 00:34:09 +000011556 else if (LocaleCompare(value,"2") == 0)
glennrpf70c4d22013-03-19 15:26:48 +000011557 mng_info->write_png_depth = 2;
11558
glennrp5a4989d2014-04-23 00:34:09 +000011559 else if (LocaleCompare(value,"4") == 0)
glennrpf70c4d22013-03-19 15:26:48 +000011560 mng_info->write_png_depth = 4;
11561
11562 else if (LocaleCompare(value,"8") == 0)
11563 mng_info->write_png_depth = 8;
11564
11565 else if (LocaleCompare(value,"16") == 0)
11566 mng_info->write_png_depth = 16;
11567 }
11568
glennrp3398b5b2013-05-03 23:10:31 +000011569 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
glennrpf70c4d22013-03-19 15:26:48 +000011570
11571 if (value != (char *) NULL)
11572 {
11573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11574 " png00 inherited color type=%s",value);
11575
11576 if (LocaleCompare(value,"0") == 0)
11577 mng_info->write_png_colortype = 1;
11578
11579 else if (LocaleCompare(value,"2") == 0)
11580 mng_info->write_png_colortype = 3;
11581
11582 else if (LocaleCompare(value,"3") == 0)
11583 mng_info->write_png_colortype = 4;
11584
11585 else if (LocaleCompare(value,"4") == 0)
11586 mng_info->write_png_colortype = 5;
11587
11588 else if (LocaleCompare(value,"6") == 0)
11589 mng_info->write_png_colortype = 7;
11590 }
glennrp5830fbc2013-01-27 06:11:45 +000011591 }
11592 }
11593
cristy3ed852e2009-09-05 21:47:34 +000011594 if (mng_info->write_png8)
11595 {
glennrp9c1eb072010-06-06 22:19:15 +000011596 mng_info->write_png_colortype = /* 3 */ 4;
11597 mng_info->write_png_depth = 8;
11598 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011599 }
11600
11601 if (mng_info->write_png24)
11602 {
glennrp9c1eb072010-06-06 22:19:15 +000011603 mng_info->write_png_colortype = /* 2 */ 3;
11604 mng_info->write_png_depth = 8;
11605 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011606
cristy8a46d822012-08-28 23:32:39 +000011607 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +000011608 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011609
glennrp9c1eb072010-06-06 22:19:15 +000011610 else
cristy16ea1392012-03-21 20:38:41 +000011611 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011612
cristy16ea1392012-03-21 20:38:41 +000011613 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011614 }
11615
11616 if (mng_info->write_png32)
11617 {
glennrp9c1eb072010-06-06 22:19:15 +000011618 mng_info->write_png_colortype = /* 6 */ 7;
11619 mng_info->write_png_depth = 8;
11620 image->depth = 8;
dirk47da46d2014-04-24 18:48:54 +000011621 image->alpha_trait = BlendPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +000011622
glennrp197c8e62014-04-22 23:45:20 +000011623 (void) SetImageType(image,TrueColorMatteType,exception);
cristy16ea1392012-03-21 20:38:41 +000011624 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011625 }
11626
glennrpfd164d22013-01-26 21:10:22 +000011627 if (mng_info->write_png48)
11628 {
11629 mng_info->write_png_colortype = /* 2 */ 3;
11630 mng_info->write_png_depth = 16;
11631 image->depth = 16;
11632
glennrp4dda64f2013-01-26 21:20:24 +000011633 if (image->alpha_trait == BlendPixelTrait)
11634 (void) SetImageType(image,TrueColorMatteType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011635
11636 else
glennrp4dda64f2013-01-26 21:20:24 +000011637 (void) SetImageType(image,TrueColorType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011638
glennrp4dda64f2013-01-26 21:20:24 +000011639 (void) SyncImage(image,exception);
glennrpfd164d22013-01-26 21:10:22 +000011640 }
11641
11642 if (mng_info->write_png64)
11643 {
11644 mng_info->write_png_colortype = /* 6 */ 7;
11645 mng_info->write_png_depth = 16;
11646 image->depth = 16;
dirk47da46d2014-04-24 18:48:54 +000011647 image->alpha_trait = BlendPixelTrait;
glennrpfd164d22013-01-26 21:10:22 +000011648
glennrp197c8e62014-04-22 23:45:20 +000011649 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp4dda64f2013-01-26 21:20:24 +000011650 (void) SyncImage(image,exception);
glennrpfd164d22013-01-26 21:10:22 +000011651 }
11652
cristy092ec8d2013-04-26 13:46:22 +000011653 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011654
cristy3ed852e2009-09-05 21:47:34 +000011655 if (value != (char *) NULL)
11656 {
11657 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011658 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011659
cristy3ed852e2009-09-05 21:47:34 +000011660 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011661 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011662
cristy3ed852e2009-09-05 21:47:34 +000011663 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011664 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011665
cristy3ed852e2009-09-05 21:47:34 +000011666 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011667 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011668
cristy3ed852e2009-09-05 21:47:34 +000011669 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011670 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011671
glennrpbb8a7332010-11-13 15:17:35 +000011672 else
cristy16ea1392012-03-21 20:38:41 +000011673 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011674 GetMagickModule(),CoderWarning,
11675 "ignoring invalid defined png:bit-depth",
11676 "=%s",value);
11677
cristy3ed852e2009-09-05 21:47:34 +000011678 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011680 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011681 }
glennrp0fe50b42010-11-16 03:52:51 +000011682
cristy092ec8d2013-04-26 13:46:22 +000011683 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011684
cristy3ed852e2009-09-05 21:47:34 +000011685 if (value != (char *) NULL)
11686 {
11687 /* We must store colortype+1 because 0 is a valid colortype */
11688 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011689 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011690
cristy16ea1392012-03-21 20:38:41 +000011691 else if (LocaleCompare(value,"1") == 0)
11692 mng_info->write_png_colortype = 2;
11693
cristy3ed852e2009-09-05 21:47:34 +000011694 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011695 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011696
cristy3ed852e2009-09-05 21:47:34 +000011697 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011698 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011699
cristy3ed852e2009-09-05 21:47:34 +000011700 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011701 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011702
cristy3ed852e2009-09-05 21:47:34 +000011703 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011704 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011705
glennrpbb8a7332010-11-13 15:17:35 +000011706 else
cristy16ea1392012-03-21 20:38:41 +000011707 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011708 GetMagickModule(),CoderWarning,
11709 "ignoring invalid defined png:color-type",
11710 "=%s",value);
11711
cristy3ed852e2009-09-05 21:47:34 +000011712 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011714 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011715 }
11716
glennrp0e8ea192010-12-24 18:00:33 +000011717 /* Check for chunks to be excluded:
11718 *
glennrp0dff56c2011-01-29 19:10:02 +000011719 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011720 * listed in the "unused_chunks" array, above.
11721 *
cristy5d6fc9c2011-12-27 03:10:42 +000011722 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011723 * define (in the image properties or in the image artifacts)
11724 * or via a mng_info member. For convenience, in addition
11725 * to or instead of a comma-separated list of chunks, the
11726 * "exclude-chunk" string can be simply "all" or "none".
11727 *
11728 * The exclude-chunk define takes priority over the mng_info.
11729 *
cristy5d6fc9c2011-12-27 03:10:42 +000011730 * A "png:include-chunk" define takes priority over both the
11731 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011732 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011733 * well as a comma-separated list. Chunks that are unknown to
11734 * ImageMagick are always excluded, regardless of their "copy-safe"
11735 * status according to the PNG specification, and even if they
glennrpaa192b12012-01-17 21:35:21 +000011736 * appear in the "include-chunk" list. Such defines appearing among
11737 * the image options take priority over those found among the image
11738 * artifacts.
glennrp0e8ea192010-12-24 18:00:33 +000011739 *
11740 * Finally, all chunks listed in the "unused_chunks" array are
11741 * automatically excluded, regardless of the other instructions
11742 * or lack thereof.
11743 *
11744 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11745 * will not be written and the gAMA chunk will only be written if it
11746 * is not between .45 and .46, or approximately (1.0/2.2).
11747 *
11748 * If you exclude tRNS and the image has transparency, the colortype
11749 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11750 *
11751 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011752 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011753 */
11754
glennrp26f37912010-12-23 16:22:42 +000011755 mng_info->ping_exclude_bKGD=MagickFalse;
11756 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011757 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011758 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11759 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011760 mng_info->ping_exclude_iCCP=MagickFalse;
11761 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11762 mng_info->ping_exclude_oFFs=MagickFalse;
11763 mng_info->ping_exclude_pHYs=MagickFalse;
11764 mng_info->ping_exclude_sRGB=MagickFalse;
11765 mng_info->ping_exclude_tEXt=MagickFalse;
dirkfd6fd072014-10-24 21:10:08 +000011766 mng_info->ping_exclude_tIME=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011767 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011768 mng_info->ping_exclude_vpAg=MagickFalse;
11769 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11770 mng_info->ping_exclude_zTXt=MagickFalse;
11771
glennrp8d3d6e52011-04-19 04:39:51 +000011772 mng_info->ping_preserve_colormap=MagickFalse;
11773
cristy092ec8d2013-04-26 13:46:22 +000011774 value=GetImageOption(image_info,"png:preserve-colormap");
glennrp8d3d6e52011-04-19 04:39:51 +000011775 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011776 value=GetImageArtifact(image,"png:preserve-colormap");
glennrp8d3d6e52011-04-19 04:39:51 +000011777 if (value != NULL)
11778 mng_info->ping_preserve_colormap=MagickTrue;
11779
glennrpecab7d72013-05-14 22:50:32 +000011780 mng_info->ping_preserve_iCCP=MagickFalse;
11781
11782 value=GetImageOption(image_info,"png:preserve-iCCP");
11783 if (value == NULL)
11784 value=GetImageArtifact(image,"png:preserve-iCCP");
11785 if (value != NULL)
11786 mng_info->ping_preserve_iCCP=MagickTrue;
11787
11788 /* These compression-level, compression-strategy, and compression-filter
glennrp18682582011-06-30 18:11:47 +000011789 * defines take precedence over values from the -quality option.
11790 */
cristy092ec8d2013-04-26 13:46:22 +000011791 value=GetImageOption(image_info,"png:compression-level");
glennrp18682582011-06-30 18:11:47 +000011792 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011793 value=GetImageArtifact(image,"png:compression-level");
glennrp18682582011-06-30 18:11:47 +000011794 if (value != NULL)
11795 {
glennrp18682582011-06-30 18:11:47 +000011796 /* We have to add 1 to everything because 0 is a valid input,
11797 * and we want to use 0 (the default) to mean undefined.
11798 */
11799 if (LocaleCompare(value,"0") == 0)
11800 mng_info->write_png_compression_level = 1;
11801
glennrp0ffb95c2012-01-30 21:16:22 +000011802 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011803 mng_info->write_png_compression_level = 2;
11804
11805 else if (LocaleCompare(value,"2") == 0)
11806 mng_info->write_png_compression_level = 3;
11807
11808 else if (LocaleCompare(value,"3") == 0)
11809 mng_info->write_png_compression_level = 4;
11810
11811 else if (LocaleCompare(value,"4") == 0)
11812 mng_info->write_png_compression_level = 5;
11813
11814 else if (LocaleCompare(value,"5") == 0)
11815 mng_info->write_png_compression_level = 6;
11816
11817 else if (LocaleCompare(value,"6") == 0)
11818 mng_info->write_png_compression_level = 7;
11819
11820 else if (LocaleCompare(value,"7") == 0)
11821 mng_info->write_png_compression_level = 8;
11822
11823 else if (LocaleCompare(value,"8") == 0)
11824 mng_info->write_png_compression_level = 9;
11825
11826 else if (LocaleCompare(value,"9") == 0)
11827 mng_info->write_png_compression_level = 10;
11828
11829 else
cristy16ea1392012-03-21 20:38:41 +000011830 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011831 GetMagickModule(),CoderWarning,
11832 "ignoring invalid defined png:compression-level",
11833 "=%s",value);
11834 }
11835
cristy092ec8d2013-04-26 13:46:22 +000011836 value=GetImageOption(image_info,"png:compression-strategy");
glennrp18682582011-06-30 18:11:47 +000011837 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011838 value=GetImageArtifact(image,"png:compression-strategy");
glennrp18682582011-06-30 18:11:47 +000011839 if (value != NULL)
11840 {
glennrp18682582011-06-30 18:11:47 +000011841 if (LocaleCompare(value,"0") == 0)
11842 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11843
11844 else if (LocaleCompare(value,"1") == 0)
11845 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11846
11847 else if (LocaleCompare(value,"2") == 0)
11848 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11849
11850 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011851#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011852 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011853#else
11854 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11855#endif
glennrp18682582011-06-30 18:11:47 +000011856
11857 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011858#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011859 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011860#else
11861 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11862#endif
glennrp18682582011-06-30 18:11:47 +000011863
11864 else
cristy16ea1392012-03-21 20:38:41 +000011865 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011866 GetMagickModule(),CoderWarning,
11867 "ignoring invalid defined png:compression-strategy",
11868 "=%s",value);
11869 }
11870
cristy092ec8d2013-04-26 13:46:22 +000011871 value=GetImageOption(image_info,"png:compression-filter");
glennrp18682582011-06-30 18:11:47 +000011872 if (value == NULL)
cristy8b206ba2013-04-05 01:11:15 +000011873 value=GetImageArtifact(image,"png:compression-filter");
glennrp18682582011-06-30 18:11:47 +000011874 if (value != NULL)
11875 {
glennrp18682582011-06-30 18:11:47 +000011876 /* To do: combinations of filters allowed by libpng
11877 * masks 0x08 through 0xf8
11878 *
11879 * Implement this as a comma-separated list of 0,1,2,3,4,5
11880 * where 5 is a special case meaning PNG_ALL_FILTERS.
11881 */
11882
11883 if (LocaleCompare(value,"0") == 0)
11884 mng_info->write_png_compression_filter = 1;
11885
cristyb19b8122012-10-22 11:03:30 +000011886 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011887 mng_info->write_png_compression_filter = 2;
11888
11889 else if (LocaleCompare(value,"2") == 0)
11890 mng_info->write_png_compression_filter = 3;
11891
11892 else if (LocaleCompare(value,"3") == 0)
11893 mng_info->write_png_compression_filter = 4;
11894
11895 else if (LocaleCompare(value,"4") == 0)
11896 mng_info->write_png_compression_filter = 5;
11897
11898 else if (LocaleCompare(value,"5") == 0)
11899 mng_info->write_png_compression_filter = 6;
11900
glennrp18682582011-06-30 18:11:47 +000011901 else
cristy16ea1392012-03-21 20:38:41 +000011902 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011903 GetMagickModule(),CoderWarning,
11904 "ignoring invalid defined png:compression-filter",
11905 "=%s",value);
glennrp1a506ac2014-07-25 19:49:10 +000011906 }
11907
11908 for (source=0; source<8; source++)
11909 {
11910 value = NULL;
11911
glennrp2dd19062014-07-28 00:22:24 +000011912 if (source == 0)
11913 value=GetImageOption(image_info,"png:exclude-chunks");
11914
11915 if (source == 1)
11916 value=GetImageArtifact(image,"png:exclude-chunks");
11917
11918 if (source == 2)
11919 value=GetImageOption(image_info,"png:exclude-chunk");
11920
11921 if (source == 3)
11922 value=GetImageArtifact(image,"png:exclude-chunk");
11923
11924 if (source == 4)
11925 value=GetImageOption(image_info,"png:include-chunks");
11926
11927 if (source == 5)
11928 value=GetImageArtifact(image,"png:include-chunks");
11929
11930 if (source == 6)
11931 value=GetImageOption(image_info,"png:include-chunk");
11932
11933 if (source == 7)
11934 value=GetImageArtifact(image,"png:include-chunk");
glennrp18682582011-06-30 18:11:47 +000011935
glennrp1a506ac2014-07-25 19:49:10 +000011936 if (value == NULL)
11937 continue;
glennrp03812ae2010-12-24 01:31:34 +000011938
glennrp1a506ac2014-07-25 19:49:10 +000011939 if (source < 4)
11940 excluding = MagickTrue;
glennrp5c7cf4e2010-12-24 00:30:00 +000011941 else
glennrp1a506ac2014-07-25 19:49:10 +000011942 excluding = MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011943
11944 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011945 {
glennrp1a506ac2014-07-25 19:49:10 +000011946 if (source == 0 || source == 2)
11947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11948 " png:exclude-chunk=%s found in image options.\n", value);
11949 else if (source == 1 || source == 3)
glennrp2cc891a2010-12-24 13:44:32 +000011950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11951 " png:exclude-chunk=%s found in image artifacts.\n", value);
glennrp1a506ac2014-07-25 19:49:10 +000011952 else if (source == 4 || source == 6)
glennrp2cc891a2010-12-24 13:44:32 +000011953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a506ac2014-07-25 19:49:10 +000011954 " png:include-chunk=%s found in image options.\n", value);
11955 else /* if (source == 5 || source == 7) */
11956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11957 " png:include-chunk=%s found in image artifacts.\n", value);
glennrp2cc891a2010-12-24 13:44:32 +000011958 }
glennrp03812ae2010-12-24 01:31:34 +000011959
glennrp689efa22014-07-25 00:00:15 +000011960 if (IsOptionMember("all",value) != MagickFalse)
glennrp97d1b572013-05-24 01:41:32 +000011961 {
glennrp1a506ac2014-07-25 19:49:10 +000011962 mng_info->ping_exclude_bKGD=excluding;
11963 mng_info->ping_exclude_cHRM=excluding;
11964 mng_info->ping_exclude_date=excluding;
11965 mng_info->ping_exclude_EXIF=excluding;
11966 mng_info->ping_exclude_gAMA=excluding;
11967 mng_info->ping_exclude_iCCP=excluding;
11968 /* mng_info->ping_exclude_iTXt=excluding; */
11969 mng_info->ping_exclude_oFFs=excluding;
11970 mng_info->ping_exclude_pHYs=excluding;
11971 mng_info->ping_exclude_sRGB=excluding;
11972 mng_info->ping_exclude_tEXt=excluding;
dirkfd6fd072014-10-24 21:10:08 +000011973 mng_info->ping_exclude_tIME=excluding;
glennrp1a506ac2014-07-25 19:49:10 +000011974 mng_info->ping_exclude_tRNS=excluding;
11975 mng_info->ping_exclude_vpAg=excluding;
11976 mng_info->ping_exclude_zCCP=excluding;
11977 mng_info->ping_exclude_zTXt=excluding;
glennrp03812ae2010-12-24 01:31:34 +000011978 }
glennrp280283d2014-08-01 15:02:04 +000011979
glennrp1a506ac2014-07-25 19:49:10 +000011980 if (IsOptionMember("none",value) != MagickFalse)
glennrpacba0042010-12-24 14:27:26 +000011981 {
cristya20c8fe2014-08-14 11:55:11 +000011982 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
11983 MagickTrue;
11984 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
11985 MagickTrue;
11986 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
11987 MagickTrue;
11988 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
11989 MagickTrue;
11990 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
11991 MagickTrue;
11992 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
11993 MagickTrue;
glennrp1a506ac2014-07-25 19:49:10 +000011994 /* mng_info->ping_exclude_iTXt=!excluding; */
cristya20c8fe2014-08-14 11:55:11 +000011995 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
11996 MagickTrue;
11997 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
11998 MagickTrue;
11999 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12000 MagickTrue;
12001 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12002 MagickTrue;
dirkfd6fd072014-10-24 21:10:08 +000012003 mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12004 MagickTrue;
cristya20c8fe2014-08-14 11:55:11 +000012005 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12006 MagickTrue;
12007 mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12008 MagickTrue;
12009 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12010 MagickTrue;
12011 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12012 MagickTrue;
glennrpacba0042010-12-24 14:27:26 +000012013 }
12014
glennrp1a506ac2014-07-25 19:49:10 +000012015 if (IsOptionMember("bkgd",value) != MagickFalse)
12016 mng_info->ping_exclude_bKGD=excluding;
glennrp26f37912010-12-23 16:22:42 +000012017
glennrp1a506ac2014-07-25 19:49:10 +000012018 if (IsOptionMember("chrm",value) != MagickFalse)
12019 mng_info->ping_exclude_cHRM=excluding;
glennrp03812ae2010-12-24 01:31:34 +000012020
glennrp1a506ac2014-07-25 19:49:10 +000012021 if (IsOptionMember("date",value) != MagickFalse)
12022 mng_info->ping_exclude_date=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012023
glennrp1a506ac2014-07-25 19:49:10 +000012024 if (IsOptionMember("exif",value) != MagickFalse)
12025 mng_info->ping_exclude_EXIF=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012026
glennrp1a506ac2014-07-25 19:49:10 +000012027 if (IsOptionMember("gama",value) != MagickFalse)
12028 mng_info->ping_exclude_gAMA=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012029
glennrp1a506ac2014-07-25 19:49:10 +000012030 if (IsOptionMember("iccp",value) != MagickFalse)
12031 mng_info->ping_exclude_iCCP=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012032
glennrp689efa22014-07-25 00:00:15 +000012033#if 0
glennrp1a506ac2014-07-25 19:49:10 +000012034 if (IsOptionMember("itxt",value) != MagickFalse)
12035 mng_info->ping_exclude_iTXt=excluding;
glennrp689efa22014-07-25 00:00:15 +000012036#endif
glennrp2cc891a2010-12-24 13:44:32 +000012037
glennrp1a506ac2014-07-25 19:49:10 +000012038 if (IsOptionMember("offs",value) != MagickFalse)
12039 mng_info->ping_exclude_oFFs=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012040
glennrp1a506ac2014-07-25 19:49:10 +000012041 if (IsOptionMember("phys",value) != MagickFalse)
12042 mng_info->ping_exclude_pHYs=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012043
glennrp1a506ac2014-07-25 19:49:10 +000012044 if (IsOptionMember("srgb",value) != MagickFalse)
12045 mng_info->ping_exclude_sRGB=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012046
glennrp1a506ac2014-07-25 19:49:10 +000012047 if (IsOptionMember("text",value) != MagickFalse)
12048 mng_info->ping_exclude_tEXt=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012049
dirkfd6fd072014-10-24 21:10:08 +000012050 if (IsOptionMember("time",value) != MagickFalse)
12051 mng_info->ping_exclude_tIME=excluding;
12052
glennrp1a506ac2014-07-25 19:49:10 +000012053 if (IsOptionMember("trns",value) != MagickFalse)
12054 mng_info->ping_exclude_tRNS=excluding;
glennrpa1e3b7b2010-12-24 16:37:33 +000012055
glennrp1a506ac2014-07-25 19:49:10 +000012056 if (IsOptionMember("vpag",value) != MagickFalse)
12057 mng_info->ping_exclude_vpAg=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012058
glennrp1a506ac2014-07-25 19:49:10 +000012059 if (IsOptionMember("zccp",value) != MagickFalse)
12060 mng_info->ping_exclude_zCCP=excluding;
glennrp2cc891a2010-12-24 13:44:32 +000012061
glennrp1a506ac2014-07-25 19:49:10 +000012062 if (IsOptionMember("ztxt",value) != MagickFalse)
12063 mng_info->ping_exclude_zTXt=excluding;
glennrp26f37912010-12-23 16:22:42 +000012064 }
12065
glennrp1a506ac2014-07-25 19:49:10 +000012066 if (logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000012067 {
12068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000012069 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000012070 if (mng_info->ping_exclude_bKGD != MagickFalse)
12071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12072 " bKGD");
12073 if (mng_info->ping_exclude_cHRM != MagickFalse)
12074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12075 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000012076 if (mng_info->ping_exclude_date != MagickFalse)
12077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12078 " date");
glennrp26f37912010-12-23 16:22:42 +000012079 if (mng_info->ping_exclude_EXIF != MagickFalse)
12080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12081 " EXIF");
12082 if (mng_info->ping_exclude_gAMA != MagickFalse)
12083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12084 " gAMA");
12085 if (mng_info->ping_exclude_iCCP != MagickFalse)
12086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12087 " iCCP");
glennrp689efa22014-07-25 00:00:15 +000012088#if 0
glennrp26f37912010-12-23 16:22:42 +000012089 if (mng_info->ping_exclude_iTXt != MagickFalse)
12090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12091 " iTXt");
glennrp689efa22014-07-25 00:00:15 +000012092#endif
12093
glennrp26f37912010-12-23 16:22:42 +000012094 if (mng_info->ping_exclude_oFFs != MagickFalse)
12095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12096 " oFFs");
12097 if (mng_info->ping_exclude_pHYs != MagickFalse)
12098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12099 " pHYs");
12100 if (mng_info->ping_exclude_sRGB != MagickFalse)
12101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12102 " sRGB");
12103 if (mng_info->ping_exclude_tEXt != MagickFalse)
12104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12105 " tEXt");
dirkfd6fd072014-10-24 21:10:08 +000012106 if (mng_info->ping_exclude_tIME != MagickFalse)
12107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12108 " tIME");
glennrpa1e3b7b2010-12-24 16:37:33 +000012109 if (mng_info->ping_exclude_tRNS != MagickFalse)
12110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12111 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000012112 if (mng_info->ping_exclude_vpAg != MagickFalse)
12113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12114 " vpAg");
12115 if (mng_info->ping_exclude_zCCP != MagickFalse)
12116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12117 " zCCP");
12118 if (mng_info->ping_exclude_zTXt != MagickFalse)
12119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12120 " zTXt");
12121 }
12122
glennrpb9cfe272010-12-21 15:08:06 +000012123 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000012124
cristy16ea1392012-03-21 20:38:41 +000012125 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012126
12127 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012128
cristy3ed852e2009-09-05 21:47:34 +000012129 if (logging != MagickFalse)
12130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012131
cristy3ed852e2009-09-05 21:47:34 +000012132 return(status);
12133}
12134
12135#if defined(JNG_SUPPORTED)
12136
12137/* Write one JNG image */
12138static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +000012139 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012140{
12141 Image
12142 *jpeg_image;
12143
12144 ImageInfo
12145 *jpeg_image_info;
12146
12147 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000012148 logging,
cristy3ed852e2009-09-05 21:47:34 +000012149 status;
12150
12151 size_t
12152 length;
12153
12154 unsigned char
12155 *blob,
12156 chunk[80],
12157 *p;
12158
12159 unsigned int
12160 jng_alpha_compression_method,
12161 jng_alpha_sample_depth,
12162 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000012163 transparent;
12164
cristybb503372010-05-27 20:51:26 +000012165 size_t
glennrp59575fa2011-12-31 21:31:39 +000012166 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000012167 jng_quality;
12168
12169 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000012170 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012171
12172 blob=(unsigned char *) NULL;
12173 jpeg_image=(Image *) NULL;
12174 jpeg_image_info=(ImageInfo *) NULL;
12175
12176 status=MagickTrue;
12177 transparent=image_info->type==GrayscaleMatteType ||
cristy8a46d822012-08-28 23:32:39 +000012178 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +000012179
glennrp4b917592013-06-20 19:53:59 +000012180 jng_alpha_sample_depth = 0;
12181
glennrp59575fa2011-12-31 21:31:39 +000012182 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12183
12184 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12185
glennrp750105b2012-04-25 16:20:45 +000012186 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
glennrp59575fa2011-12-31 21:31:39 +000012187 image_info->quality;
12188
12189 if (jng_alpha_quality >= 1000)
12190 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000012191
glennrpd0ee5a22014-03-28 14:28:47 +000012192 length=0;
12193
cristy3ed852e2009-09-05 21:47:34 +000012194 if (transparent)
12195 {
12196 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000012197
cristy3ed852e2009-09-05 21:47:34 +000012198 /* Create JPEG blob, image, and image_info */
12199 if (logging != MagickFalse)
12200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +000012201 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000012202
cristy3ed852e2009-09-05 21:47:34 +000012203 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000012204
cristy3ed852e2009-09-05 21:47:34 +000012205 if (jpeg_image_info == (ImageInfo *) NULL)
12206 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000012207
cristy3ed852e2009-09-05 21:47:34 +000012208 if (logging != MagickFalse)
12209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12210 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000012211
cristy16ea1392012-03-21 20:38:41 +000012212 jpeg_image=SeparateImage(image,AlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +000012213 if (jpeg_image == (Image *) NULL)
12214 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12215 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy8a46d822012-08-28 23:32:39 +000012216 jpeg_image->alpha_trait=UndefinedPixelTrait;
glennrp8f77fdc2012-03-21 15:16:37 +000012217 jpeg_image->quality=jng_alpha_quality;
cristy16ea1392012-03-21 20:38:41 +000012218 jpeg_image_info->type=GrayscaleType;
12219 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000012220 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012221 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000012222 "%s",jpeg_image->filename);
12223 }
glennrp59575fa2011-12-31 21:31:39 +000012224 else
12225 {
12226 jng_alpha_compression_method=0;
12227 jng_color_type=10;
12228 jng_alpha_sample_depth=0;
12229 }
cristy3ed852e2009-09-05 21:47:34 +000012230
12231 /* To do: check bit depth of PNG alpha channel */
12232
12233 /* Check if image is grayscale. */
12234 if (image_info->type != TrueColorMatteType && image_info->type !=
cristy7fb26522012-06-21 13:02:48 +000012235 TrueColorType && IsImageGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000012236 jng_color_type-=2;
12237
glennrp59575fa2011-12-31 21:31:39 +000012238 if (logging != MagickFalse)
12239 {
12240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12241 " JNG Quality = %d",(int) jng_quality);
12242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12243 " JNG Color Type = %d",jng_color_type);
12244 if (transparent)
12245 {
12246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12247 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12249 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12251 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12252 }
12253 }
12254
cristy3ed852e2009-09-05 21:47:34 +000012255 if (transparent)
12256 {
12257 if (jng_alpha_compression_method==0)
12258 {
12259 const char
12260 *value;
12261
cristy16ea1392012-03-21 20:38:41 +000012262 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000012263 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012264 exception);
cristy3ed852e2009-09-05 21:47:34 +000012265 if (logging != MagickFalse)
12266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12267 " Creating PNG blob.");
cristy3ed852e2009-09-05 21:47:34 +000012268
12269 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12270 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12271 jpeg_image_info->interlace=NoInterlace;
12272
glennrpcc5d45b2012-01-06 04:06:10 +000012273 /* Exclude all ancillary chunks */
12274 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12275
cristy3ed852e2009-09-05 21:47:34 +000012276 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000012277 exception);
cristy3ed852e2009-09-05 21:47:34 +000012278
12279 /* Retrieve sample depth used */
cristy16ea1392012-03-21 20:38:41 +000012280 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000012281 if (value != (char *) NULL)
12282 jng_alpha_sample_depth= (unsigned int) value[0];
12283 }
12284 else
12285 {
cristy16ea1392012-03-21 20:38:41 +000012286 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000012287
12288 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012289 exception);
cristy3ed852e2009-09-05 21:47:34 +000012290
12291 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12292 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12293 jpeg_image_info->interlace=NoInterlace;
12294 if (logging != MagickFalse)
12295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12296 " Creating blob.");
12297 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000012298 exception);
cristy3ed852e2009-09-05 21:47:34 +000012299 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000012300
cristy3ed852e2009-09-05 21:47:34 +000012301 if (logging != MagickFalse)
12302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012303 " Successfully read jpeg_image into a blob, length=%.20g.",
12304 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012305
12306 }
12307 /* Destroy JPEG image and image_info */
12308 jpeg_image=DestroyImage(jpeg_image);
12309 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12310 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12311 }
12312
12313 /* Write JHDR chunk */
12314 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12315 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000012316 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000012317 PNGLong(chunk+4,(png_uint_32) image->columns);
12318 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012319 chunk[12]=jng_color_type;
12320 chunk[13]=8; /* sample depth */
12321 chunk[14]=8; /*jng_image_compression_method */
12322 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12323 chunk[16]=jng_alpha_sample_depth;
12324 chunk[17]=jng_alpha_compression_method;
12325 chunk[18]=0; /*jng_alpha_filter_method */
12326 chunk[19]=0; /*jng_alpha_interlace_method */
12327 (void) WriteBlob(image,20,chunk);
12328 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12329 if (logging != MagickFalse)
12330 {
12331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000012332 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000012333
cristy3ed852e2009-09-05 21:47:34 +000012334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000012335 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000012336
cristy3ed852e2009-09-05 21:47:34 +000012337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12338 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000012339
cristy3ed852e2009-09-05 21:47:34 +000012340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12341 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000012342
cristy3ed852e2009-09-05 21:47:34 +000012343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12344 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000012345
cristy3ed852e2009-09-05 21:47:34 +000012346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12347 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000012348
cristy3ed852e2009-09-05 21:47:34 +000012349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12350 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000012351
cristy3ed852e2009-09-05 21:47:34 +000012352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12353 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000012354
cristy3ed852e2009-09-05 21:47:34 +000012355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12356 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000012357
cristy3ed852e2009-09-05 21:47:34 +000012358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12359 " JNG alpha interlace:%5d",0);
12360 }
12361
glennrp0fe50b42010-11-16 03:52:51 +000012362 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000012363 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000012364
12365 /*
12366 Write leading ancillary chunks
12367 */
12368
12369 if (transparent)
12370 {
12371 /*
12372 Write JNG bKGD chunk
12373 */
12374
12375 unsigned char
12376 blue,
12377 green,
12378 red;
12379
cristybb503372010-05-27 20:51:26 +000012380 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012381 num_bytes;
12382
12383 if (jng_color_type == 8 || jng_color_type == 12)
12384 num_bytes=6L;
12385 else
12386 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000012387 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012388 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012389 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012390 red=ScaleQuantumToChar(image->background_color.red);
12391 green=ScaleQuantumToChar(image->background_color.green);
12392 blue=ScaleQuantumToChar(image->background_color.blue);
12393 *(chunk+4)=0;
12394 *(chunk+5)=red;
12395 *(chunk+6)=0;
12396 *(chunk+7)=green;
12397 *(chunk+8)=0;
12398 *(chunk+9)=blue;
12399 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12400 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12401 }
12402
12403 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12404 {
12405 /*
12406 Write JNG sRGB chunk
12407 */
12408 (void) WriteBlobMSBULong(image,1L);
12409 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012410 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012411
cristy3ed852e2009-09-05 21:47:34 +000012412 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012413 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012414 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012415 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012416
cristy3ed852e2009-09-05 21:47:34 +000012417 else
glennrpe610a072010-08-05 17:08:46 +000012418 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012419 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012420 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012421
cristy3ed852e2009-09-05 21:47:34 +000012422 (void) WriteBlob(image,5,chunk);
12423 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12424 }
12425 else
12426 {
12427 if (image->gamma != 0.0)
12428 {
12429 /*
12430 Write JNG gAMA chunk
12431 */
12432 (void) WriteBlobMSBULong(image,4L);
12433 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012434 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012435 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012436 (void) WriteBlob(image,8,chunk);
12437 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12438 }
glennrp0fe50b42010-11-16 03:52:51 +000012439
cristy3ed852e2009-09-05 21:47:34 +000012440 if ((mng_info->equal_chrms == MagickFalse) &&
12441 (image->chromaticity.red_primary.x != 0.0))
12442 {
12443 PrimaryInfo
12444 primary;
12445
12446 /*
12447 Write JNG cHRM chunk
12448 */
12449 (void) WriteBlobMSBULong(image,32L);
12450 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012451 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012452 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012453 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12454 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012455 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012456 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12457 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012458 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012459 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12460 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012461 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012462 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12463 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012464 (void) WriteBlob(image,36,chunk);
12465 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12466 }
12467 }
glennrp0fe50b42010-11-16 03:52:51 +000012468
cristy16ea1392012-03-21 20:38:41 +000012469 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012470 {
12471 /*
12472 Write JNG pHYs chunk
12473 */
12474 (void) WriteBlobMSBULong(image,9L);
12475 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012476 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000012477 if (image->units == PixelsPerInchResolution)
12478 {
cristy35ef8242010-06-03 16:24:13 +000012479 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012480 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012481
cristy35ef8242010-06-03 16:24:13 +000012482 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012483 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012484
cristy3ed852e2009-09-05 21:47:34 +000012485 chunk[12]=1;
12486 }
glennrp0fe50b42010-11-16 03:52:51 +000012487
cristy3ed852e2009-09-05 21:47:34 +000012488 else
12489 {
12490 if (image->units == PixelsPerCentimeterResolution)
12491 {
cristy35ef8242010-06-03 16:24:13 +000012492 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012493 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012494
cristy35ef8242010-06-03 16:24:13 +000012495 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012496 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012497
cristy3ed852e2009-09-05 21:47:34 +000012498 chunk[12]=1;
12499 }
glennrp0fe50b42010-11-16 03:52:51 +000012500
cristy3ed852e2009-09-05 21:47:34 +000012501 else
12502 {
cristy16ea1392012-03-21 20:38:41 +000012503 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12504 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012505 chunk[12]=0;
12506 }
12507 }
12508 (void) WriteBlob(image,13,chunk);
12509 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12510 }
12511
12512 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12513 {
12514 /*
12515 Write JNG oFFs chunk
12516 */
12517 (void) WriteBlobMSBULong(image,9L);
12518 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000012519 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000012520 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12521 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000012522 chunk[12]=0;
12523 (void) WriteBlob(image,13,chunk);
12524 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12525 }
12526 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12527 {
12528 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12529 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000012530 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012531 PNGLong(chunk+4,(png_uint_32) image->page.width);
12532 PNGLong(chunk+8,(png_uint_32) image->page.height);
12533 chunk[12]=0; /* unit = pixels */
12534 (void) WriteBlob(image,13,chunk);
12535 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12536 }
12537
12538
12539 if (transparent)
12540 {
12541 if (jng_alpha_compression_method==0)
12542 {
cristybb503372010-05-27 20:51:26 +000012543 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012544 i;
12545
cristyfa6de8c2014-05-18 16:34:44 +000012546 size_t
cristy3ed852e2009-09-05 21:47:34 +000012547 len;
12548
12549 /* Write IDAT chunk header */
12550 if (logging != MagickFalse)
12551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012552 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012553 length);
cristy3ed852e2009-09-05 21:47:34 +000012554
12555 /* Copy IDAT chunks */
12556 len=0;
12557 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012558 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012559 {
cristy5389e5a2014-05-28 14:22:43 +000012560 len=(size_t) (*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
cristy3ed852e2009-09-05 21:47:34 +000012561 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012562
cristy3ed852e2009-09-05 21:47:34 +000012563 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12564 {
12565 /* Found an IDAT chunk. */
cristyfa6de8c2014-05-18 16:34:44 +000012566 (void) WriteBlobMSBULong(image,len);
12567 LogPNGChunk(logging,mng_IDAT,len);
12568 (void) WriteBlob(image,len+4,p);
12569 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
cristy3ed852e2009-09-05 21:47:34 +000012570 }
glennrp0fe50b42010-11-16 03:52:51 +000012571
cristy3ed852e2009-09-05 21:47:34 +000012572 else
12573 {
12574 if (logging != MagickFalse)
12575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012576 " Skipping %c%c%c%c chunk, length=%.20g.",
12577 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012578 }
12579 p+=(8+len);
12580 }
12581 }
12582 else
12583 {
12584 /* Write JDAA chunk header */
12585 if (logging != MagickFalse)
12586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012587 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012588 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012589 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012590 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012591 /* Write JDAT chunk(s) data */
12592 (void) WriteBlob(image,4,chunk);
12593 (void) WriteBlob(image,length,blob);
12594 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12595 (uInt) length));
12596 }
12597 blob=(unsigned char *) RelinquishMagickMemory(blob);
12598 }
12599
12600 /* Encode image as a JPEG blob */
12601 if (logging != MagickFalse)
12602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12603 " Creating jpeg_image_info.");
12604 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12605 if (jpeg_image_info == (ImageInfo *) NULL)
12606 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12607
12608 if (logging != MagickFalse)
12609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12610 " Creating jpeg_image.");
12611
cristy16ea1392012-03-21 20:38:41 +000012612 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012613 if (jpeg_image == (Image *) NULL)
12614 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12615 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12616
12617 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012618 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012619 jpeg_image->filename);
12620
12621 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012622 exception);
cristy3ed852e2009-09-05 21:47:34 +000012623
12624 if (logging != MagickFalse)
12625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012626 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12627 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012628
12629 if (jng_color_type == 8 || jng_color_type == 12)
12630 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012631
glennrp59575fa2011-12-31 21:31:39 +000012632 jpeg_image_info->quality=jng_quality;
12633 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012634 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12635 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012636
cristy3ed852e2009-09-05 21:47:34 +000012637 if (logging != MagickFalse)
12638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12639 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012640
cristy16ea1392012-03-21 20:38:41 +000012641 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012642
cristy3ed852e2009-09-05 21:47:34 +000012643 if (logging != MagickFalse)
12644 {
12645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012646 " Successfully read jpeg_image into a blob, length=%.20g.",
12647 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012648
12649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012650 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012651 }
glennrp0fe50b42010-11-16 03:52:51 +000012652
cristy3ed852e2009-09-05 21:47:34 +000012653 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012654 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012655 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012656 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012657 (void) WriteBlob(image,4,chunk);
12658 (void) WriteBlob(image,length,blob);
12659 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12660
12661 jpeg_image=DestroyImage(jpeg_image);
12662 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12663 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12664 blob=(unsigned char *) RelinquishMagickMemory(blob);
12665
12666 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012667 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012668
12669 /* Write IEND chunk */
12670 (void) WriteBlobMSBULong(image,0L);
12671 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012672 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012673 (void) WriteBlob(image,4,chunk);
12674 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12675
12676 if (logging != MagickFalse)
12677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12678 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012679
cristy3ed852e2009-09-05 21:47:34 +000012680 return(status);
12681}
12682
12683
12684/*
12685%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12686% %
12687% %
12688% %
12689% W r i t e J N G I m a g e %
12690% %
12691% %
12692% %
12693%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12694%
12695% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12696%
12697% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12698%
12699% The format of the WriteJNGImage method is:
12700%
cristy16ea1392012-03-21 20:38:41 +000012701% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12702% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012703%
12704% A description of each parameter follows:
12705%
12706% o image_info: the image info.
12707%
12708% o image: The image.
12709%
cristy16ea1392012-03-21 20:38:41 +000012710% o exception: return any errors or warnings in this structure.
12711%
cristy3ed852e2009-09-05 21:47:34 +000012712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12713*/
cristy16ea1392012-03-21 20:38:41 +000012714static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12715 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012716{
12717 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012718 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012719 logging,
cristy3ed852e2009-09-05 21:47:34 +000012720 status;
12721
12722 MngInfo
12723 *mng_info;
12724
cristy3ed852e2009-09-05 21:47:34 +000012725 /*
12726 Open image file.
12727 */
12728 assert(image_info != (const ImageInfo *) NULL);
12729 assert(image_info->signature == MagickSignature);
12730 assert(image != (Image *) NULL);
12731 assert(image->signature == MagickSignature);
12732 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012733 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012734 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012735 if (status == MagickFalse)
12736 return(status);
12737
12738 /*
12739 Allocate a MngInfo structure.
12740 */
12741 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012742 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012743 if (mng_info == (MngInfo *) NULL)
12744 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12745 /*
12746 Initialize members of the MngInfo structure.
12747 */
12748 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12749 mng_info->image=image;
12750 have_mng_structure=MagickTrue;
12751
12752 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12753
cristy16ea1392012-03-21 20:38:41 +000012754 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012755 (void) CloseBlob(image);
12756
12757 (void) CatchImageException(image);
12758 MngInfoFreeStruct(mng_info,&have_mng_structure);
12759 if (logging != MagickFalse)
12760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12761 return(status);
12762}
12763#endif
12764
cristy16ea1392012-03-21 20:38:41 +000012765static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12766 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012767{
12768 const char
12769 *option;
12770
12771 Image
12772 *next_image;
12773
12774 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012775 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012776 status;
12777
glennrp03812ae2010-12-24 01:31:34 +000012778 volatile MagickBooleanType
12779 logging;
12780
cristy3ed852e2009-09-05 21:47:34 +000012781 MngInfo
12782 *mng_info;
12783
12784 int
cristy3ed852e2009-09-05 21:47:34 +000012785 image_count,
12786 need_iterations,
12787 need_matte;
12788
12789 volatile int
12790#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12791 defined(PNG_MNG_FEATURES_SUPPORTED)
12792 need_local_plte,
12793#endif
12794 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012795 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012796 use_global_plte;
12797
cristybb503372010-05-27 20:51:26 +000012798 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012799 i;
12800
12801 unsigned char
12802 chunk[800];
12803
12804 volatile unsigned int
12805 write_jng,
12806 write_mng;
12807
cristybb503372010-05-27 20:51:26 +000012808 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012809 scene;
12810
cristybb503372010-05-27 20:51:26 +000012811 size_t
cristy3ed852e2009-09-05 21:47:34 +000012812 final_delay=0,
12813 initial_delay;
12814
glennrpd5045b42010-03-24 12:40:35 +000012815#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012816 if (image_info->verbose)
12817 printf("Your PNG library (libpng-%s) is rather old.\n",
12818 PNG_LIBPNG_VER_STRING);
12819#endif
12820
12821 /*
12822 Open image file.
12823 */
12824 assert(image_info != (const ImageInfo *) NULL);
12825 assert(image_info->signature == MagickSignature);
12826 assert(image != (Image *) NULL);
12827 assert(image->signature == MagickSignature);
12828 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012829 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012830 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012831 if (status == MagickFalse)
12832 return(status);
12833
12834 /*
12835 Allocate a MngInfo structure.
12836 */
12837 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012838 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012839 if (mng_info == (MngInfo *) NULL)
12840 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12841 /*
12842 Initialize members of the MngInfo structure.
12843 */
12844 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12845 mng_info->image=image;
12846 have_mng_structure=MagickTrue;
12847 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12848
12849 /*
12850 * See if user has requested a specific PNG subformat to be used
12851 * for all of the PNGs in the MNG being written, e.g.,
12852 *
12853 * convert *.png png8:animation.mng
12854 *
12855 * To do: check -define png:bit_depth and png:color_type as well,
12856 * or perhaps use mng:bit_depth and mng:color_type instead for
12857 * global settings.
12858 */
12859
12860 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12861 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12862 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12863
12864 write_jng=MagickFalse;
12865 if (image_info->compression == JPEGCompression)
12866 write_jng=MagickTrue;
12867
12868 mng_info->adjoin=image_info->adjoin &&
12869 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12870
cristy3ed852e2009-09-05 21:47:34 +000012871 if (logging != MagickFalse)
12872 {
12873 /* Log some info about the input */
12874 Image
12875 *p;
12876
12877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp2dd19062014-07-28 00:22:24 +000012878 " Checking input image(s)\n"
12879 " Image_info depth: %.20g, Type: %d",
12880 (double) image_info->depth, image_info->type);
cristy3ed852e2009-09-05 21:47:34 +000012881
12882 scene=0;
12883 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12884 {
glennrp0fe50b42010-11-16 03:52:51 +000012885
cristy3ed852e2009-09-05 21:47:34 +000012886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp280283d2014-08-01 15:02:04 +000012887 " Scene: %.20g\n, Image depth: %.20g",
12888 (double) scene++, (double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012889
cristydc2d3272013-02-12 14:00:44 +000012890 if (p->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000012891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12892 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012893
cristy3ed852e2009-09-05 21:47:34 +000012894 else
12895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12896 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012897
cristy3ed852e2009-09-05 21:47:34 +000012898 if (p->storage_class == PseudoClass)
12899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12900 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012901
cristy3ed852e2009-09-05 21:47:34 +000012902 else
12903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12904 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012905
cristy3ed852e2009-09-05 21:47:34 +000012906 if (p->colors)
12907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012908 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012909
cristy3ed852e2009-09-05 21:47:34 +000012910 else
12911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12912 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012913
cristy3ed852e2009-09-05 21:47:34 +000012914 if (mng_info->adjoin == MagickFalse)
12915 break;
12916 }
12917 }
12918
cristy3ed852e2009-09-05 21:47:34 +000012919 use_global_plte=MagickFalse;
12920 all_images_are_gray=MagickFalse;
12921#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12922 need_local_plte=MagickTrue;
12923#endif
12924 need_defi=MagickFalse;
12925 need_matte=MagickFalse;
12926 mng_info->framing_mode=1;
12927 mng_info->old_framing_mode=1;
12928
12929 if (write_mng)
12930 if (image_info->page != (char *) NULL)
12931 {
12932 /*
12933 Determine image bounding box.
12934 */
12935 SetGeometry(image,&mng_info->page);
12936 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12937 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12938 }
12939 if (write_mng)
12940 {
12941 unsigned int
12942 need_geom;
12943
12944 unsigned short
12945 red,
12946 green,
12947 blue;
12948
12949 mng_info->page=image->page;
12950 need_geom=MagickTrue;
12951 if (mng_info->page.width || mng_info->page.height)
12952 need_geom=MagickFalse;
12953 /*
12954 Check all the scenes.
12955 */
12956 initial_delay=image->delay;
12957 need_iterations=MagickFalse;
12958 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12959 mng_info->equal_physs=MagickTrue,
12960 mng_info->equal_gammas=MagickTrue;
12961 mng_info->equal_srgbs=MagickTrue;
12962 mng_info->equal_backgrounds=MagickTrue;
12963 image_count=0;
12964#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12965 defined(PNG_MNG_FEATURES_SUPPORTED)
12966 all_images_are_gray=MagickTrue;
12967 mng_info->equal_palettes=MagickFalse;
12968 need_local_plte=MagickFalse;
12969#endif
12970 for (next_image=image; next_image != (Image *) NULL; )
12971 {
12972 if (need_geom)
12973 {
12974 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12975 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012976
cristy3ed852e2009-09-05 21:47:34 +000012977 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12978 mng_info->page.height=next_image->rows+next_image->page.y;
12979 }
glennrp0fe50b42010-11-16 03:52:51 +000012980
cristy3ed852e2009-09-05 21:47:34 +000012981 if (next_image->page.x || next_image->page.y)
12982 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012983
cristydc2d3272013-02-12 14:00:44 +000012984 if (next_image->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000012985 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012986
cristy3ed852e2009-09-05 21:47:34 +000012987 if ((int) next_image->dispose >= BackgroundDispose)
cristydc2d3272013-02-12 14:00:44 +000012988 if ((next_image->alpha_trait == BlendPixelTrait) ||
12989 next_image->page.x || next_image->page.y ||
cristy3ed852e2009-09-05 21:47:34 +000012990 ((next_image->columns < mng_info->page.width) &&
12991 (next_image->rows < mng_info->page.height)))
12992 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012993
cristy3ed852e2009-09-05 21:47:34 +000012994 if (next_image->iterations)
12995 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012996
cristy3ed852e2009-09-05 21:47:34 +000012997 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012998
cristy3ed852e2009-09-05 21:47:34 +000012999 if (final_delay != initial_delay || final_delay > 1UL*
13000 next_image->ticks_per_second)
13001 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000013002
cristy3ed852e2009-09-05 21:47:34 +000013003#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13004 defined(PNG_MNG_FEATURES_SUPPORTED)
13005 /*
13006 check for global palette possibility.
13007 */
cristy8a46d822012-08-28 23:32:39 +000013008 if (image->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000013009 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013010
cristy3ed852e2009-09-05 21:47:34 +000013011 if (need_local_plte == 0)
13012 {
cristy7fb26522012-06-21 13:02:48 +000013013 if (IsImageGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000013014 all_images_are_gray=MagickFalse;
13015 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13016 if (use_global_plte == 0)
13017 use_global_plte=mng_info->equal_palettes;
13018 need_local_plte=!mng_info->equal_palettes;
13019 }
13020#endif
13021 if (GetNextImageInList(next_image) != (Image *) NULL)
13022 {
13023 if (next_image->background_color.red !=
13024 next_image->next->background_color.red ||
13025 next_image->background_color.green !=
13026 next_image->next->background_color.green ||
13027 next_image->background_color.blue !=
13028 next_image->next->background_color.blue)
13029 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013030
cristy3ed852e2009-09-05 21:47:34 +000013031 if (next_image->gamma != next_image->next->gamma)
13032 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013033
cristy3ed852e2009-09-05 21:47:34 +000013034 if (next_image->rendering_intent !=
13035 next_image->next->rendering_intent)
13036 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013037
cristy3ed852e2009-09-05 21:47:34 +000013038 if ((next_image->units != next_image->next->units) ||
cristy16ea1392012-03-21 20:38:41 +000013039 (next_image->resolution.x != next_image->next->resolution.x) ||
13040 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000013041 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000013042
cristy3ed852e2009-09-05 21:47:34 +000013043 if (mng_info->equal_chrms)
13044 {
13045 if (next_image->chromaticity.red_primary.x !=
13046 next_image->next->chromaticity.red_primary.x ||
13047 next_image->chromaticity.red_primary.y !=
13048 next_image->next->chromaticity.red_primary.y ||
13049 next_image->chromaticity.green_primary.x !=
13050 next_image->next->chromaticity.green_primary.x ||
13051 next_image->chromaticity.green_primary.y !=
13052 next_image->next->chromaticity.green_primary.y ||
13053 next_image->chromaticity.blue_primary.x !=
13054 next_image->next->chromaticity.blue_primary.x ||
13055 next_image->chromaticity.blue_primary.y !=
13056 next_image->next->chromaticity.blue_primary.y ||
13057 next_image->chromaticity.white_point.x !=
13058 next_image->next->chromaticity.white_point.x ||
13059 next_image->chromaticity.white_point.y !=
13060 next_image->next->chromaticity.white_point.y)
13061 mng_info->equal_chrms=MagickFalse;
13062 }
13063 }
13064 image_count++;
13065 next_image=GetNextImageInList(next_image);
13066 }
13067 if (image_count < 2)
13068 {
13069 mng_info->equal_backgrounds=MagickFalse;
13070 mng_info->equal_chrms=MagickFalse;
13071 mng_info->equal_gammas=MagickFalse;
13072 mng_info->equal_srgbs=MagickFalse;
13073 mng_info->equal_physs=MagickFalse;
13074 use_global_plte=MagickFalse;
13075#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13076 need_local_plte=MagickTrue;
13077#endif
13078 need_iterations=MagickFalse;
13079 }
glennrp0fe50b42010-11-16 03:52:51 +000013080
cristy3ed852e2009-09-05 21:47:34 +000013081 if (mng_info->need_fram == MagickFalse)
13082 {
13083 /*
13084 Only certain framing rates 100/n are exactly representable without
13085 the FRAM chunk but we'll allow some slop in VLC files
13086 */
13087 if (final_delay == 0)
13088 {
13089 if (need_iterations != MagickFalse)
13090 {
13091 /*
13092 It's probably a GIF with loop; don't run it *too* fast.
13093 */
glennrp02617122010-07-28 13:07:35 +000013094 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000013095 {
13096 final_delay=10;
cristy16ea1392012-03-21 20:38:41 +000013097 (void) ThrowMagickException(exception,GetMagickModule(),
13098 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000013099 "input has zero delay between all frames; assuming",
13100 " 10 cs `%s'","");
13101 }
cristy3ed852e2009-09-05 21:47:34 +000013102 }
13103 else
13104 mng_info->ticks_per_second=0;
13105 }
13106 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000013107 mng_info->ticks_per_second=(png_uint_32)
13108 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000013109 if (final_delay > 50)
13110 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000013111
cristy3ed852e2009-09-05 21:47:34 +000013112 if (final_delay > 75)
13113 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000013114
cristy3ed852e2009-09-05 21:47:34 +000013115 if (final_delay > 125)
13116 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000013117
cristy3ed852e2009-09-05 21:47:34 +000013118 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13119 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
glennrpd0ee5a22014-03-28 14:28:47 +000013120 (final_delay != 25) && (final_delay != 50) && (1UL*final_delay !=
cristy3ed852e2009-09-05 21:47:34 +000013121 1UL*image->ticks_per_second))
13122 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13123 }
glennrp0fe50b42010-11-16 03:52:51 +000013124
cristy3ed852e2009-09-05 21:47:34 +000013125 if (mng_info->need_fram != MagickFalse)
13126 mng_info->ticks_per_second=1UL*image->ticks_per_second;
13127 /*
13128 If pseudocolor, we should also check to see if all the
13129 palettes are identical and write a global PLTE if they are.
13130 ../glennrp Feb 99.
13131 */
13132 /*
13133 Write the MNG version 1.0 signature and MHDR chunk.
13134 */
13135 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13136 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13137 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000013138 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000013139 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13140 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000013141 PNGLong(chunk+12,mng_info->ticks_per_second);
13142 PNGLong(chunk+16,0L); /* layer count=unknown */
13143 PNGLong(chunk+20,0L); /* frame count=unknown */
13144 PNGLong(chunk+24,0L); /* play time=unknown */
13145 if (write_jng)
13146 {
13147 if (need_matte)
13148 {
13149 if (need_defi || mng_info->need_fram || use_global_plte)
13150 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000013151
cristy3ed852e2009-09-05 21:47:34 +000013152 else
13153 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13154 }
glennrp0fe50b42010-11-16 03:52:51 +000013155
cristy3ed852e2009-09-05 21:47:34 +000013156 else
13157 {
13158 if (need_defi || mng_info->need_fram || use_global_plte)
13159 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000013160
cristy3ed852e2009-09-05 21:47:34 +000013161 else
13162 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13163 }
13164 }
glennrp0fe50b42010-11-16 03:52:51 +000013165
cristy3ed852e2009-09-05 21:47:34 +000013166 else
13167 {
13168 if (need_matte)
13169 {
13170 if (need_defi || mng_info->need_fram || use_global_plte)
13171 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000013172
cristy3ed852e2009-09-05 21:47:34 +000013173 else
13174 PNGLong(chunk+28,9L); /* simplicity=VLC */
13175 }
glennrp0fe50b42010-11-16 03:52:51 +000013176
cristy3ed852e2009-09-05 21:47:34 +000013177 else
13178 {
13179 if (need_defi || mng_info->need_fram || use_global_plte)
13180 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000013181
cristy3ed852e2009-09-05 21:47:34 +000013182 else
13183 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13184 }
13185 }
13186 (void) WriteBlob(image,32,chunk);
13187 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
cristy092ec8d2013-04-26 13:46:22 +000013188 option=GetImageOption(image_info,"mng:need-cacheoff");
cristy3ed852e2009-09-05 21:47:34 +000013189 if (option != (const char *) NULL)
13190 {
13191 size_t
13192 length;
13193
13194 /*
13195 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13196 */
13197 PNGType(chunk,mng_nEED);
13198 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000013199 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000013200 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000013201 length+=4;
13202 (void) WriteBlob(image,length,chunk);
13203 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13204 }
13205 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13206 (GetNextImageInList(image) != (Image *) NULL) &&
13207 (image->iterations != 1))
13208 {
13209 /*
13210 Write MNG TERM chunk
13211 */
13212 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13213 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000013214 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013215 chunk[4]=3; /* repeat animation */
13216 chunk[5]=0; /* show last frame when done */
13217 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13218 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000013219
cristy3ed852e2009-09-05 21:47:34 +000013220 if (image->iterations == 0)
13221 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000013222
cristy3ed852e2009-09-05 21:47:34 +000013223 else
13224 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000013225
cristy3ed852e2009-09-05 21:47:34 +000013226 if (logging != MagickFalse)
13227 {
13228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013229 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13230 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000013231
cristy3ed852e2009-09-05 21:47:34 +000013232 if (image->iterations == 0)
13233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013234 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000013235
cristy3ed852e2009-09-05 21:47:34 +000013236 else
13237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013238 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000013239 }
13240 (void) WriteBlob(image,14,chunk);
13241 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13242 }
13243 /*
13244 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13245 */
13246 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13247 mng_info->equal_srgbs)
13248 {
13249 /*
13250 Write MNG sRGB chunk
13251 */
13252 (void) WriteBlobMSBULong(image,1L);
13253 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000013254 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000013255
cristy3ed852e2009-09-05 21:47:34 +000013256 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000013257 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000013258 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000013259 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000013260
cristy3ed852e2009-09-05 21:47:34 +000013261 else
glennrpe610a072010-08-05 17:08:46 +000013262 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000013263 Magick_RenderingIntent_to_PNG_RenderingIntent(
13264 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000013265
cristy3ed852e2009-09-05 21:47:34 +000013266 (void) WriteBlob(image,5,chunk);
13267 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13268 mng_info->have_write_global_srgb=MagickTrue;
13269 }
glennrp0fe50b42010-11-16 03:52:51 +000013270
cristy3ed852e2009-09-05 21:47:34 +000013271 else
13272 {
13273 if (image->gamma && mng_info->equal_gammas)
13274 {
13275 /*
13276 Write MNG gAMA chunk
13277 */
13278 (void) WriteBlobMSBULong(image,4L);
13279 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000013280 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000013281 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013282 (void) WriteBlob(image,8,chunk);
13283 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13284 mng_info->have_write_global_gama=MagickTrue;
13285 }
13286 if (mng_info->equal_chrms)
13287 {
13288 PrimaryInfo
13289 primary;
13290
13291 /*
13292 Write MNG cHRM chunk
13293 */
13294 (void) WriteBlobMSBULong(image,32L);
13295 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000013296 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000013297 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000013298 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13299 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013300 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000013301 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13302 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013303 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000013304 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13305 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013306 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000013307 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13308 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013309 (void) WriteBlob(image,36,chunk);
13310 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13311 mng_info->have_write_global_chrm=MagickTrue;
13312 }
13313 }
cristy16ea1392012-03-21 20:38:41 +000013314 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000013315 {
13316 /*
13317 Write MNG pHYs chunk
13318 */
13319 (void) WriteBlobMSBULong(image,9L);
13320 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000013321 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000013322
cristy3ed852e2009-09-05 21:47:34 +000013323 if (image->units == PixelsPerInchResolution)
13324 {
cristy35ef8242010-06-03 16:24:13 +000013325 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013326 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013327
cristy35ef8242010-06-03 16:24:13 +000013328 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013329 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013330
cristy3ed852e2009-09-05 21:47:34 +000013331 chunk[12]=1;
13332 }
glennrp0fe50b42010-11-16 03:52:51 +000013333
cristy3ed852e2009-09-05 21:47:34 +000013334 else
13335 {
13336 if (image->units == PixelsPerCentimeterResolution)
13337 {
cristy35ef8242010-06-03 16:24:13 +000013338 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013339 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013340
cristy35ef8242010-06-03 16:24:13 +000013341 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013342 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013343
cristy3ed852e2009-09-05 21:47:34 +000013344 chunk[12]=1;
13345 }
glennrp0fe50b42010-11-16 03:52:51 +000013346
cristy3ed852e2009-09-05 21:47:34 +000013347 else
13348 {
cristy16ea1392012-03-21 20:38:41 +000013349 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13350 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013351 chunk[12]=0;
13352 }
13353 }
13354 (void) WriteBlob(image,13,chunk);
13355 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13356 }
13357 /*
13358 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13359 or does not cover the entire frame.
13360 */
cristydc2d3272013-02-12 14:00:44 +000013361 if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13362 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
cristy3ed852e2009-09-05 21:47:34 +000013363 (image->page.width+image->page.x < mng_info->page.width))
13364 || (image->page.height && (image->page.height+image->page.y
13365 < mng_info->page.height))))
13366 {
13367 (void) WriteBlobMSBULong(image,6L);
13368 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000013369 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000013370 red=ScaleQuantumToShort(image->background_color.red);
13371 green=ScaleQuantumToShort(image->background_color.green);
13372 blue=ScaleQuantumToShort(image->background_color.blue);
13373 PNGShort(chunk+4,red);
13374 PNGShort(chunk+6,green);
13375 PNGShort(chunk+8,blue);
13376 (void) WriteBlob(image,10,chunk);
13377 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13378 if (mng_info->equal_backgrounds)
13379 {
13380 (void) WriteBlobMSBULong(image,6L);
13381 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000013382 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000013383 (void) WriteBlob(image,10,chunk);
13384 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13385 }
13386 }
13387
13388#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13389 if ((need_local_plte == MagickFalse) &&
13390 (image->storage_class == PseudoClass) &&
13391 (all_images_are_gray == MagickFalse))
13392 {
cristybb503372010-05-27 20:51:26 +000013393 size_t
cristy3ed852e2009-09-05 21:47:34 +000013394 data_length;
13395
13396 /*
13397 Write MNG PLTE chunk
13398 */
13399 data_length=3*image->colors;
13400 (void) WriteBlobMSBULong(image,data_length);
13401 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013402 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013403
cristybb503372010-05-27 20:51:26 +000013404 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013405 {
cristy16ea1392012-03-21 20:38:41 +000013406 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13407 image->colormap[i].red) & 0xff);
13408 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13409 image->colormap[i].green) & 0xff);
13410 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13411 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000013412 }
glennrp0fe50b42010-11-16 03:52:51 +000013413
cristy3ed852e2009-09-05 21:47:34 +000013414 (void) WriteBlob(image,data_length+4,chunk);
13415 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13416 mng_info->have_write_global_plte=MagickTrue;
13417 }
13418#endif
13419 }
13420 scene=0;
13421 mng_info->delay=0;
13422#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13423 defined(PNG_MNG_FEATURES_SUPPORTED)
13424 mng_info->equal_palettes=MagickFalse;
13425#endif
13426 do
13427 {
13428 if (mng_info->adjoin)
13429 {
13430#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13431 defined(PNG_MNG_FEATURES_SUPPORTED)
13432 /*
13433 If we aren't using a global palette for the entire MNG, check to
13434 see if we can use one for two or more consecutive images.
13435 */
13436 if (need_local_plte && use_global_plte && !all_images_are_gray)
13437 {
13438 if (mng_info->IsPalette)
13439 {
13440 /*
13441 When equal_palettes is true, this image has the same palette
13442 as the previous PseudoClass image
13443 */
13444 mng_info->have_write_global_plte=mng_info->equal_palettes;
13445 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13446 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13447 {
13448 /*
13449 Write MNG PLTE chunk
13450 */
cristybb503372010-05-27 20:51:26 +000013451 size_t
cristy3ed852e2009-09-05 21:47:34 +000013452 data_length;
13453
13454 data_length=3*image->colors;
13455 (void) WriteBlobMSBULong(image,data_length);
13456 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013457 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013458
cristybb503372010-05-27 20:51:26 +000013459 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013460 {
13461 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13462 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13463 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13464 }
glennrp0fe50b42010-11-16 03:52:51 +000013465
cristy3ed852e2009-09-05 21:47:34 +000013466 (void) WriteBlob(image,data_length+4,chunk);
13467 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13468 (uInt) (data_length+4)));
13469 mng_info->have_write_global_plte=MagickTrue;
13470 }
13471 }
13472 else
13473 mng_info->have_write_global_plte=MagickFalse;
13474 }
13475#endif
13476 if (need_defi)
13477 {
cristybb503372010-05-27 20:51:26 +000013478 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000013479 previous_x,
13480 previous_y;
13481
13482 if (scene)
13483 {
13484 previous_x=mng_info->page.x;
13485 previous_y=mng_info->page.y;
13486 }
13487 else
13488 {
13489 previous_x=0;
13490 previous_y=0;
13491 }
13492 mng_info->page=image->page;
13493 if ((mng_info->page.x != previous_x) ||
13494 (mng_info->page.y != previous_y))
13495 {
13496 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13497 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000013498 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000013499 chunk[4]=0; /* object 0 MSB */
13500 chunk[5]=0; /* object 0 LSB */
13501 chunk[6]=0; /* visible */
13502 chunk[7]=0; /* abstract */
13503 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13504 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13505 (void) WriteBlob(image,16,chunk);
13506 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13507 }
13508 }
13509 }
13510
13511 mng_info->write_mng=write_mng;
13512
13513 if ((int) image->dispose >= 3)
13514 mng_info->framing_mode=3;
13515
13516 if (mng_info->need_fram && mng_info->adjoin &&
13517 ((image->delay != mng_info->delay) ||
13518 (mng_info->framing_mode != mng_info->old_framing_mode)))
13519 {
13520 if (image->delay == mng_info->delay)
13521 {
13522 /*
13523 Write a MNG FRAM chunk with the new framing mode.
13524 */
13525 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13526 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013527 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013528 chunk[4]=(unsigned char) mng_info->framing_mode;
13529 (void) WriteBlob(image,5,chunk);
13530 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13531 }
13532 else
13533 {
13534 /*
13535 Write a MNG FRAM chunk with the delay.
13536 */
13537 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13538 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013539 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013540 chunk[4]=(unsigned char) mng_info->framing_mode;
13541 chunk[5]=0; /* frame name separator (no name) */
13542 chunk[6]=2; /* flag for changing default delay */
13543 chunk[7]=0; /* flag for changing frame timeout */
13544 chunk[8]=0; /* flag for changing frame clipping */
13545 chunk[9]=0; /* flag for changing frame sync_id */
13546 PNGLong(chunk+10,(png_uint_32)
13547 ((mng_info->ticks_per_second*
13548 image->delay)/MagickMax(image->ticks_per_second,1)));
13549 (void) WriteBlob(image,14,chunk);
13550 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013551 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013552 }
13553 mng_info->old_framing_mode=mng_info->framing_mode;
13554 }
13555
13556#if defined(JNG_SUPPORTED)
13557 if (image_info->compression == JPEGCompression)
13558 {
13559 ImageInfo
13560 *write_info;
13561
13562 if (logging != MagickFalse)
13563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13564 " Writing JNG object.");
13565 /* To do: specify the desired alpha compression method. */
13566 write_info=CloneImageInfo(image_info);
13567 write_info->compression=UndefinedCompression;
cristy16ea1392012-03-21 20:38:41 +000013568 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013569 write_info=DestroyImageInfo(write_info);
13570 }
13571 else
13572#endif
13573 {
13574 if (logging != MagickFalse)
13575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13576 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013577
glennrpb9cfe272010-12-21 15:08:06 +000013578 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013579 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013580
13581 /* We don't want any ancillary chunks written */
13582 mng_info->ping_exclude_bKGD=MagickTrue;
13583 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013584 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013585 mng_info->ping_exclude_EXIF=MagickTrue;
13586 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013587 mng_info->ping_exclude_iCCP=MagickTrue;
13588 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13589 mng_info->ping_exclude_oFFs=MagickTrue;
13590 mng_info->ping_exclude_pHYs=MagickTrue;
13591 mng_info->ping_exclude_sRGB=MagickTrue;
13592 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013593 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013594 mng_info->ping_exclude_vpAg=MagickTrue;
13595 mng_info->ping_exclude_zCCP=MagickTrue;
13596 mng_info->ping_exclude_zTXt=MagickTrue;
13597
cristy16ea1392012-03-21 20:38:41 +000013598 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013599 }
13600
13601 if (status == MagickFalse)
13602 {
13603 MngInfoFreeStruct(mng_info,&have_mng_structure);
13604 (void) CloseBlob(image);
13605 return(MagickFalse);
13606 }
13607 (void) CatchImageException(image);
13608 if (GetNextImageInList(image) == (Image *) NULL)
13609 break;
13610 image=SyncNextImageInList(image);
13611 status=SetImageProgress(image,SaveImagesTag,scene++,
13612 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013613
cristy3ed852e2009-09-05 21:47:34 +000013614 if (status == MagickFalse)
13615 break;
glennrp0fe50b42010-11-16 03:52:51 +000013616
cristy3ed852e2009-09-05 21:47:34 +000013617 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013618
cristy3ed852e2009-09-05 21:47:34 +000013619 if (write_mng)
13620 {
13621 while (GetPreviousImageInList(image) != (Image *) NULL)
13622 image=GetPreviousImageInList(image);
13623 /*
13624 Write the MEND chunk.
13625 */
13626 (void) WriteBlobMSBULong(image,0x00000000L);
13627 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013628 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013629 (void) WriteBlob(image,4,chunk);
13630 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13631 }
13632 /*
13633 Relinquish resources.
13634 */
13635 (void) CloseBlob(image);
13636 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013637
cristy3ed852e2009-09-05 21:47:34 +000013638 if (logging != MagickFalse)
13639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013640
cristy3ed852e2009-09-05 21:47:34 +000013641 return(MagickTrue);
13642}
glennrpd5045b42010-03-24 12:40:35 +000013643#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013644
cristy3ed852e2009-09-05 21:47:34 +000013645static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13646{
glennrp3bd393f2011-12-21 18:54:53 +000013647 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013648 printf("Your PNG library is too old: You have libpng-%s\n",
13649 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013650
cristy3ed852e2009-09-05 21:47:34 +000013651 ThrowBinaryException(CoderError,"PNG library is too old",
13652 image_info->filename);
13653}
glennrp39992b42010-11-14 00:03:43 +000013654
cristy3ed852e2009-09-05 21:47:34 +000013655static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13656{
13657 return(WritePNGImage(image_info,image));
13658}
glennrpd5045b42010-03-24 12:40:35 +000013659#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013660#endif