blob: 24b1c40af7837309e6d62e2d20712a282e7c653d [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP N N GGGG %
7% P P NN N G %
8% PPPP N N N G GG %
9% P N NN G G %
10% P N N GGG %
11% %
12% %
13% Read/Write Portable Network Graphics Image Format %
14% %
15% Software Design %
16% John Cristy %
17% Glenn Randers-Pehrson %
18% November 1997 %
19% %
20% %
cristy45ef08f2012-12-07 13:13:34 +000021% Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000022% dedicated to making software imaging solutions freely available. %
23% %
24% You may not use this file except in compliance with the License. You may %
25% obtain a copy of the License at %
26% %
27% http://www.imagemagick.org/script/license.php %
28% %
29% Unless required by applicable law or agreed to in writing, software %
30% distributed under the License is distributed on an "AS IS" BASIS, %
31% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32% See the License for the specific language governing permissions and %
33% limitations under the License. %
34% %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40
41/*
42 Include declarations.
43*/
cristy16ea1392012-03-21 20:38:41 +000044#include "MagickCore/studio.h"
45#include "MagickCore/artifact.h"
46#include "MagickCore/attribute.h"
47#include "MagickCore/blob.h"
48#include "MagickCore/blob-private.h"
49#include "MagickCore/cache.h"
cristyaa2c16c2012-03-25 22:21:35 +000050#include "MagickCore/channel.h"
cristy16ea1392012-03-21 20:38:41 +000051#include "MagickCore/color.h"
52#include "MagickCore/color-private.h"
53#include "MagickCore/colormap.h"
54#include "MagickCore/colorspace.h"
55#include "MagickCore/colorspace-private.h"
56#include "MagickCore/constitute.h"
57#include "MagickCore/enhance.h"
58#include "MagickCore/exception.h"
59#include "MagickCore/exception-private.h"
60#include "MagickCore/geometry.h"
61#include "MagickCore/histogram.h"
62#include "MagickCore/image.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/layer.h"
65#include "MagickCore/list.h"
66#include "MagickCore/log.h"
67#include "MagickCore/MagickCore.h"
68#include "MagickCore/memory_.h"
69#include "MagickCore/module.h"
70#include "MagickCore/monitor.h"
71#include "MagickCore/monitor-private.h"
72#include "MagickCore/option.h"
73#include "MagickCore/pixel.h"
74#include "MagickCore/pixel-accessor.h"
75#include "MagickCore/profile.h"
76#include "MagickCore/property.h"
77#include "MagickCore/quantum-private.h"
78#include "MagickCore/resource_.h"
79#include "MagickCore/semaphore.h"
80#include "MagickCore/quantum-private.h"
81#include "MagickCore/static.h"
82#include "MagickCore/statistic.h"
83#include "MagickCore/string_.h"
84#include "MagickCore/string-private.h"
85#include "MagickCore/transform.h"
86#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000087#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000088
glennrp7ef138c2009-11-10 13:50:20 +000089/* Suppress libpng pedantic warnings that were added in
90 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000091 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000092 * fix any code that generates warnings.
93 */
glennrp991e92a2010-01-28 03:09:00 +000094/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000095/* #define PNG_USE_RESULT The result of this function must be checked */
96/* #define PNG_NORETURN This function does not return */
97/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000098/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000099
100/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +0000101#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000102
cristy3ed852e2009-09-05 21:47:34 +0000103#include "png.h"
104#include "zlib.h"
105
106/* ImageMagick differences */
107#define first_scene scene
108
glennrpd5045b42010-03-24 12:40:35 +0000109#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000110/*
111 Optional declarations. Define or undefine them as you like.
112*/
113/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
114
115/*
116 Features under construction. Define these to work on them.
117*/
118#undef MNG_OBJECT_BUFFERS
119#undef MNG_BASI_SUPPORTED
120#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000122#if defined(MAGICKCORE_JPEG_DELEGATE)
123# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
124#endif
125#if !defined(RGBColorMatchExact)
126#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000127 (((color).red == (target).red) && \
128 ((color).green == (target).green) && \
129 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000130#endif
131
glennrp8e58efd2011-05-20 12:16:29 +0000132/* Macros for left-bit-replication to ensure that pixels
cristy16ea1392012-03-21 20:38:41 +0000133 * and PixelInfos all have the same image->depth, and for use
glennrp8e58efd2011-05-20 12:16:29 +0000134 * in PNG8 quantization.
135 */
136
137
138/* LBR01: Replicate top bit */
139
glennrp05001c32011-08-06 13:04:16 +0000140#define LBR01PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000141 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
142 0 : QuantumRange);
143
glennrp91d99252011-06-25 14:30:13 +0000144#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000145 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
146 0 : QuantumRange);
147
glennrp91d99252011-06-25 14:30:13 +0000148#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000149 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
150 0 : QuantumRange);
151
cristy16ea1392012-03-21 20:38:41 +0000152#define LBR01PacketAlpha(pixelpacket) \
153 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000154 0 : QuantumRange);
155
glennrp91d99252011-06-25 14:30:13 +0000156#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000157 { \
glennrp05001c32011-08-06 13:04:16 +0000158 LBR01PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000159 LBR01PacketGreen((pixelpacket)); \
160 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000161 }
glennrp8e58efd2011-05-20 12:16:29 +0000162
glennrp91d99252011-06-25 14:30:13 +0000163#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000164 { \
glennrp91d99252011-06-25 14:30:13 +0000165 LBR01PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000166 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000167 }
glennrp8e58efd2011-05-20 12:16:29 +0000168
cristyef618312011-06-25 12:26:44 +0000169#define LBR01PixelRed(pixel) \
glennrp360c1542013-01-06 01:21:46 +0000170 (SetPixelRed(image, \
171 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrpc7375f92013-01-06 01:32:16 +0000172 0 : QuantumRange,(pixel)));
glennrp8e58efd2011-05-20 12:16:29 +0000173
glennrp54cf7972011-08-06 14:28:09 +0000174#define LBR01PixelGreen(pixel) \
glennrp360c1542013-01-06 01:21:46 +0000175 (SetPixelGreen(image, \
176 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrpc7375f92013-01-06 01:32:16 +0000177 0 : QuantumRange,(pixel)));
glennrp8e58efd2011-05-20 12:16:29 +0000178
glennrp54cf7972011-08-06 14:28:09 +0000179#define LBR01PixelBlue(pixel) \
glennrp360c1542013-01-06 01:21:46 +0000180 (SetPixelBlue(image, \
181 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrpc7375f92013-01-06 01:32:16 +0000182 0 : QuantumRange,(pixel)));
glennrp8e58efd2011-05-20 12:16:29 +0000183
cristy16ea1392012-03-21 20:38:41 +0000184#define LBR01PixelAlpha(pixel) \
glennrp360c1542013-01-06 01:21:46 +0000185 (SetPixelAlpha(image, \
186 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrpc7375f92013-01-06 01:32:16 +0000187 0 : QuantumRange,(pixel)));
glennrp8e58efd2011-05-20 12:16:29 +0000188
glennrp54cf7972011-08-06 14:28:09 +0000189#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000190 { \
cristyef618312011-06-25 12:26:44 +0000191 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000192 LBR01PixelGreen((pixel)); \
193 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000194 }
glennrp8e58efd2011-05-20 12:16:29 +0000195
cristy16ea1392012-03-21 20:38:41 +0000196#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000197 { \
glennrp54cf7972011-08-06 14:28:09 +0000198 LBR01PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000199 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000200 }
glennrp8e58efd2011-05-20 12:16:29 +0000201
202/* LBR02: Replicate top 2 bits */
203
glennrp05001c32011-08-06 13:04:16 +0000204#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000205 { \
206 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
207 (pixelpacket).red=ScaleCharToQuantum( \
208 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
209 }
glennrp91d99252011-06-25 14:30:13 +0000210#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000211 { \
212 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
213 (pixelpacket).green=ScaleCharToQuantum( \
214 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
215 }
glennrp91d99252011-06-25 14:30:13 +0000216#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000217 { \
218 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
219 (pixelpacket).blue=ScaleCharToQuantum( \
220 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
221 }
cristy16ea1392012-03-21 20:38:41 +0000222#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000223 { \
cristy16ea1392012-03-21 20:38:41 +0000224 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
225 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000226 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
227 }
228
glennrp91d99252011-06-25 14:30:13 +0000229#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000230 { \
glennrp05001c32011-08-06 13:04:16 +0000231 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000232 LBR02PacketGreen((pixelpacket)); \
233 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000234 }
glennrp8e58efd2011-05-20 12:16:29 +0000235
glennrp91d99252011-06-25 14:30:13 +0000236#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000237 { \
glennrp91d99252011-06-25 14:30:13 +0000238 LBR02PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000239 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000240 }
glennrp8e58efd2011-05-20 12:16:29 +0000241
cristyef618312011-06-25 12:26:44 +0000242#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000243 { \
cristy16ea1392012-03-21 20:38:41 +0000244 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000245 & 0xc0; \
cristy16ea1392012-03-21 20:38:41 +0000246 SetPixelRed(image, ScaleCharToQuantum( \
247 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
248 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000249 }
glennrp54cf7972011-08-06 14:28:09 +0000250#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000251 { \
cristy16ea1392012-03-21 20:38:41 +0000252 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000253 & 0xc0; \
cristy16ea1392012-03-21 20:38:41 +0000254 SetPixelGreen(image, ScaleCharToQuantum( \
255 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
256 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000257 }
glennrp54cf7972011-08-06 14:28:09 +0000258#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000259 { \
260 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000261 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
262 SetPixelBlue(image, ScaleCharToQuantum( \
263 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
264 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000265 }
cristy16ea1392012-03-21 20:38:41 +0000266#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000267 { \
268 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000269 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
270 SetPixelAlpha(image, ScaleCharToQuantum( \
271 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
272 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000273 }
274
glennrp54cf7972011-08-06 14:28:09 +0000275#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000276 { \
cristyef618312011-06-25 12:26:44 +0000277 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000278 LBR02PixelGreen((pixel)); \
279 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000280 }
glennrp8e58efd2011-05-20 12:16:29 +0000281
cristy16ea1392012-03-21 20:38:41 +0000282#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000283 { \
glennrp54cf7972011-08-06 14:28:09 +0000284 LBR02PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000285 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000286 }
glennrp8e58efd2011-05-20 12:16:29 +0000287
288/* LBR03: Replicate top 3 bits (only used with opaque pixels during
289 PNG8 quantization) */
290
glennrp05001c32011-08-06 13:04:16 +0000291#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000292 { \
293 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
294 (pixelpacket).red=ScaleCharToQuantum( \
295 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
296 }
glennrp91d99252011-06-25 14:30:13 +0000297#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000298 { \
299 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
300 (pixelpacket).green=ScaleCharToQuantum( \
301 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
302 }
glennrp91d99252011-06-25 14:30:13 +0000303#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000304 { \
305 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
306 (pixelpacket).blue=ScaleCharToQuantum( \
307 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
308 }
309
glennrp91d99252011-06-25 14:30:13 +0000310#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000311 { \
glennrp05001c32011-08-06 13:04:16 +0000312 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000313 LBR03PacketGreen((pixelpacket)); \
314 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000315 }
glennrp8e58efd2011-05-20 12:16:29 +0000316
cristyef618312011-06-25 12:26:44 +0000317#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000318 { \
cristy16ea1392012-03-21 20:38:41 +0000319 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000320 & 0xe0; \
cristy16ea1392012-03-21 20:38:41 +0000321 SetPixelRed(image, ScaleCharToQuantum( \
322 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000323 }
cristy16ea1392012-03-21 20:38:41 +0000324#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000325 { \
cristy16ea1392012-03-21 20:38:41 +0000326 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000327 & 0xe0; \
cristy16ea1392012-03-21 20:38:41 +0000328 SetPixelGreen(image, ScaleCharToQuantum( \
329 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000330 }
cristy16ea1392012-03-21 20:38:41 +0000331#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000332 { \
cristy16ea1392012-03-21 20:38:41 +0000333 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000334 & 0xe0; \
cristy16ea1392012-03-21 20:38:41 +0000335 SetPixelBlue(image, ScaleCharToQuantum( \
336 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000337 }
338
cristy16ea1392012-03-21 20:38:41 +0000339#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000340 { \
cristyef618312011-06-25 12:26:44 +0000341 LBR03PixelRed((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000342 LBR03Green((pixel)); \
343 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000344 }
glennrp8e58efd2011-05-20 12:16:29 +0000345
346/* LBR04: Replicate top 4 bits */
347
glennrp05001c32011-08-06 13:04:16 +0000348#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
351 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352 }
glennrp91d99252011-06-25 14:30:13 +0000353#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000354 { \
355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
356 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
357 }
glennrp91d99252011-06-25 14:30:13 +0000358#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000359 { \
360 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
361 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
362 }
cristy16ea1392012-03-21 20:38:41 +0000363#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000364 { \
cristy16ea1392012-03-21 20:38:41 +0000365 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
366 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000367 }
368
glennrp91d99252011-06-25 14:30:13 +0000369#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000370 { \
glennrp05001c32011-08-06 13:04:16 +0000371 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000372 LBR04PacketGreen((pixelpacket)); \
373 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000374 }
glennrp8e58efd2011-05-20 12:16:29 +0000375
glennrp91d99252011-06-25 14:30:13 +0000376#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000377 { \
glennrp91d99252011-06-25 14:30:13 +0000378 LBR04PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000379 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000380 }
glennrp8e58efd2011-05-20 12:16:29 +0000381
cristyef618312011-06-25 12:26:44 +0000382#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000383 { \
cristy16ea1392012-03-21 20:38:41 +0000384 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000385 & 0xf0; \
cristy16ea1392012-03-21 20:38:41 +0000386 SetPixelRed(image,\
387 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000388 }
glennrp54cf7972011-08-06 14:28:09 +0000389#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000390 { \
cristy16ea1392012-03-21 20:38:41 +0000391 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000392 & 0xf0; \
cristy16ea1392012-03-21 20:38:41 +0000393 SetPixelGreen(image,\
394 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000395 }
glennrp54cf7972011-08-06 14:28:09 +0000396#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000397 { \
398 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000399 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
400 SetPixelBlue(image,\
401 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000402 }
cristy16ea1392012-03-21 20:38:41 +0000403#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000404 { \
405 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000406 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
407 SetPixelAlpha(image,\
408 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000409 }
410
glennrp54cf7972011-08-06 14:28:09 +0000411#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000412 { \
cristyef618312011-06-25 12:26:44 +0000413 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000414 LBR04PixelGreen((pixel)); \
415 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000416 }
glennrp8e58efd2011-05-20 12:16:29 +0000417
cristy16ea1392012-03-21 20:38:41 +0000418#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000419 { \
glennrp54cf7972011-08-06 14:28:09 +0000420 LBR04PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000421 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000422 }
glennrp8e58efd2011-05-20 12:16:29 +0000423
424
glennrp11458992013-01-08 18:12:52 +0000425#if MAGICKCORE_QUANTUM_DEPTH > 8
glennrp8e58efd2011-05-20 12:16:29 +0000426/* LBR08: Replicate top 8 bits */
427
glennrp05001c32011-08-06 13:04:16 +0000428#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000429 { \
430 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
431 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
432 }
glennrp91d99252011-06-25 14:30:13 +0000433#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000434 { \
435 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
436 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
437 }
glennrp91d99252011-06-25 14:30:13 +0000438#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000439 { \
440 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
441 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
442 }
cristy16ea1392012-03-21 20:38:41 +0000443#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000444 { \
cristy16ea1392012-03-21 20:38:41 +0000445 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
446 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000447 }
448
glennrp91d99252011-06-25 14:30:13 +0000449#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000450 { \
glennrp05001c32011-08-06 13:04:16 +0000451 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000452 LBR08PacketGreen((pixelpacket)); \
453 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000454 }
glennrp8e58efd2011-05-20 12:16:29 +0000455
glennrp91d99252011-06-25 14:30:13 +0000456#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000457 { \
glennrp91d99252011-06-25 14:30:13 +0000458 LBR08PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000459 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000460 }
glennrp8e58efd2011-05-20 12:16:29 +0000461
cristyef618312011-06-25 12:26:44 +0000462#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000463 { \
464 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000465 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
466 SetPixelRed(image,\
467 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000468 }
glennrp54cf7972011-08-06 14:28:09 +0000469#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000470 { \
471 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000472 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
473 SetPixelGreen(image,\
474 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000475 }
glennrp54cf7972011-08-06 14:28:09 +0000476#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000477 { \
478 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000479 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
480 SetPixelBlue(image,\
481 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000482 }
cristy16ea1392012-03-21 20:38:41 +0000483#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000484 { \
485 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000486 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
487 SetPixelAlpha(image,\
488 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000489 }
490
glennrp54cf7972011-08-06 14:28:09 +0000491#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000492 { \
cristyef618312011-06-25 12:26:44 +0000493 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000494 LBR08PixelGreen((pixel)); \
495 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000496 }
glennrp8e58efd2011-05-20 12:16:29 +0000497
cristy16ea1392012-03-21 20:38:41 +0000498#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000499 { \
glennrp54cf7972011-08-06 14:28:09 +0000500 LBR08PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000501 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000502 }
glennrp11458992013-01-08 18:12:52 +0000503#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
glennrp8e58efd2011-05-20 12:16:29 +0000504
505
glennrp15f07662013-01-08 18:08:13 +0000506#if MAGICKCORE_QUANTUM_DEPTH > 16
glennrp8e58efd2011-05-20 12:16:29 +0000507/* LBR16: Replicate top 16 bits */
508
glennrp05001c32011-08-06 13:04:16 +0000509#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000510 { \
511 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
512 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
513 }
glennrp91d99252011-06-25 14:30:13 +0000514#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000515 { \
516 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
517 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
518 }
glennrp91d99252011-06-25 14:30:13 +0000519#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000520 { \
521 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
522 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
523 }
cristy16ea1392012-03-21 20:38:41 +0000524#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000525 { \
cristy16ea1392012-03-21 20:38:41 +0000526 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
527 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000528 }
529
glennrp91d99252011-06-25 14:30:13 +0000530#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000531 { \
glennrp05001c32011-08-06 13:04:16 +0000532 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000533 LBR16PacketGreen((pixelpacket)); \
534 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000535 }
glennrp8e58efd2011-05-20 12:16:29 +0000536
glennrp91d99252011-06-25 14:30:13 +0000537#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000538 { \
glennrp91d99252011-06-25 14:30:13 +0000539 LBR16PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000540 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000541 }
glennrp8e58efd2011-05-20 12:16:29 +0000542
cristyef618312011-06-25 12:26:44 +0000543#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000544 { \
545 unsigned short lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000546 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
547 SetPixelRed(image,\
548 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000549 }
glennrp54cf7972011-08-06 14:28:09 +0000550#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000551 { \
552 unsigned short lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000553 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
554 SetPixelGreen(image,\
555 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000556 }
glennrp54cf7972011-08-06 14:28:09 +0000557#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000558 { \
559 unsigned short lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000560 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
561 SetPixelBlue(image,\
562 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000563 }
cristy16ea1392012-03-21 20:38:41 +0000564#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000565 { \
566 unsigned short lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000567 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
568 SetPixelAlpha(image,\
569 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000570 }
571
glennrp54cf7972011-08-06 14:28:09 +0000572#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000573 { \
cristyef618312011-06-25 12:26:44 +0000574 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000575 LBR16PixelGreen((pixel)); \
576 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000577 }
glennrp8e58efd2011-05-20 12:16:29 +0000578
cristy16ea1392012-03-21 20:38:41 +0000579#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000580 { \
glennrp54cf7972011-08-06 14:28:09 +0000581 LBR16PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000582 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000583 }
glennrp15f07662013-01-08 18:08:13 +0000584#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
glennrp8e58efd2011-05-20 12:16:29 +0000585
cristy3ed852e2009-09-05 21:47:34 +0000586/*
587 Establish thread safety.
588 setjmp/longjmp is claimed to be safe on these platforms:
589 setjmp/longjmp is alleged to be unsafe on these platforms:
590*/
591#ifndef SETJMP_IS_THREAD_SAFE
592#define PNG_SETJMP_NOT_THREAD_SAFE
593#endif
594
glennrpedaa0382012-04-12 14:16:21 +0000595#ifdef PNG_SETJMP_NOT_THREAD_SAFE
cristy3ed852e2009-09-05 21:47:34 +0000596static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000597 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000598#endif
599
600/*
601 This temporary until I set up malloc'ed object attributes array.
602 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
603 waste more memory.
604*/
605#define MNG_MAX_OBJECTS 256
606
607/*
608 If this not defined, spec is interpreted strictly. If it is
609 defined, an attempt will be made to recover from some errors,
610 including
611 o global PLTE too short
612*/
613#undef MNG_LOOSE
614
615/*
616 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
617 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
618 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
619 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
620 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
621 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
622 will be enabled by default in libpng-1.2.0.
623*/
cristy3ed852e2009-09-05 21:47:34 +0000624#ifdef PNG_MNG_FEATURES_SUPPORTED
625# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
626# define PNG_READ_EMPTY_PLTE_SUPPORTED
627# endif
628# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
629# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
630# endif
631#endif
632
633/*
cristybb503372010-05-27 20:51:26 +0000634 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000635 This macro is only defined in libpng-1.0.3 and later.
636 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
637*/
638#ifndef PNG_UINT_31_MAX
639#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
640#endif
641
642/*
643 Constant strings for known chunk types. If you need to add a chunk,
644 add a string holding the name here. To make the code more
645 portable, we use ASCII numbers like this, not characters.
646*/
647
glennrp85dcf872011-12-07 02:51:47 +0000648static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
649static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
650static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
651static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
652static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
653static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
654static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
655static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
656static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
657static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
658static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
659static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
660static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
661static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
662static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
663static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
664static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
665static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
666static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
667static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
668static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
669static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
670static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
671static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
672static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
673static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
674static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
675static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
676static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
677static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
678static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
679static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
680static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
681static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000682
683#if defined(JNG_SUPPORTED)
glennrp85dcf872011-12-07 02:51:47 +0000684static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
685static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
686static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
687static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
688static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
689static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000690#endif
691
692/*
693Other known chunks that are not yet supported by ImageMagick:
glennrp85dcf872011-12-07 02:51:47 +0000694static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
695static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
696static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
697static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
698static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
699static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
700static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
701static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000702*/
703
704typedef struct _MngBox
705{
cristy8182b072010-05-30 20:10:53 +0000706 long
cristy3ed852e2009-09-05 21:47:34 +0000707 left,
708 right,
709 top,
710 bottom;
711} MngBox;
712
713typedef struct _MngPair
714{
cristy8182b072010-05-30 20:10:53 +0000715 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000716 a,
717 b;
718} MngPair;
719
720#ifdef MNG_OBJECT_BUFFERS
721typedef struct _MngBuffer
722{
723
cristybb503372010-05-27 20:51:26 +0000724 size_t
cristy3ed852e2009-09-05 21:47:34 +0000725 height,
726 width;
727
728 Image
729 *image;
730
731 png_color
732 plte[256];
733
734 int
735 reference_count;
736
737 unsigned char
738 alpha_sample_depth,
739 compression_method,
740 color_type,
741 concrete,
742 filter_method,
743 frozen,
744 image_type,
745 interlace_method,
746 pixel_sample_depth,
747 plte_length,
748 sample_depth,
749 viewable;
750} MngBuffer;
751#endif
752
753typedef struct _MngInfo
754{
755
756#ifdef MNG_OBJECT_BUFFERS
757 MngBuffer
758 *ob[MNG_MAX_OBJECTS];
759#endif
760
761 Image *
762 image;
763
764 RectangleInfo
765 page;
766
767 int
768 adjoin,
769#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
770 bytes_in_read_buffer,
771 found_empty_plte,
772#endif
773 equal_backgrounds,
774 equal_chrms,
775 equal_gammas,
776#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
777 defined(PNG_MNG_FEATURES_SUPPORTED)
778 equal_palettes,
779#endif
780 equal_physs,
781 equal_srgbs,
782 framing_mode,
783 have_global_bkgd,
784 have_global_chrm,
785 have_global_gama,
786 have_global_phys,
787 have_global_sbit,
788 have_global_srgb,
789 have_saved_bkgd_index,
790 have_write_global_chrm,
791 have_write_global_gama,
792 have_write_global_plte,
793 have_write_global_srgb,
794 need_fram,
795 object_id,
796 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000797 saved_bkgd_index;
798
799 int
800 new_number_colors;
801
cristybb503372010-05-27 20:51:26 +0000802 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000803 image_found,
804 loop_count[256],
805 loop_iteration[256],
806 scenes_found,
807 x_off[MNG_MAX_OBJECTS],
808 y_off[MNG_MAX_OBJECTS];
809
810 MngBox
811 clip,
812 frame,
813 image_box,
814 object_clip[MNG_MAX_OBJECTS];
815
816 unsigned char
817 /* These flags could be combined into one byte */
818 exists[MNG_MAX_OBJECTS],
819 frozen[MNG_MAX_OBJECTS],
820 loop_active[256],
821 invisible[MNG_MAX_OBJECTS],
822 viewable[MNG_MAX_OBJECTS];
823
824 MagickOffsetType
825 loop_jump[256];
826
827 png_colorp
828 global_plte;
829
830 png_color_8
831 global_sbit;
832
833 png_byte
834#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
835 read_buffer[8],
836#endif
837 global_trns[256];
838
839 float
840 global_gamma;
841
842 ChromaticityInfo
843 global_chrm;
844
845 RenderingIntent
846 global_srgb_intent;
847
cristy35ef8242010-06-03 16:24:13 +0000848 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000849 delay,
850 global_plte_length,
851 global_trns_length,
852 global_x_pixels_per_unit,
853 global_y_pixels_per_unit,
854 mng_width,
855 mng_height,
856 ticks_per_second;
857
glennrpb9cfe272010-12-21 15:08:06 +0000858 MagickBooleanType
859 need_blob;
860
cristy3ed852e2009-09-05 21:47:34 +0000861 unsigned int
862 IsPalette,
863 global_phys_unit_type,
864 basi_warning,
865 clon_warning,
866 dhdr_warning,
867 jhdr_warning,
868 magn_warning,
869 past_warning,
870 phyg_warning,
871 phys_warning,
872 sbit_warning,
873 show_warning,
874 mng_type,
875 write_mng,
876 write_png_colortype,
877 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000878 write_png_compression_level,
879 write_png_compression_strategy,
880 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000881 write_png8,
882 write_png24,
883 write_png32;
884
885#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000886 size_t
cristy3ed852e2009-09-05 21:47:34 +0000887 basi_width,
888 basi_height;
889
890 unsigned int
891 basi_depth,
892 basi_color_type,
893 basi_compression_method,
894 basi_filter_type,
895 basi_interlace_method,
896 basi_red,
897 basi_green,
898 basi_blue,
899 basi_alpha,
900 basi_viewable;
901#endif
902
903 png_uint_16
904 magn_first,
905 magn_last,
906 magn_mb,
907 magn_ml,
908 magn_mr,
909 magn_mt,
910 magn_mx,
911 magn_my,
912 magn_methx,
913 magn_methy;
914
cristy16ea1392012-03-21 20:38:41 +0000915 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000916 mng_global_bkgd;
917
glennrp26f37912010-12-23 16:22:42 +0000918 /* Added at version 6.6.6-7 */
919 MagickBooleanType
920 ping_exclude_bKGD,
921 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000922 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000923 ping_exclude_EXIF,
924 ping_exclude_gAMA,
925 ping_exclude_iCCP,
926 /* ping_exclude_iTXt, */
927 ping_exclude_oFFs,
928 ping_exclude_pHYs,
929 ping_exclude_sRGB,
930 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000931 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000932 ping_exclude_vpAg,
933 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000934 ping_exclude_zTXt,
935 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000936
cristy3ed852e2009-09-05 21:47:34 +0000937} MngInfo;
938#endif /* VER */
939
940/*
941 Forward declarations.
942*/
943static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000944 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000945
cristy3ed852e2009-09-05 21:47:34 +0000946static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000947 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000948
cristy3ed852e2009-09-05 21:47:34 +0000949#if defined(JNG_SUPPORTED)
950static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000951 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000952#endif
953
glennrp0c3e06b2010-11-19 13:45:02 +0000954#if PNG_LIBPNG_VER > 10011
955
glennrpfd05d622011-02-25 04:10:33 +0000956
glennrp0c3e06b2010-11-19 13:45:02 +0000957#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
958static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000959LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
glennrp0c3e06b2010-11-19 13:45:02 +0000960{
glennrp67b9c1a2011-04-22 18:47:36 +0000961 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
962 *
963 * This is true if the high byte and the next highest byte of
964 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000965 * are equal to each other. We check this by seeing if the samples
966 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000967 *
968 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000969 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000970 */
971
glennrp3faa9a32011-04-23 14:00:25 +0000972#define QuantumToCharToQuantumEqQuantum(quantum) \
973 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
974
glennrp0c3e06b2010-11-19 13:45:02 +0000975 MagickBooleanType
976 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000977
glennrp03e11f62011-04-22 13:30:16 +0000978 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000979 {
980
cristy16ea1392012-03-21 20:38:41 +0000981 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000982 *p;
983
984 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000985 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
986 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
987 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
988 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000989
990 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
991 {
992 int indx;
993
994 for (indx=0; indx < (ssize_t) image->colors; indx++)
995 {
glennrp3faa9a32011-04-23 14:00:25 +0000996 ok_to_reduce=(
997 QuantumToCharToQuantumEqQuantum(
998 image->colormap[indx].red) &&
999 QuantumToCharToQuantumEqQuantum(
1000 image->colormap[indx].green) &&
1001 QuantumToCharToQuantumEqQuantum(
1002 image->colormap[indx].blue)) ?
1003 MagickTrue : MagickFalse;
1004
glennrp0c3e06b2010-11-19 13:45:02 +00001005 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00001006 break;
glennrp0c3e06b2010-11-19 13:45:02 +00001007 }
1008 }
1009
1010 if ((ok_to_reduce != MagickFalse) &&
1011 (image->storage_class != PseudoClass))
1012 {
1013 ssize_t
1014 y;
1015
1016 register ssize_t
1017 x;
1018
1019 for (y=0; y < (ssize_t) image->rows; y++)
1020 {
cristy16ea1392012-03-21 20:38:41 +00001021 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0c3e06b2010-11-19 13:45:02 +00001022
cristy16ea1392012-03-21 20:38:41 +00001023 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001024 {
1025 ok_to_reduce = MagickFalse;
1026 break;
1027 }
1028
1029 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1030 {
glennrp3faa9a32011-04-23 14:00:25 +00001031 ok_to_reduce=
cristy16ea1392012-03-21 20:38:41 +00001032 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1033 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1034 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001035 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001036
1037 if (ok_to_reduce == MagickFalse)
1038 break;
1039
cristy16ea1392012-03-21 20:38:41 +00001040 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001041 }
glennrp8640fb52010-11-23 15:48:26 +00001042 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001043 break;
1044 }
1045 }
1046
1047 if (ok_to_reduce != MagickFalse)
1048 {
glennrp0c3e06b2010-11-19 13:45:02 +00001049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001050 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001051 }
glennrpa6a06632011-01-19 15:15:34 +00001052 else
1053 {
1054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001055 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001056 }
glennrp0c3e06b2010-11-19 13:45:02 +00001057 }
1058
1059 return ok_to_reduce;
1060}
1061#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1062
glennrp1a56e9c2012-04-25 17:06:57 +00001063static const char* PngColorTypeToString(const unsigned int color_type)
1064{
1065 const char
1066 *result = "Unknown";
1067
1068 switch (color_type)
1069 {
1070 case PNG_COLOR_TYPE_GRAY:
1071 result = "Gray";
1072 break;
1073 case PNG_COLOR_TYPE_GRAY_ALPHA:
1074 result = "Gray+Alpha";
1075 break;
1076 case PNG_COLOR_TYPE_PALETTE:
1077 result = "Palette";
1078 break;
1079 case PNG_COLOR_TYPE_RGB:
1080 result = "RGB";
1081 break;
1082 case PNG_COLOR_TYPE_RGB_ALPHA:
1083 result = "RGB+Alpha";
1084 break;
1085 }
1086
1087 return result;
1088}
1089
glennrpe610a072010-08-05 17:08:46 +00001090static int
glennrpcf002022011-01-30 02:38:15 +00001091Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001092{
glennrpe610a072010-08-05 17:08:46 +00001093 switch (intent)
1094 {
1095 case PerceptualIntent:
1096 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001097
glennrpe610a072010-08-05 17:08:46 +00001098 case RelativeIntent:
1099 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001100
glennrpe610a072010-08-05 17:08:46 +00001101 case SaturationIntent:
1102 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001103
glennrpe610a072010-08-05 17:08:46 +00001104 case AbsoluteIntent:
1105 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001106
glennrpe610a072010-08-05 17:08:46 +00001107 default:
1108 return -1;
1109 }
1110}
1111
1112static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001113Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001114{
glennrpcf002022011-01-30 02:38:15 +00001115 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001116 {
1117 case 0:
1118 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001119
glennrpe610a072010-08-05 17:08:46 +00001120 case 1:
1121 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001122
glennrpe610a072010-08-05 17:08:46 +00001123 case 2:
1124 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001125
glennrpe610a072010-08-05 17:08:46 +00001126 case 3:
1127 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001128
glennrpe610a072010-08-05 17:08:46 +00001129 default:
1130 return UndefinedIntent;
1131 }
1132}
1133
cristy9d8c1222012-08-10 12:34:19 +00001134static const char *
glennrp98b83d42012-07-23 02:50:31 +00001135Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1136{
1137 switch (ping_intent)
1138 {
1139 case 0:
1140 return "Perceptual Intent";
1141
1142 case 1:
1143 return "Relative Intent";
1144
1145 case 2:
1146 return "Saturation Intent";
1147
1148 case 3:
1149 return "Absolute Intent";
1150
1151 default:
1152 return "Undefined Intent";
1153 }
1154}
1155
cristybb503372010-05-27 20:51:26 +00001156static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001157{
1158 if (x > y)
1159 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001160
cristy3ed852e2009-09-05 21:47:34 +00001161 return(y);
1162}
glennrp0c3e06b2010-11-19 13:45:02 +00001163
cristyd9ecd042012-06-17 18:26:12 +00001164static const char *
glennrp5dff4352012-06-06 22:12:04 +00001165Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1166{
1167 switch (ping_colortype)
1168 {
1169 case 0:
1170 return "Grayscale";
1171
1172 case 2:
1173 return "Truecolor";
1174
1175 case 3:
1176 return "Indexed";
1177
1178 case 4:
1179 return "GrayAlpha";
1180
1181 case 6:
1182 return "RGBA";
1183
1184 default:
1185 return "UndefinedColorType";
1186 }
1187}
1188
1189
cristybb503372010-05-27 20:51:26 +00001190static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001191{
1192 if (x < y)
1193 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001194
cristy3ed852e2009-09-05 21:47:34 +00001195 return(y);
1196}
glennrpd5045b42010-03-24 12:40:35 +00001197#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001198#endif /* MAGICKCORE_PNG_DELEGATE */
1199
1200/*
1201%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1202% %
1203% %
1204% %
1205% I s M N G %
1206% %
1207% %
1208% %
1209%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1210%
1211% IsMNG() returns MagickTrue if the image format type, identified by the
1212% magick string, is MNG.
1213%
1214% The format of the IsMNG method is:
1215%
1216% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1217%
1218% A description of each parameter follows:
1219%
1220% o magick: compare image format pattern against these bytes.
1221%
1222% o length: Specifies the length of the magick string.
1223%
1224%
1225*/
1226static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1227{
1228 if (length < 8)
1229 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001230
cristy3ed852e2009-09-05 21:47:34 +00001231 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1232 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001233
cristy3ed852e2009-09-05 21:47:34 +00001234 return(MagickFalse);
1235}
1236
1237/*
1238%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1239% %
1240% %
1241% %
1242% I s J N G %
1243% %
1244% %
1245% %
1246%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1247%
1248% IsJNG() returns MagickTrue if the image format type, identified by the
1249% magick string, is JNG.
1250%
1251% The format of the IsJNG method is:
1252%
1253% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1254%
1255% A description of each parameter follows:
1256%
1257% o magick: compare image format pattern against these bytes.
1258%
1259% o length: Specifies the length of the magick string.
1260%
1261%
1262*/
1263static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1264{
1265 if (length < 8)
1266 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001267
cristy3ed852e2009-09-05 21:47:34 +00001268 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1269 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001270
cristy3ed852e2009-09-05 21:47:34 +00001271 return(MagickFalse);
1272}
1273
1274/*
1275%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1276% %
1277% %
1278% %
1279% I s P N G %
1280% %
1281% %
1282% %
1283%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1284%
1285% IsPNG() returns MagickTrue if the image format type, identified by the
1286% magick string, is PNG.
1287%
1288% The format of the IsPNG method is:
1289%
1290% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1291%
1292% A description of each parameter follows:
1293%
1294% o magick: compare image format pattern against these bytes.
1295%
1296% o length: Specifies the length of the magick string.
1297%
1298*/
1299static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1300{
1301 if (length < 8)
1302 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001303
cristy3ed852e2009-09-05 21:47:34 +00001304 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1305 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001306
cristy3ed852e2009-09-05 21:47:34 +00001307 return(MagickFalse);
1308}
1309
1310#if defined(MAGICKCORE_PNG_DELEGATE)
1311#if defined(__cplusplus) || defined(c_plusplus)
1312extern "C" {
1313#endif
1314
glennrpd5045b42010-03-24 12:40:35 +00001315#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001316static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001317{
1318 unsigned char
1319 buffer[4];
1320
1321 assert(image != (Image *) NULL);
1322 assert(image->signature == MagickSignature);
1323 buffer[0]=(unsigned char) (value >> 24);
1324 buffer[1]=(unsigned char) (value >> 16);
1325 buffer[2]=(unsigned char) (value >> 8);
1326 buffer[3]=(unsigned char) value;
1327 return((size_t) WriteBlob(image,4,buffer));
1328}
1329
1330static void PNGLong(png_bytep p,png_uint_32 value)
1331{
1332 *p++=(png_byte) ((value >> 24) & 0xff);
1333 *p++=(png_byte) ((value >> 16) & 0xff);
1334 *p++=(png_byte) ((value >> 8) & 0xff);
1335 *p++=(png_byte) (value & 0xff);
1336}
1337
glennrpa521b2f2010-10-29 04:11:03 +00001338#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001339static void PNGsLong(png_bytep p,png_int_32 value)
1340{
1341 *p++=(png_byte) ((value >> 24) & 0xff);
1342 *p++=(png_byte) ((value >> 16) & 0xff);
1343 *p++=(png_byte) ((value >> 8) & 0xff);
1344 *p++=(png_byte) (value & 0xff);
1345}
glennrpa521b2f2010-10-29 04:11:03 +00001346#endif
cristy3ed852e2009-09-05 21:47:34 +00001347
1348static void PNGShort(png_bytep p,png_uint_16 value)
1349{
1350 *p++=(png_byte) ((value >> 8) & 0xff);
1351 *p++=(png_byte) (value & 0xff);
1352}
1353
1354static void PNGType(png_bytep p,png_bytep type)
1355{
1356 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1357}
1358
glennrp03812ae2010-12-24 01:31:34 +00001359static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1360 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001361{
1362 if (logging != MagickFalse)
1363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001364 " Writing %c%c%c%c chunk, length: %.20g",
1365 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001366}
glennrpd5045b42010-03-24 12:40:35 +00001367#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001368
1369#if defined(__cplusplus) || defined(c_plusplus)
1370}
1371#endif
1372
glennrpd5045b42010-03-24 12:40:35 +00001373#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001374/*
1375%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1376% %
1377% %
1378% %
1379% R e a d P N G I m a g e %
1380% %
1381% %
1382% %
1383%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1384%
1385% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1386% Multiple-image Network Graphics (MNG) image file and returns it. It
1387% allocates the memory necessary for the new Image structure and returns a
1388% pointer to the new image or set of images.
1389%
1390% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1391%
1392% The format of the ReadPNGImage method is:
1393%
1394% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1395%
1396% A description of each parameter follows:
1397%
1398% o image_info: the image info.
1399%
1400% o exception: return any errors or warnings in this structure.
1401%
1402% To do, more or less in chronological order (as of version 5.5.2,
1403% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1404%
1405% Get 16-bit cheap transparency working.
1406%
1407% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1408%
1409% Preserve all unknown and not-yet-handled known chunks found in input
1410% PNG file and copy them into output PNG files according to the PNG
1411% copying rules.
1412%
1413% (At this point, PNG encoding should be in full MNG compliance)
1414%
1415% Provide options for choice of background to use when the MNG BACK
1416% chunk is not present or is not mandatory (i.e., leave transparent,
1417% user specified, MNG BACK, PNG bKGD)
1418%
1419% Implement LOOP/ENDL [done, but could do discretionary loops more
1420% efficiently by linking in the duplicate frames.].
1421%
1422% Decode and act on the MHDR simplicity profile (offer option to reject
1423% files or attempt to process them anyway when the profile isn't LC or VLC).
1424%
1425% Upgrade to full MNG without Delta-PNG.
1426%
1427% o BACK [done a while ago except for background image ID]
1428% o MOVE [done 15 May 1999]
1429% o CLIP [done 15 May 1999]
1430% o DISC [done 19 May 1999]
1431% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1432% o SEEK [partially done 19 May 1999 (discard function only)]
1433% o SHOW
1434% o PAST
1435% o BASI
1436% o MNG-level tEXt/iTXt/zTXt
1437% o pHYg
1438% o pHYs
1439% o sBIT
1440% o bKGD
1441% o iTXt (wait for libpng implementation).
1442%
1443% Use the scene signature to discover when an identical scene is
1444% being reused, and just point to the original image->exception instead
1445% of storing another set of pixels. This not specific to MNG
1446% but could be applied generally.
1447%
1448% Upgrade to full MNG with Delta-PNG.
1449%
1450% JNG tEXt/iTXt/zTXt
1451%
1452% We will not attempt to read files containing the CgBI chunk.
1453% They are really Xcode files meant for display on the iPhone.
1454% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001455% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001456% since irretrievable loss of color data has occurred due to the
1457% use of premultiplied alpha.
1458*/
1459
1460#if defined(__cplusplus) || defined(c_plusplus)
1461extern "C" {
1462#endif
1463
1464/*
1465 This the function that does the actual reading of data. It is
1466 the same as the one supplied in libpng, except that it receives the
1467 datastream from the ReadBlob() function instead of standard input.
1468*/
1469static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1470{
1471 Image
1472 *image;
1473
1474 image=(Image *) png_get_io_ptr(png_ptr);
1475 if (length)
1476 {
1477 png_size_t
1478 check;
1479
1480 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1481 if (check != length)
1482 {
1483 char
1484 msg[MaxTextExtent];
1485
cristy3b6fd2e2011-05-20 12:53:50 +00001486 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001487 "Expected %.20g bytes; found %.20g bytes",(double) length,
1488 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001489 png_warning(png_ptr,msg);
1490 png_error(png_ptr,"Read Exception");
1491 }
1492 }
1493}
1494
1495#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1496 !defined(PNG_MNG_FEATURES_SUPPORTED)
1497/* We use mng_get_data() instead of png_get_data() if we have a libpng
1498 * older than libpng-1.0.3a, which was the first to allow the empty
1499 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1500 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1501 * encountered after an empty PLTE, so we have to look ahead for bKGD
1502 * chunks and remove them from the datastream that is passed to libpng,
1503 * and store their contents for later use.
1504 */
1505static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1506{
1507 MngInfo
1508 *mng_info;
1509
1510 Image
1511 *image;
1512
1513 png_size_t
1514 check;
1515
cristybb503372010-05-27 20:51:26 +00001516 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001517 i;
1518
1519 i=0;
1520 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1521 image=(Image *) mng_info->image;
1522 while (mng_info->bytes_in_read_buffer && length)
1523 {
1524 data[i]=mng_info->read_buffer[i];
1525 mng_info->bytes_in_read_buffer--;
1526 length--;
1527 i++;
1528 }
1529 if (length)
1530 {
1531 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001532
cristy3ed852e2009-09-05 21:47:34 +00001533 if (check != length)
1534 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001535
cristy3ed852e2009-09-05 21:47:34 +00001536 if (length == 4)
1537 {
1538 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1539 (data[3] == 0))
1540 {
1541 check=(png_size_t) ReadBlob(image,(size_t) length,
1542 (char *) mng_info->read_buffer);
1543 mng_info->read_buffer[4]=0;
1544 mng_info->bytes_in_read_buffer=4;
1545 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1546 mng_info->found_empty_plte=MagickTrue;
1547 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1548 {
1549 mng_info->found_empty_plte=MagickFalse;
1550 mng_info->have_saved_bkgd_index=MagickFalse;
1551 }
1552 }
glennrp0fe50b42010-11-16 03:52:51 +00001553
cristy3ed852e2009-09-05 21:47:34 +00001554 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1555 (data[3] == 1))
1556 {
1557 check=(png_size_t) ReadBlob(image,(size_t) length,
1558 (char *) mng_info->read_buffer);
1559 mng_info->read_buffer[4]=0;
1560 mng_info->bytes_in_read_buffer=4;
1561 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1562 if (mng_info->found_empty_plte)
1563 {
1564 /*
1565 Skip the bKGD data byte and CRC.
1566 */
1567 check=(png_size_t)
1568 ReadBlob(image,5,(char *) mng_info->read_buffer);
1569 check=(png_size_t) ReadBlob(image,(size_t) length,
1570 (char *) mng_info->read_buffer);
1571 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1572 mng_info->have_saved_bkgd_index=MagickTrue;
1573 mng_info->bytes_in_read_buffer=0;
1574 }
1575 }
1576 }
1577 }
1578}
1579#endif
1580
1581static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1582{
1583 Image
1584 *image;
1585
1586 image=(Image *) png_get_io_ptr(png_ptr);
1587 if (length)
1588 {
1589 png_size_t
1590 check;
1591
cristybb503372010-05-27 20:51:26 +00001592 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001593
cristy3ed852e2009-09-05 21:47:34 +00001594 if (check != length)
1595 png_error(png_ptr,"WriteBlob Failed");
1596 }
1597}
1598
1599static void png_flush_data(png_structp png_ptr)
1600{
1601 (void) png_ptr;
1602}
1603
1604#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1605static int PalettesAreEqual(Image *a,Image *b)
1606{
cristybb503372010-05-27 20:51:26 +00001607 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001608 i;
1609
1610 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1611 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001612
cristy3ed852e2009-09-05 21:47:34 +00001613 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1614 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001615
cristy3ed852e2009-09-05 21:47:34 +00001616 if (a->colors != b->colors)
1617 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001618
cristybb503372010-05-27 20:51:26 +00001619 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001620 {
1621 if ((a->colormap[i].red != b->colormap[i].red) ||
1622 (a->colormap[i].green != b->colormap[i].green) ||
1623 (a->colormap[i].blue != b->colormap[i].blue))
1624 return((int) MagickFalse);
1625 }
glennrp0fe50b42010-11-16 03:52:51 +00001626
cristy3ed852e2009-09-05 21:47:34 +00001627 return((int) MagickTrue);
1628}
1629#endif
1630
1631static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1632{
1633 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1634 mng_info->exists[i] && !mng_info->frozen[i])
1635 {
1636#ifdef MNG_OBJECT_BUFFERS
1637 if (mng_info->ob[i] != (MngBuffer *) NULL)
1638 {
1639 if (mng_info->ob[i]->reference_count > 0)
1640 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001641
cristy3ed852e2009-09-05 21:47:34 +00001642 if (mng_info->ob[i]->reference_count == 0)
1643 {
1644 if (mng_info->ob[i]->image != (Image *) NULL)
1645 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001646
cristy3ed852e2009-09-05 21:47:34 +00001647 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1648 }
1649 }
1650 mng_info->ob[i]=(MngBuffer *) NULL;
1651#endif
1652 mng_info->exists[i]=MagickFalse;
1653 mng_info->invisible[i]=MagickFalse;
1654 mng_info->viewable[i]=MagickFalse;
1655 mng_info->frozen[i]=MagickFalse;
1656 mng_info->x_off[i]=0;
1657 mng_info->y_off[i]=0;
1658 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001659 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001660 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001661 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001662 }
1663}
1664
glennrp21f0e622011-01-07 16:20:57 +00001665static void MngInfoFreeStruct(MngInfo *mng_info,
1666 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001667{
glennrp21f0e622011-01-07 16:20:57 +00001668 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001669 {
cristybb503372010-05-27 20:51:26 +00001670 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001671 i;
1672
1673 for (i=1; i < MNG_MAX_OBJECTS; i++)
1674 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001675
cristy3ed852e2009-09-05 21:47:34 +00001676 if (mng_info->global_plte != (png_colorp) NULL)
1677 mng_info->global_plte=(png_colorp)
1678 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001679
cristy3ed852e2009-09-05 21:47:34 +00001680 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1681 *have_mng_structure=MagickFalse;
1682 }
1683}
1684
1685static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1686{
1687 MngBox
1688 box;
1689
1690 box=box1;
1691 if (box.left < box2.left)
1692 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001693
cristy3ed852e2009-09-05 21:47:34 +00001694 if (box.top < box2.top)
1695 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001696
cristy3ed852e2009-09-05 21:47:34 +00001697 if (box.right > box2.right)
1698 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001699
cristy3ed852e2009-09-05 21:47:34 +00001700 if (box.bottom > box2.bottom)
1701 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001702
cristy3ed852e2009-09-05 21:47:34 +00001703 return box;
1704}
1705
1706static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1707{
1708 MngBox
1709 box;
1710
1711 /*
1712 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1713 */
cristybb503372010-05-27 20:51:26 +00001714 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1715 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1716 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1717 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001718 if (delta_type != 0)
1719 {
1720 box.left+=previous_box.left;
1721 box.right+=previous_box.right;
1722 box.top+=previous_box.top;
1723 box.bottom+=previous_box.bottom;
1724 }
glennrp0fe50b42010-11-16 03:52:51 +00001725
cristy3ed852e2009-09-05 21:47:34 +00001726 return(box);
1727}
1728
1729static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1730 unsigned char *p)
1731{
1732 MngPair
1733 pair;
1734 /*
cristybb503372010-05-27 20:51:26 +00001735 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001736 */
cristy8182b072010-05-30 20:10:53 +00001737 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1738 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001739
cristy3ed852e2009-09-05 21:47:34 +00001740 if (delta_type != 0)
1741 {
1742 pair.a+=previous_pair.a;
1743 pair.b+=previous_pair.b;
1744 }
glennrp0fe50b42010-11-16 03:52:51 +00001745
cristy3ed852e2009-09-05 21:47:34 +00001746 return(pair);
1747}
1748
cristy8182b072010-05-30 20:10:53 +00001749static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001750{
cristy8182b072010-05-30 20:10:53 +00001751 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001752}
1753
cristy16ea1392012-03-21 20:38:41 +00001754typedef struct _PNGErrorInfo
cristyc82a27b2011-10-21 01:07:16 +00001755{
cristyc82a27b2011-10-21 01:07:16 +00001756 Image
1757 *image;
1758
cristy16ea1392012-03-21 20:38:41 +00001759 ExceptionInfo
1760 *exception;
1761} PNGErrorInfo;
cristyc82a27b2011-10-21 01:07:16 +00001762
cristy16ea1392012-03-21 20:38:41 +00001763static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1764{
1765 ExceptionInfo
1766 *exception;
cristyc82a27b2011-10-21 01:07:16 +00001767
cristy16ea1392012-03-21 20:38:41 +00001768 Image
1769 *image;
1770
1771 PNGErrorInfo
1772 *error_info;
1773
1774 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1775 image=error_info->image;
1776 exception=error_info->exception;
1777
1778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1779 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1780
1781 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1782 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001783
glennrpe4017e32011-01-08 17:16:09 +00001784#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001785 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1786 * are building with libpng-1.4.x and can be ignored.
1787 */
cristy3ed852e2009-09-05 21:47:34 +00001788 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001789#else
1790 png_longjmp(ping,1);
1791#endif
cristy3ed852e2009-09-05 21:47:34 +00001792}
1793
glennrpcf002022011-01-30 02:38:15 +00001794static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001795{
cristy16ea1392012-03-21 20:38:41 +00001796 ExceptionInfo
1797 *exception;
1798
cristy3ed852e2009-09-05 21:47:34 +00001799 Image
1800 *image;
1801
cristy16ea1392012-03-21 20:38:41 +00001802 PNGErrorInfo
1803 *error_info;
1804
cristy3ed852e2009-09-05 21:47:34 +00001805 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1806 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001807
cristy16ea1392012-03-21 20:38:41 +00001808 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1809 image=error_info->image;
1810 exception=error_info->exception;
1811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1812 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001813
cristy16ea1392012-03-21 20:38:41 +00001814 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001815 message,"`%s'",image->filename);
1816}
1817
1818#ifdef PNG_USER_MEM_SUPPORTED
cristya865ccd2012-07-28 00:33:10 +00001819#if PNG_LIBPNG_VER >= 14000
1820static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1821#else
1822static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1823#endif
cristy3ed852e2009-09-05 21:47:34 +00001824{
cristydf0d90e2011-12-12 01:03:55 +00001825 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001826 return((png_voidp) AcquireMagickMemory((size_t) size));
cristy3ed852e2009-09-05 21:47:34 +00001827}
1828
1829/*
1830 Free a pointer. It is removed from the list at the same time.
1831*/
glennrpcf002022011-01-30 02:38:15 +00001832static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001833{
glennrp3bd393f2011-12-21 18:54:53 +00001834 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001835 ptr=RelinquishMagickMemory(ptr);
1836 return((png_free_ptr) NULL);
1837}
1838#endif
1839
1840#if defined(__cplusplus) || defined(c_plusplus)
1841}
1842#endif
1843
1844static int
glennrpedaa0382012-04-12 14:16:21 +00001845Magick_png_read_raw_profile(png_struct *ping,Image *image,
1846 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001847{
cristybb503372010-05-27 20:51:26 +00001848 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001849 i;
1850
1851 register unsigned char
1852 *dp;
1853
1854 register png_charp
1855 sp;
1856
1857 png_uint_32
1858 length,
1859 nibbles;
1860
1861 StringInfo
1862 *profile;
1863
glennrp0c3e06b2010-11-19 13:45:02 +00001864 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001865 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1866 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1867 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1868 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1869 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1870 13,14,15};
1871
1872 sp=text[ii].text+1;
1873 /* look for newline */
1874 while (*sp != '\n')
1875 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001876
cristy3ed852e2009-09-05 21:47:34 +00001877 /* look for length */
1878 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1879 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001880
cristyf2f27272009-12-17 14:48:46 +00001881 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001882
glennrp97f90e22011-02-22 05:47:58 +00001883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1884 " length: %lu",(unsigned long) length);
1885
cristy3ed852e2009-09-05 21:47:34 +00001886 while (*sp != ' ' && *sp != '\n')
1887 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001888
cristy3ed852e2009-09-05 21:47:34 +00001889 /* allocate space */
1890 if (length == 0)
1891 {
glennrpedaa0382012-04-12 14:16:21 +00001892 png_warning(ping,"invalid profile length");
cristy3ed852e2009-09-05 21:47:34 +00001893 return(MagickFalse);
1894 }
glennrp0fe50b42010-11-16 03:52:51 +00001895
cristy8723e4b2011-09-01 13:11:19 +00001896 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001897
cristy3ed852e2009-09-05 21:47:34 +00001898 if (profile == (StringInfo *) NULL)
1899 {
glennrpedaa0382012-04-12 14:16:21 +00001900 png_warning(ping, "unable to copy profile");
cristy3ed852e2009-09-05 21:47:34 +00001901 return(MagickFalse);
1902 }
glennrp0fe50b42010-11-16 03:52:51 +00001903
cristy3ed852e2009-09-05 21:47:34 +00001904 /* copy profile, skipping white space and column 1 "=" signs */
1905 dp=GetStringInfoDatum(profile);
1906 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001907
cristybb503372010-05-27 20:51:26 +00001908 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001909 {
1910 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1911 {
1912 if (*sp == '\0')
1913 {
glennrpedaa0382012-04-12 14:16:21 +00001914 png_warning(ping, "ran out of profile data");
cristy3ed852e2009-09-05 21:47:34 +00001915 profile=DestroyStringInfo(profile);
1916 return(MagickFalse);
1917 }
1918 sp++;
1919 }
glennrp0fe50b42010-11-16 03:52:51 +00001920
cristy3ed852e2009-09-05 21:47:34 +00001921 if (i%2 == 0)
1922 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001923
cristy3ed852e2009-09-05 21:47:34 +00001924 else
1925 (*dp++)+=unhex[(int) *sp++];
1926 }
1927 /*
1928 We have already read "Raw profile type.
1929 */
cristy16ea1392012-03-21 20:38:41 +00001930 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001931 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001932
cristy3ed852e2009-09-05 21:47:34 +00001933 if (image_info->verbose)
1934 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001935
cristy3ed852e2009-09-05 21:47:34 +00001936 return MagickTrue;
1937}
1938
1939#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1940static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1941{
1942 Image
1943 *image;
1944
1945
1946 /* The unknown chunk structure contains the chunk data:
1947 png_byte name[5];
1948 png_byte *data;
1949 png_size_t size;
1950
1951 Note that libpng has already taken care of the CRC handling.
1952 */
1953
1954
1955 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1956 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1957 return(0); /* Did not recognize */
1958
1959 /* recognized vpAg */
1960
1961 if (chunk->size != 9)
1962 return(-1); /* Error return */
1963
1964 if (chunk->data[8] != 0)
1965 return(0); /* ImageMagick requires pixel units */
1966
1967 image=(Image *) png_get_user_chunk_ptr(ping);
1968
cristybb503372010-05-27 20:51:26 +00001969 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001970 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001971
cristybb503372010-05-27 20:51:26 +00001972 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001973 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1974
1975 /* Return one of the following: */
1976 /* return(-n); chunk had an error */
1977 /* return(0); did not recognize */
1978 /* return(n); success */
1979
1980 return(1);
1981
1982}
1983#endif
1984
1985/*
1986%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1987% %
1988% %
1989% %
1990% R e a d O n e P N G I m a g e %
1991% %
1992% %
1993% %
1994%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1995%
1996% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1997% (minus the 8-byte signature) and returns it. It allocates the memory
1998% necessary for the new Image structure and returns a pointer to the new
1999% image.
2000%
2001% The format of the ReadOnePNGImage method is:
2002%
2003% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2004% ExceptionInfo *exception)
2005%
2006% A description of each parameter follows:
2007%
2008% o mng_info: Specifies a pointer to a MngInfo structure.
2009%
2010% o image_info: the image info.
2011%
2012% o exception: return any errors or warnings in this structure.
2013%
2014*/
2015static Image *ReadOnePNGImage(MngInfo *mng_info,
2016 const ImageInfo *image_info, ExceptionInfo *exception)
2017{
2018 /* Read one PNG image */
2019
glennrpcc95c3f2011-04-18 16:46:48 +00002020 /* To do: Read the tIME chunk into the date:modify property */
2021 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2022
cristy3ed852e2009-09-05 21:47:34 +00002023 Image
2024 *image;
2025
2026 int
glennrp98b83d42012-07-23 02:50:31 +00002027 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
glennrpcb395ac2011-03-30 19:50:23 +00002028 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002029 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002030 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002031 num_passes,
glennrp913f9612012-06-27 17:50:00 +00002032 number_colors,
glennrpfaa852b2010-03-30 12:17:00 +00002033 pass,
2034 ping_bit_depth,
2035 ping_color_type,
glennrpfcf06162012-11-05 14:57:08 +00002036 ping_file_depth,
glennrpfaa852b2010-03-30 12:17:00 +00002037 ping_interlace_method,
2038 ping_compression_method,
2039 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002040 ping_num_trans,
2041 unit_type;
2042
2043 double
2044 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002045
2046 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002047 logging,
glennrp98b83d42012-07-23 02:50:31 +00002048 ping_found_cHRM,
2049 ping_found_gAMA,
2050 ping_found_iCCP,
2051 ping_found_sRGB,
cristy3ed852e2009-09-05 21:47:34 +00002052 status;
2053
cristy16ea1392012-03-21 20:38:41 +00002054 PixelInfo
2055 transparent_color;
2056
2057 PNGErrorInfo
2058 error_info;
2059
glennrpfaa852b2010-03-30 12:17:00 +00002060 png_bytep
2061 ping_trans_alpha;
2062
2063 png_color_16p
2064 ping_background,
2065 ping_trans_color;
2066
cristy3ed852e2009-09-05 21:47:34 +00002067 png_info
2068 *end_info,
2069 *ping_info;
2070
2071 png_struct
2072 *ping;
2073
2074 png_textp
2075 text;
2076
glennrpfaa852b2010-03-30 12:17:00 +00002077 png_uint_32
2078 ping_height,
2079 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002080 x_resolution,
2081 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002082
cristy16ea1392012-03-21 20:38:41 +00002083 QuantumInfo
2084 *quantum_info;
2085
cristybb503372010-05-27 20:51:26 +00002086 ssize_t
cristy756ae432011-11-19 02:18:25 +00002087 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002088 y;
2089
2090 register unsigned char
2091 *p;
2092
cristybb503372010-05-27 20:51:26 +00002093 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002094 i,
2095 x;
2096
cristy16ea1392012-03-21 20:38:41 +00002097 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002098 *q;
2099
2100 size_t
glennrp39992b42010-11-14 00:03:43 +00002101 length,
cristy3ed852e2009-09-05 21:47:34 +00002102 row_offset;
2103
cristyeb3b22a2011-03-31 20:16:11 +00002104 ssize_t
2105 j;
2106
cristy75fc68f2012-10-08 16:26:00 +00002107 unsigned char
2108 *volatile ping_pixels;
cristy55b78b52012-10-08 14:01:27 +00002109
glennrp629960f2012-05-29 19:13:52 +00002110#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002111 png_byte unused_chunks[]=
2112 {
2113 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2114 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2115 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2116 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2117 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2118 116, 73, 77, 69, (png_byte) '\0', /* tIME */
glennrp629960f2012-05-29 19:13:52 +00002119#ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2120 /* ignore the APNG chunks */
2121 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2122 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2123 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2124#endif
cristy3ed852e2009-09-05 21:47:34 +00002125 };
2126#endif
2127
2128 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002129 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002130
glennrp25c1e2b2010-03-25 01:39:56 +00002131#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002132 if (image_info->verbose)
2133 printf("Your PNG library (libpng-%s) is rather old.\n",
2134 PNG_LIBPNG_VER_STRING);
2135#endif
2136
glennrp61b4c952009-11-10 20:40:41 +00002137#if (PNG_LIBPNG_VER >= 10400)
2138# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2139 if (image_info->verbose)
2140 {
2141 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2142 PNG_LIBPNG_VER_STRING);
2143 printf("Please update it.\n");
2144 }
2145# endif
2146#endif
2147
cristy16ea1392012-03-21 20:38:41 +00002148
2149 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002150 image=mng_info->image;
2151
glennrpa6a06632011-01-19 15:15:34 +00002152 if (logging != MagickFalse)
glennrp98b83d42012-07-23 02:50:31 +00002153 {
glennrpa6a06632011-01-19 15:15:34 +00002154 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00002155 " image->alpha_trait=%d",(int) image->alpha_trait);
glennrpa6a06632011-01-19 15:15:34 +00002156
glennrp98b83d42012-07-23 02:50:31 +00002157 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2158 " image->rendering_intent=%d",(int) image->rendering_intent);
glennrpe88af772012-08-22 13:59:50 +00002159
2160 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2161 " image->colorspace=%d",(int) image->colorspace);
glennrp98b83d42012-07-23 02:50:31 +00002162 }
2163 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2164
glennrp0e319732011-01-25 21:53:13 +00002165 /* Set to an out-of-range color unless tRNS chunk is present */
2166 transparent_color.red=65537;
2167 transparent_color.green=65537;
2168 transparent_color.blue=65537;
cristy16ea1392012-03-21 20:38:41 +00002169 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002170
glennrp913f9612012-06-27 17:50:00 +00002171 number_colors=0;
glennrpcb395ac2011-03-30 19:50:23 +00002172 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002173 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002174 num_raw_profiles = 0;
2175
glennrp98b83d42012-07-23 02:50:31 +00002176 ping_found_cHRM = MagickFalse;
2177 ping_found_gAMA = MagickFalse;
2178 ping_found_iCCP = MagickFalse;
2179 ping_found_sRGB = MagickFalse;
2180
cristy3ed852e2009-09-05 21:47:34 +00002181 /*
2182 Allocate the PNG structures
2183 */
2184#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00002185 error_info.image=image;
2186 error_info.exception=exception;
2187 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002188 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2189 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002190#else
cristy16ea1392012-03-21 20:38:41 +00002191 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002192 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002193#endif
2194 if (ping == (png_struct *) NULL)
2195 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002196
cristy3ed852e2009-09-05 21:47:34 +00002197 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002198
cristy3ed852e2009-09-05 21:47:34 +00002199 if (ping_info == (png_info *) NULL)
2200 {
2201 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2202 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2203 }
glennrp0fe50b42010-11-16 03:52:51 +00002204
cristy3ed852e2009-09-05 21:47:34 +00002205 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002206
cristy3ed852e2009-09-05 21:47:34 +00002207 if (end_info == (png_info *) NULL)
2208 {
2209 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2210 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2211 }
glennrp0fe50b42010-11-16 03:52:51 +00002212
glennrpcf002022011-01-30 02:38:15 +00002213 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002214
glennrpfaa852b2010-03-30 12:17:00 +00002215 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002216 {
2217 /*
2218 PNG image is corrupt.
2219 */
2220 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002221
2222#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002223 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002224#endif
glennrpedaa0382012-04-12 14:16:21 +00002225
2226 if (ping_pixels != (unsigned char *) NULL)
2227 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2228
cristy3ed852e2009-09-05 21:47:34 +00002229 if (logging != MagickFalse)
2230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2231 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002232
cristy3ed852e2009-09-05 21:47:34 +00002233 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002234 {
cristy16ea1392012-03-21 20:38:41 +00002235 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002236 image->columns=0;
2237 }
glennrp0fe50b42010-11-16 03:52:51 +00002238
cristy3ed852e2009-09-05 21:47:34 +00002239 return(GetFirstImageInList(image));
2240 }
glennrpedaa0382012-04-12 14:16:21 +00002241
2242 /* { For navigation to end of SETJMP-protected block. Within this
2243 * block, use png_error() instead of Throwing an Exception, to ensure
2244 * that libpng is able to clean up, and that the semaphore is unlocked.
2245 */
2246
2247#ifdef PNG_SETJMP_NOT_THREAD_SAFE
2248 LockSemaphoreInfo(ping_semaphore);
2249#endif
2250
cristy3ed852e2009-09-05 21:47:34 +00002251 /*
2252 Prepare PNG for reading.
2253 */
glennrpfaa852b2010-03-30 12:17:00 +00002254
cristy3ed852e2009-09-05 21:47:34 +00002255 mng_info->image_found++;
2256 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002257
cristy3ed852e2009-09-05 21:47:34 +00002258 if (LocaleCompare(image_info->magick,"MNG") == 0)
2259 {
2260#if defined(PNG_MNG_FEATURES_SUPPORTED)
2261 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2262 png_set_read_fn(ping,image,png_get_data);
2263#else
2264#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2265 png_permit_empty_plte(ping,MagickTrue);
2266 png_set_read_fn(ping,image,png_get_data);
2267#else
2268 mng_info->image=image;
2269 mng_info->bytes_in_read_buffer=0;
2270 mng_info->found_empty_plte=MagickFalse;
2271 mng_info->have_saved_bkgd_index=MagickFalse;
2272 png_set_read_fn(ping,mng_info,mng_get_data);
2273#endif
2274#endif
2275 }
glennrp0fe50b42010-11-16 03:52:51 +00002276
cristy3ed852e2009-09-05 21:47:34 +00002277 else
2278 png_set_read_fn(ping,image,png_get_data);
2279
2280#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2281 /* Ignore unused chunks and all unknown chunks except for vpAg */
2282 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2283 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2284 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2285 (int)sizeof(unused_chunks)/5);
2286 /* Callback for other unknown chunks */
2287 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2288#endif
2289
glennrp9bf97b62012-06-06 21:03:14 +00002290#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2291 /* Disable new libpng-1.5.10 feature */
2292 png_set_check_for_invalid_index (ping, 0);
2293#endif
2294
glennrp991e92a2010-01-28 03:09:00 +00002295#if (PNG_LIBPNG_VER < 10400)
2296# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2297 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002298 /* Disable thread-unsafe features of pnggccrd */
2299 if (png_access_version_number() >= 10200)
2300 {
2301 png_uint_32 mmx_disable_mask=0;
2302 png_uint_32 asm_flags;
2303
2304 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2305 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2306 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2307 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2308 asm_flags=png_get_asm_flags(ping);
2309 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2310 }
glennrp991e92a2010-01-28 03:09:00 +00002311# endif
cristy3ed852e2009-09-05 21:47:34 +00002312#endif
2313
2314 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002315
2316 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2317 &ping_bit_depth,&ping_color_type,
2318 &ping_interlace_method,&ping_compression_method,
2319 &ping_filter_method);
2320
glennrpfcf06162012-11-05 14:57:08 +00002321 ping_file_depth = ping_bit_depth;
2322
glennrpfaa852b2010-03-30 12:17:00 +00002323 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2324 &ping_trans_color);
2325
2326 (void) png_get_bKGD(ping, ping_info, &ping_background);
2327
2328 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002329 {
glennrpfcf06162012-11-05 14:57:08 +00002330 png_set_packing(ping);
2331 ping_bit_depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00002332 }
glennrpfaa852b2010-03-30 12:17:00 +00002333
2334 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002335 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002336 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
glennrp98b83d42012-07-23 02:50:31 +00002337
cristy176b29a2012-06-21 13:35:15 +00002338 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2339 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2340 {
2341 image->rendering_intent=UndefinedIntent;
glennrp98b83d42012-07-23 02:50:31 +00002342 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
cristy176b29a2012-06-21 13:35:15 +00002343 image->gamma=1.000;
2344 (void) ResetMagickMemory(&image->chromaticity,0,
2345 sizeof(image->chromaticity));
2346 }
glennrp98b83d42012-07-23 02:50:31 +00002347
cristy3ed852e2009-09-05 21:47:34 +00002348 if (logging != MagickFalse)
2349 {
2350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002351 " PNG width: %.20g, height: %.20g",
2352 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002353
cristy3ed852e2009-09-05 21:47:34 +00002354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2355 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002356 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002357
cristy3ed852e2009-09-05 21:47:34 +00002358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2359 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002360 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002361
cristy3ed852e2009-09-05 21:47:34 +00002362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2363 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002364 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002365 }
2366
glennrp98b83d42012-07-23 02:50:31 +00002367 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2368 {
2369 ping_found_gAMA=MagickTrue;
2370 if (logging != MagickFalse)
2371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2372 " Found PNG gAMA chunk.");
2373 }
2374
2375 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2376 {
2377 ping_found_cHRM=MagickTrue;
2378 if (logging != MagickFalse)
2379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2380 " Found PNG cHRM chunk.");
2381 }
2382
2383 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2384 {
2385 ping_found_iCCP=MagickTrue;
2386 if (logging != MagickFalse)
2387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2388 " Found PNG iCCP chunk.");
2389 }
2390
2391 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2392 {
2393 ping_found_sRGB=MagickTrue;
2394 if (logging != MagickFalse)
2395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2396 " Found PNG sRGB chunk.");
2397 }
2398
glennrpfaa852b2010-03-30 12:17:00 +00002399#ifdef PNG_READ_iCCP_SUPPORTED
2400 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002401 {
2402 int
2403 compression;
2404
glennrpe4017e32011-01-08 17:16:09 +00002405#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002406 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002407 info;
2408#else
2409 png_bytep
2410 info;
2411#endif
2412
2413 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002414 name;
2415
2416 png_uint_32
2417 profile_length;
2418
2419 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2420 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002421
cristy3ed852e2009-09-05 21:47:34 +00002422 if (profile_length != 0)
2423 {
2424 StringInfo
2425 *profile;
2426
2427 if (logging != MagickFalse)
2428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2429 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002430 profile=BlobToStringInfo(info,profile_length);
2431 if (profile == (StringInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00002432 {
2433 png_warning(ping, "ICC profile is NULL");
2434 profile=DestroyStringInfo(profile);
2435 }
2436 else
2437 {
2438 (void) SetImageProfile(image,"icc",profile,exception);
2439 profile=DestroyStringInfo(profile);
2440 }
cristy3ed852e2009-09-05 21:47:34 +00002441 }
2442 }
2443#endif
2444#if defined(PNG_READ_sRGB_SUPPORTED)
2445 {
cristy3ed852e2009-09-05 21:47:34 +00002446 if (mng_info->have_global_srgb)
cristy2ea7a8e2012-02-08 01:04:50 +00002447 {
cristy2ea7a8e2012-02-08 01:04:50 +00002448 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2449 (mng_info->global_srgb_intent);
2450 }
glennrp0fe50b42010-11-16 03:52:51 +00002451
cristy3ed852e2009-09-05 21:47:34 +00002452 if (png_get_sRGB(ping,ping_info,&intent))
2453 {
glennrpcf002022011-01-30 02:38:15 +00002454 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2455 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002456
cristy3ed852e2009-09-05 21:47:34 +00002457 if (logging != MagickFalse)
2458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002459 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002460 }
2461 }
2462#endif
2463 {
glennrpfaa852b2010-03-30 12:17:00 +00002464 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2465 if (mng_info->have_global_gama)
2466 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002467
cristy3ed852e2009-09-05 21:47:34 +00002468 if (png_get_gAMA(ping,ping_info,&file_gamma))
2469 {
2470 image->gamma=(float) file_gamma;
2471 if (logging != MagickFalse)
2472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2473 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2474 }
2475 }
glennrp98b83d42012-07-23 02:50:31 +00002476
glennrpfaa852b2010-03-30 12:17:00 +00002477 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2478 {
2479 if (mng_info->have_global_chrm != MagickFalse)
2480 {
2481 (void) png_set_cHRM(ping,ping_info,
2482 mng_info->global_chrm.white_point.x,
2483 mng_info->global_chrm.white_point.y,
2484 mng_info->global_chrm.red_primary.x,
2485 mng_info->global_chrm.red_primary.y,
2486 mng_info->global_chrm.green_primary.x,
2487 mng_info->global_chrm.green_primary.y,
2488 mng_info->global_chrm.blue_primary.x,
2489 mng_info->global_chrm.blue_primary.y);
2490 }
2491 }
glennrp0fe50b42010-11-16 03:52:51 +00002492
glennrpfaa852b2010-03-30 12:17:00 +00002493 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002494 {
2495 (void) png_get_cHRM(ping,ping_info,
2496 &image->chromaticity.white_point.x,
2497 &image->chromaticity.white_point.y,
2498 &image->chromaticity.red_primary.x,
2499 &image->chromaticity.red_primary.y,
2500 &image->chromaticity.green_primary.x,
2501 &image->chromaticity.green_primary.y,
2502 &image->chromaticity.blue_primary.x,
2503 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002504
cristy3ed852e2009-09-05 21:47:34 +00002505 }
glennrp0fe50b42010-11-16 03:52:51 +00002506
glennrpe610a072010-08-05 17:08:46 +00002507 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002508 {
glennrpe610a072010-08-05 17:08:46 +00002509 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002510 Magick_RenderingIntent_to_PNG_RenderingIntent
2511 (image->rendering_intent));
cristyda7803d2012-05-03 01:22:45 +00002512 png_set_gAMA(ping,ping_info,1.000f/2.200f);
glennrpfaa852b2010-03-30 12:17:00 +00002513 png_set_cHRM(ping,ping_info,
2514 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2515 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002516 }
cristy3ed852e2009-09-05 21:47:34 +00002517#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002518 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002519 {
cristy905ef802011-02-23 00:29:18 +00002520 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2521 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002522
cristy3ed852e2009-09-05 21:47:34 +00002523 if (logging != MagickFalse)
2524 if (image->page.x || image->page.y)
2525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002526 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2527 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002528 }
2529#endif
2530#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002531 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2532 {
2533 if (mng_info->have_global_phys)
2534 {
2535 png_set_pHYs(ping,ping_info,
2536 mng_info->global_x_pixels_per_unit,
2537 mng_info->global_y_pixels_per_unit,
2538 mng_info->global_phys_unit_type);
2539 }
2540 }
2541
2542 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002543 {
cristy3ed852e2009-09-05 21:47:34 +00002544 /*
2545 Set image resolution.
2546 */
2547 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002548 &unit_type);
cristy16ea1392012-03-21 20:38:41 +00002549 image->resolution.x=(double) x_resolution;
2550 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002551
cristy3ed852e2009-09-05 21:47:34 +00002552 if (unit_type == PNG_RESOLUTION_METER)
2553 {
2554 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00002555 image->resolution.x=(double) x_resolution/100.0;
2556 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002557 }
glennrp0fe50b42010-11-16 03:52:51 +00002558
cristy3ed852e2009-09-05 21:47:34 +00002559 if (logging != MagickFalse)
2560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002561 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2562 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002563 }
cristy3ed852e2009-09-05 21:47:34 +00002564#endif
glennrp823b55c2011-03-14 18:46:46 +00002565
glennrpfaa852b2010-03-30 12:17:00 +00002566 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002567 {
cristy3ed852e2009-09-05 21:47:34 +00002568 png_colorp
2569 palette;
2570
2571 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002572
cristy3ed852e2009-09-05 21:47:34 +00002573 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002574 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002575 {
2576 if (mng_info->global_plte_length)
2577 {
2578 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2579 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002580
glennrpfaa852b2010-03-30 12:17:00 +00002581 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrpedaa0382012-04-12 14:16:21 +00002582 {
cristy3ed852e2009-09-05 21:47:34 +00002583 if (mng_info->global_trns_length)
2584 {
glennrpedaa0382012-04-12 14:16:21 +00002585 png_warning(ping,
2586 "global tRNS has more entries than global PLTE");
cristy3ed852e2009-09-05 21:47:34 +00002587 }
glennrpedaa0382012-04-12 14:16:21 +00002588 else
2589 {
2590 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2591 (int) mng_info->global_trns_length,NULL);
2592 }
2593 }
glennrpbfd9e612011-04-22 14:02:20 +00002594#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002595 if (
2596#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2597 mng_info->have_saved_bkgd_index ||
2598#endif
glennrpfaa852b2010-03-30 12:17:00 +00002599 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002600 {
2601 png_color_16
2602 background;
2603
2604#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2605 if (mng_info->have_saved_bkgd_index)
2606 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002607#endif
glennrpfaa852b2010-03-30 12:17:00 +00002608 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2609 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002610
cristy3ed852e2009-09-05 21:47:34 +00002611 background.red=(png_uint_16)
2612 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002613
cristy3ed852e2009-09-05 21:47:34 +00002614 background.green=(png_uint_16)
2615 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002616
cristy3ed852e2009-09-05 21:47:34 +00002617 background.blue=(png_uint_16)
2618 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002619
glennrpc6c391a2011-04-27 02:23:56 +00002620 background.gray=(png_uint_16)
2621 mng_info->global_plte[background.index].green;
2622
cristy3ed852e2009-09-05 21:47:34 +00002623 png_set_bKGD(ping,ping_info,&background);
2624 }
2625#endif
2626 }
2627 else
glennrpedaa0382012-04-12 14:16:21 +00002628 png_error(ping,"No global PLTE in file");
cristy3ed852e2009-09-05 21:47:34 +00002629 }
2630 }
2631
glennrpbfd9e612011-04-22 14:02:20 +00002632#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002633 if (mng_info->have_global_bkgd &&
2634 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002635 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002636
glennrpfaa852b2010-03-30 12:17:00 +00002637 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002638 {
glennrpbfd9e612011-04-22 14:02:20 +00002639 unsigned int
2640 bkgd_scale;
2641
cristy3ed852e2009-09-05 21:47:34 +00002642 /*
2643 Set image background color.
2644 */
2645 if (logging != MagickFalse)
2646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2647 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002648
glennrpbfd9e612011-04-22 14:02:20 +00002649 /* Scale background components to 16-bit, then scale
2650 * to quantum depth
2651 */
2652 if (logging != MagickFalse)
2653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2654 " raw ping_background=(%d,%d,%d).",ping_background->red,
2655 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002656
glennrpbfd9e612011-04-22 14:02:20 +00002657 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002658
glennrpfcf06162012-11-05 14:57:08 +00002659 if (ping_file_depth == 1)
glennrpbfd9e612011-04-22 14:02:20 +00002660 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002661
glennrpfcf06162012-11-05 14:57:08 +00002662 else if (ping_file_depth == 2)
glennrpbfd9e612011-04-22 14:02:20 +00002663 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002664
glennrpfcf06162012-11-05 14:57:08 +00002665 else if (ping_file_depth == 4)
glennrpbfd9e612011-04-22 14:02:20 +00002666 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002667
glennrpfcf06162012-11-05 14:57:08 +00002668 if (ping_file_depth <= 8)
glennrpbfd9e612011-04-22 14:02:20 +00002669 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002670
glennrpbfd9e612011-04-22 14:02:20 +00002671 ping_background->red *= bkgd_scale;
2672 ping_background->green *= bkgd_scale;
2673 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002674
glennrpbfd9e612011-04-22 14:02:20 +00002675 if (logging != MagickFalse)
2676 {
glennrp2cbb4482010-06-02 04:37:24 +00002677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2678 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002679
glennrp2cbb4482010-06-02 04:37:24 +00002680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2681 " ping_background=(%d,%d,%d).",ping_background->red,
2682 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002683 }
glennrp2cbb4482010-06-02 04:37:24 +00002684
glennrpbfd9e612011-04-22 14:02:20 +00002685 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002686 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002687
glennrpbfd9e612011-04-22 14:02:20 +00002688 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002689 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002690
glennrpbfd9e612011-04-22 14:02:20 +00002691 image->background_color.blue=
2692 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002693
cristy16ea1392012-03-21 20:38:41 +00002694 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002695
glennrpbfd9e612011-04-22 14:02:20 +00002696 if (logging != MagickFalse)
2697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2698 " image->background_color=(%.20g,%.20g,%.20g).",
2699 (double) image->background_color.red,
2700 (double) image->background_color.green,
2701 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002702 }
glennrpbfd9e612011-04-22 14:02:20 +00002703#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002704
glennrpfaa852b2010-03-30 12:17:00 +00002705 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002706 {
2707 /*
glennrpa6a06632011-01-19 15:15:34 +00002708 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002709 */
2710 int
2711 max_sample;
2712
cristy35ef8242010-06-03 16:24:13 +00002713 size_t
2714 one=1;
2715
cristy3ed852e2009-09-05 21:47:34 +00002716 if (logging != MagickFalse)
2717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2718 " Reading PNG tRNS chunk.");
2719
glennrpfcf06162012-11-05 14:57:08 +00002720 max_sample = (int) ((one << ping_file_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002721
glennrpfaa852b2010-03-30 12:17:00 +00002722 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2723 (int)ping_trans_color->gray > max_sample) ||
2724 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2725 ((int)ping_trans_color->red > max_sample ||
2726 (int)ping_trans_color->green > max_sample ||
2727 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002728 {
2729 if (logging != MagickFalse)
2730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2731 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002732 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002733 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy8a46d822012-08-28 23:32:39 +00002734 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00002735 }
2736 else
2737 {
glennrpa6a06632011-01-19 15:15:34 +00002738 int
2739 scale_to_short;
2740
glennrpfcf06162012-11-05 14:57:08 +00002741 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
glennrpa6a06632011-01-19 15:15:34 +00002742
2743 /* Scale transparent_color to short */
2744 transparent_color.red= scale_to_short*ping_trans_color->red;
2745 transparent_color.green= scale_to_short*ping_trans_color->green;
2746 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy16ea1392012-03-21 20:38:41 +00002747 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002748
glennrpfaa852b2010-03-30 12:17:00 +00002749 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002750 {
glennrp0f111982010-07-07 20:18:33 +00002751 if (logging != MagickFalse)
2752 {
2753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2754 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002755
glennrp0f111982010-07-07 20:18:33 +00002756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00002757 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002758 }
cristy16ea1392012-03-21 20:38:41 +00002759 transparent_color.red=transparent_color.alpha;
2760 transparent_color.green=transparent_color.alpha;
2761 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002762 }
2763 }
2764 }
2765#if defined(PNG_READ_sBIT_SUPPORTED)
2766 if (mng_info->have_global_sbit)
2767 {
glennrpfaa852b2010-03-30 12:17:00 +00002768 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002769 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2770 }
2771#endif
2772 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002773
cristy3ed852e2009-09-05 21:47:34 +00002774 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002775
2776 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2777
cristy3ed852e2009-09-05 21:47:34 +00002778 /*
2779 Initialize image structure.
2780 */
2781 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002782 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002783 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002784 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002785 if (mng_info->mng_type == 0)
2786 {
glennrpfaa852b2010-03-30 12:17:00 +00002787 mng_info->mng_width=ping_width;
2788 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002789 mng_info->frame=mng_info->image_box;
2790 mng_info->clip=mng_info->image_box;
2791 }
glennrp0fe50b42010-11-16 03:52:51 +00002792
cristy3ed852e2009-09-05 21:47:34 +00002793 else
2794 {
2795 image->page.y=mng_info->y_off[mng_info->object_id];
2796 }
glennrp0fe50b42010-11-16 03:52:51 +00002797
cristy3ed852e2009-09-05 21:47:34 +00002798 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002799 image->columns=ping_width;
2800 image->rows=ping_height;
glennrpa6c5d342012-05-14 13:21:17 +00002801
cristy16ea1392012-03-21 20:38:41 +00002802 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2803 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
glennrp8d0bca52012-05-17 02:33:23 +00002804 {
glennrpe88af772012-08-22 13:59:50 +00002805 if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
2806 image->gamma == 1.0) &&
glennrp8d0bca52012-05-17 02:33:23 +00002807 !png_get_valid(ping,ping_info,PNG_INFO_cHRM) &&
2808 !png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2809 {
2810 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
glennrpe88af772012-08-22 13:59:50 +00002811 * image->colorspace to GRAY, and reset image->chromaticity.
glennrp8d0bca52012-05-17 02:33:23 +00002812 */
2813 SetImageColorspace(image,GRAYColorspace,exception);
2814 }
glennrp8d0bca52012-05-17 02:33:23 +00002815 }
glennrpe88af772012-08-22 13:59:50 +00002816
2817 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2818 " image->colorspace=%d",(int) image->colorspace);
glennrpa6c5d342012-05-14 13:21:17 +00002819
glennrpfaa852b2010-03-30 12:17:00 +00002820 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrp32340ff2012-08-25 17:36:19 +00002821 ((int) ping_bit_depth < 16 &&
2822 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002823 {
cristybefe4d22010-06-07 01:18:58 +00002824 size_t
2825 one;
2826
cristy3ed852e2009-09-05 21:47:34 +00002827 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002828 one=1;
glennrpfcf06162012-11-05 14:57:08 +00002829 image->colors=one << ping_file_depth;
cristy3ed852e2009-09-05 21:47:34 +00002830#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2831 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002832 image->colors=256;
2833#else
2834 if (image->colors > 65536L)
2835 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002836#endif
glennrpfaa852b2010-03-30 12:17:00 +00002837 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002838 {
cristy3ed852e2009-09-05 21:47:34 +00002839 png_colorp
2840 palette;
2841
2842 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002843 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002844
cristy3ed852e2009-09-05 21:47:34 +00002845 if (logging != MagickFalse)
2846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2847 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2848 }
2849 }
2850
2851 if (image->storage_class == PseudoClass)
2852 {
2853 /*
2854 Initialize image colormap.
2855 */
cristy16ea1392012-03-21 20:38:41 +00002856 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
glennrpedaa0382012-04-12 14:16:21 +00002857 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00002858
glennrpfaa852b2010-03-30 12:17:00 +00002859 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002860 {
cristy3ed852e2009-09-05 21:47:34 +00002861 png_colorp
2862 palette;
2863
2864 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002865
glennrp6af6cf12011-04-22 13:05:16 +00002866 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002867 {
2868 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2869 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2870 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2871 }
glennrp6af6cf12011-04-22 13:05:16 +00002872
glennrp67b9c1a2011-04-22 18:47:36 +00002873 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002874 {
2875 image->colormap[i].red=0;
2876 image->colormap[i].green=0;
2877 image->colormap[i].blue=0;
2878 }
cristy3ed852e2009-09-05 21:47:34 +00002879 }
glennrp0fe50b42010-11-16 03:52:51 +00002880
cristy3ed852e2009-09-05 21:47:34 +00002881 else
2882 {
cristybb503372010-05-27 20:51:26 +00002883 size_t
cristy3ed852e2009-09-05 21:47:34 +00002884 scale;
2885
glennrpfcf06162012-11-05 14:57:08 +00002886 scale=(QuantumRange/((1UL << ping_file_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002887
cristy3ed852e2009-09-05 21:47:34 +00002888 if (scale < 1)
2889 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002890
cristybb503372010-05-27 20:51:26 +00002891 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002892 {
2893 image->colormap[i].red=(Quantum) (i*scale);
2894 image->colormap[i].green=(Quantum) (i*scale);
2895 image->colormap[i].blue=(Quantum) (i*scale);
2896 }
2897 }
2898 }
glennrp147bc912011-03-30 18:47:21 +00002899
glennrpcb395ac2011-03-30 19:50:23 +00002900 /* Set some properties for reporting by "identify" */
2901 {
glennrp147bc912011-03-30 18:47:21 +00002902 char
2903 msg[MaxTextExtent];
2904
glennrpfcf06162012-11-05 14:57:08 +00002905 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
glennrp147bc912011-03-30 18:47:21 +00002906 ping_interlace_method in value */
2907
cristy3b6fd2e2011-05-20 12:53:50 +00002908 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002909 "%d, %d",(int) ping_width, (int) ping_height);
cristy16ea1392012-03-21 20:38:41 +00002910 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002911
glennrpfcf06162012-11-05 14:57:08 +00002912 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
cristy16ea1392012-03-21 20:38:41 +00002913 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002914
glennrp5dff4352012-06-06 22:12:04 +00002915 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
2916 (int) ping_color_type,
2917 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
cristy16ea1392012-03-21 20:38:41 +00002918 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002919
glennrp913f9612012-06-27 17:50:00 +00002920 if (ping_interlace_method == 0)
2921 {
2922 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
2923 (int) ping_interlace_method);
2924 }
2925 else if (ping_interlace_method == 1)
2926 {
2927 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
2928 (int) ping_interlace_method);
2929 }
2930 else
2931 {
2932 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
2933 (int) ping_interlace_method);
2934 }
2935 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
2936
2937 if (number_colors != 0)
2938 {
2939 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2940 (int) number_colors);
2941 (void) SetImageProperty(image,"png:PLTE.number_colors ",msg,
2942 exception);
2943 }
glennrpcb395ac2011-03-30 19:50:23 +00002944 }
glennrp147bc912011-03-30 18:47:21 +00002945
cristy3ed852e2009-09-05 21:47:34 +00002946 /*
2947 Read image scanlines.
2948 */
2949 if (image->delay != 0)
2950 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002951
glennrp0ca69b12010-07-26 01:57:52 +00002952 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002953 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2954 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002955 {
glennrp1b888c42012-03-02 15:18:14 +00002956 /* This happens later in non-ping decodes */
2957 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2958 image->storage_class=DirectClass;
2959
cristy3ed852e2009-09-05 21:47:34 +00002960 if (logging != MagickFalse)
2961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002962 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002963 mng_info->scenes_found-1);
2964 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002965
2966#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002967 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002968#endif
glennrpedaa0382012-04-12 14:16:21 +00002969
cristy3ed852e2009-09-05 21:47:34 +00002970 if (logging != MagickFalse)
2971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2972 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002973
cristy3ed852e2009-09-05 21:47:34 +00002974 return(image);
2975 }
glennrp0fe50b42010-11-16 03:52:51 +00002976
cristy3ed852e2009-09-05 21:47:34 +00002977 if (logging != MagickFalse)
2978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2979 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002980
cristy3ed852e2009-09-05 21:47:34 +00002981 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002982 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2983 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002984
cristy3ed852e2009-09-05 21:47:34 +00002985 else
glennrpcf002022011-01-30 02:38:15 +00002986 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2987 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002988
glennrpcf002022011-01-30 02:38:15 +00002989 if (ping_pixels == (unsigned char *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00002990 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00002991
cristy3ed852e2009-09-05 21:47:34 +00002992 if (logging != MagickFalse)
2993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2994 " Converting PNG pixels to pixel packets");
2995 /*
2996 Convert PNG pixels to pixel packets.
2997 */
cristy16ea1392012-03-21 20:38:41 +00002998 quantum_info=AcquireQuantumInfo(image_info,image);
2999
3000 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003001 png_error(ping,"Failed to allocate quantum_info");
glennrp0fe50b42010-11-16 03:52:51 +00003002
glennrp4b840d72012-11-22 16:01:16 +00003003 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3004
glennrpc8cbc5d2011-01-01 00:12:34 +00003005 {
3006
3007 MagickBooleanType
3008 found_transparent_pixel;
3009
3010 found_transparent_pixel=MagickFalse;
3011
cristy3ed852e2009-09-05 21:47:34 +00003012 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00003013 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003014 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00003015 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003016 /*
3017 Convert image to DirectClass pixel packets.
3018 */
cristy8a46d822012-08-28 23:32:39 +00003019 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
glennrpc8cbc5d2011-01-01 00:12:34 +00003020 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3021 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
cristyb0a657e2012-08-29 00:45:37 +00003022 BlendPixelTrait : UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00003023
glennrpc8cbc5d2011-01-01 00:12:34 +00003024 for (y=0; y < (ssize_t) image->rows; y++)
3025 {
3026 if (num_passes > 1)
3027 row_offset=ping_rowbytes*y;
3028
3029 else
3030 row_offset=0;
3031
glennrpcf002022011-01-30 02:38:15 +00003032 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc5c31be2012-11-07 18:42:18 +00003033
3034 if (pass < num_passes-1)
3035 continue;
3036
cristy862a33c2012-05-17 22:49:37 +00003037 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003038
cristy16ea1392012-03-21 20:38:41 +00003039 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00003040 break;
3041
cristy16ea1392012-03-21 20:38:41 +00003042 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3043 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3044 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003045
cristy16ea1392012-03-21 20:38:41 +00003046 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3047 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3048 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003049
cristy16ea1392012-03-21 20:38:41 +00003050 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3051 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3052 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003053
cristy16ea1392012-03-21 20:38:41 +00003054 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3055 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3056 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003057
cristy16ea1392012-03-21 20:38:41 +00003058 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3059 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3060 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00003061
glennrpc8cbc5d2011-01-01 00:12:34 +00003062 if (found_transparent_pixel == MagickFalse)
3063 {
3064 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00003065 if (y== 0 && logging != MagickFalse)
3066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3067 " Looking for cheap transparent pixel");
3068
glennrpc8cbc5d2011-01-01 00:12:34 +00003069 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3070 {
glennrp5aa37f62011-01-02 03:07:57 +00003071 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3072 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy16ea1392012-03-21 20:38:41 +00003073 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00003074 {
glennrpa6a06632011-01-19 15:15:34 +00003075 if (logging != MagickFalse)
3076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3077 " ...got one.");
3078
glennrpc8cbc5d2011-01-01 00:12:34 +00003079 found_transparent_pixel = MagickTrue;
3080 break;
3081 }
glennrp4f25bd02011-01-01 18:51:28 +00003082 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3083 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
cristy16ea1392012-03-21 20:38:41 +00003084 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3085 transparent_color.red &&
3086 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3087 transparent_color.green &&
3088 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3089 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00003090 {
glennrpa6a06632011-01-19 15:15:34 +00003091 if (logging != MagickFalse)
3092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3093 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00003094 found_transparent_pixel = MagickTrue;
3095 break;
3096 }
cristy16ea1392012-03-21 20:38:41 +00003097 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00003098 }
3099 }
3100
3101 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3102 {
3103 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3104 image->rows);
3105
3106 if (status == MagickFalse)
3107 break;
3108 }
3109 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3110 break;
3111 }
3112
3113 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3114 {
3115 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003116 if (status == MagickFalse)
3117 break;
3118 }
cristy3ed852e2009-09-05 21:47:34 +00003119 }
cristy3ed852e2009-09-05 21:47:34 +00003120 }
glennrp0fe50b42010-11-16 03:52:51 +00003121
cristy3ed852e2009-09-05 21:47:34 +00003122 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003123
cristy3ed852e2009-09-05 21:47:34 +00003124 for (pass=0; pass < num_passes; pass++)
3125 {
3126 Quantum
3127 *quantum_scanline;
3128
3129 register Quantum
3130 *r;
3131
3132 /*
3133 Convert grayscale image to PseudoClass pixel packets.
3134 */
glennrpc17d96f2011-06-27 01:20:11 +00003135 if (logging != MagickFalse)
3136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3137 " Converting grayscale pixels to pixel packets");
cristy16ea1392012-03-21 20:38:41 +00003138
cristy8a46d822012-08-28 23:32:39 +00003139 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristyb0a657e2012-08-29 00:45:37 +00003140 BlendPixelTrait : UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00003141
cristy3ed852e2009-09-05 21:47:34 +00003142 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
cristyb0a657e2012-08-29 00:45:37 +00003143 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3144 sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003145
cristy3ed852e2009-09-05 21:47:34 +00003146 if (quantum_scanline == (Quantum *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003147 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003148
cristybb503372010-05-27 20:51:26 +00003149 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003150 {
3151 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003152 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003153
cristy3ed852e2009-09-05 21:47:34 +00003154 else
3155 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003156
glennrpcf002022011-01-30 02:38:15 +00003157 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc5c31be2012-11-07 18:42:18 +00003158
3159 if (pass < num_passes-1)
3160 continue;
3161
cristy3ed852e2009-09-05 21:47:34 +00003162 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003163
cristy16ea1392012-03-21 20:38:41 +00003164 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003165 break;
glennrp0fe50b42010-11-16 03:52:51 +00003166
glennrpcf002022011-01-30 02:38:15 +00003167 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003168 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003169
glennrpfaa852b2010-03-30 12:17:00 +00003170 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003171 {
cristy3ed852e2009-09-05 21:47:34 +00003172 case 8:
3173 {
glennrpfaa852b2010-03-30 12:17:00 +00003174 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003175 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003176 {
glennrpa18d5bc2011-04-23 14:51:34 +00003177 *r++=*p++;
cristy16ea1392012-03-21 20:38:41 +00003178 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3179 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003180 found_transparent_pixel = MagickTrue;
cristy16ea1392012-03-21 20:38:41 +00003181 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003182 }
glennrp0fe50b42010-11-16 03:52:51 +00003183
cristy3ed852e2009-09-05 21:47:34 +00003184 else
cristybb503372010-05-27 20:51:26 +00003185 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003186 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003187
cristy3ed852e2009-09-05 21:47:34 +00003188 break;
3189 }
glennrp47b9dd52010-11-24 18:12:06 +00003190
cristy3ed852e2009-09-05 21:47:34 +00003191 case 16:
3192 {
cristybb503372010-05-27 20:51:26 +00003193 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003194 {
glennrpc17d96f2011-06-27 01:20:11 +00003195#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003196 size_t
3197 quantum;
3198
3199 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003200 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003201
3202 else
glennrpc17d96f2011-06-27 01:20:11 +00003203 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003204
glennrp58f77c72011-04-23 14:09:09 +00003205 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003206 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003207 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003208
3209 if (ping_color_type == 4)
3210 {
glennrpc17d96f2011-06-27 01:20:11 +00003211 if (image->colors > 256)
3212 quantum=((*p++) << 8);
3213 else
3214 quantum=0;
3215
glennrp58f77c72011-04-23 14:09:09 +00003216 quantum|=(*p++);
cristy16ea1392012-03-21 20:38:41 +00003217 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3218 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003219 found_transparent_pixel = MagickTrue;
cristy16ea1392012-03-21 20:38:41 +00003220 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003221 }
glennrp58f77c72011-04-23 14:09:09 +00003222
3223#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3224 *r++=(*p++);
3225 p++; /* strip low byte */
3226
3227 if (ping_color_type == 4)
3228 {
cristy16ea1392012-03-21 20:38:41 +00003229 SetPixelAlpha(image,*p++,q);
3230 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003231 found_transparent_pixel = MagickTrue;
3232 p++;
cristy16ea1392012-03-21 20:38:41 +00003233 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003234 }
cristy3ed852e2009-09-05 21:47:34 +00003235#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003236 }
glennrp47b9dd52010-11-24 18:12:06 +00003237
cristy3ed852e2009-09-05 21:47:34 +00003238 break;
3239 }
glennrp47b9dd52010-11-24 18:12:06 +00003240
cristy3ed852e2009-09-05 21:47:34 +00003241 default:
3242 break;
3243 }
glennrp3faa9a32011-04-23 14:00:25 +00003244
cristy3ed852e2009-09-05 21:47:34 +00003245 /*
3246 Transfer image scanline.
3247 */
3248 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003249
cristy16ea1392012-03-21 20:38:41 +00003250 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3251
3252 if (q == (Quantum *) NULL)
3253 break;
cristybb503372010-05-27 20:51:26 +00003254 for (x=0; x < (ssize_t) image->columns; x++)
cristy16ea1392012-03-21 20:38:41 +00003255 {
3256 SetPixelIndex(image,*r++,q);
3257 q+=GetPixelChannels(image);
3258 }
glennrp0fe50b42010-11-16 03:52:51 +00003259
cristy3ed852e2009-09-05 21:47:34 +00003260 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3261 break;
glennrp0fe50b42010-11-16 03:52:51 +00003262
cristy7a287bf2010-02-14 02:18:09 +00003263 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3264 {
cristycee97112010-05-28 00:44:52 +00003265 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003266 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003267
cristy7a287bf2010-02-14 02:18:09 +00003268 if (status == MagickFalse)
3269 break;
3270 }
cristy3ed852e2009-09-05 21:47:34 +00003271 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003272
cristy7a287bf2010-02-14 02:18:09 +00003273 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003274 {
3275 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003276
cristy3ed852e2009-09-05 21:47:34 +00003277 if (status == MagickFalse)
3278 break;
3279 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003280
cristy3ed852e2009-09-05 21:47:34 +00003281 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3282 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003283
cristyb0a657e2012-08-29 00:45:37 +00003284 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3285 UndefinedPixelTrait;
glennrpc8cbc5d2011-01-01 00:12:34 +00003286
3287 if (logging != MagickFalse)
3288 {
3289 if (found_transparent_pixel != MagickFalse)
3290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3291 " Found transparent pixel");
3292 else
glennrp5aa37f62011-01-02 03:07:57 +00003293 {
3294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3295 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003296
glennrp5aa37f62011-01-02 03:07:57 +00003297 ping_color_type&=0x03;
3298 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003299 }
3300 }
3301
cristy16ea1392012-03-21 20:38:41 +00003302 if (quantum_info != (QuantumInfo *) NULL)
3303 quantum_info=DestroyQuantumInfo(quantum_info);
3304
cristy5c6f7892010-05-05 22:53:29 +00003305 if (image->storage_class == PseudoClass)
3306 {
cristyb0a657e2012-08-29 00:45:37 +00003307 PixelTrait
3308 alpha_trait;
cristy5c6f7892010-05-05 22:53:29 +00003309
cristyb0a657e2012-08-29 00:45:37 +00003310 alpha_trait=image->alpha_trait;
cristy8a46d822012-08-28 23:32:39 +00003311 image->alpha_trait=UndefinedPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003312 (void) SyncImage(image,exception);
cristyb0a657e2012-08-29 00:45:37 +00003313 image->alpha_trait=alpha_trait;
cristy5c6f7892010-05-05 22:53:29 +00003314 }
glennrp47b9dd52010-11-24 18:12:06 +00003315
glennrp4eb39312011-03-30 21:34:55 +00003316 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003317
3318 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003319 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003320 {
3321 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003322 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003323 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00003324 (void) SetImageBackgroundColor(image,exception);
glennrpedaa0382012-04-12 14:16:21 +00003325#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00003326 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003327#endif
3328 if (logging != MagickFalse)
3329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3330 " exit ReadOnePNGImage() early.");
3331 return(image);
3332 }
glennrp47b9dd52010-11-24 18:12:06 +00003333
glennrpfaa852b2010-03-30 12:17:00 +00003334 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003335 {
3336 ClassType
3337 storage_class;
3338
3339 /*
3340 Image has a transparent background.
3341 */
3342 storage_class=image->storage_class;
cristy8a46d822012-08-28 23:32:39 +00003343 image->alpha_trait=BlendPixelTrait;
glennrpc11cf6a2010-03-20 16:46:19 +00003344
glennrp3c218112010-11-27 15:31:26 +00003345/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003346
glennrp0fe50b42010-11-16 03:52:51 +00003347 if (storage_class == PseudoClass)
3348 {
3349 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003350 {
glennrp0fe50b42010-11-16 03:52:51 +00003351 for (x=0; x < ping_num_trans; x++)
3352 {
cristy8a46d822012-08-28 23:32:39 +00003353 image->colormap[x].alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003354 image->colormap[x].alpha =
3355 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003356 }
glennrpc11cf6a2010-03-20 16:46:19 +00003357 }
glennrp47b9dd52010-11-24 18:12:06 +00003358
glennrp0fe50b42010-11-16 03:52:51 +00003359 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3360 {
3361 for (x=0; x < (int) image->colors; x++)
3362 {
3363 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy16ea1392012-03-21 20:38:41 +00003364 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003365 {
cristy8a46d822012-08-28 23:32:39 +00003366 image->colormap[x].alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003367 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003368 }
3369 }
3370 }
cristy16ea1392012-03-21 20:38:41 +00003371 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003372 }
glennrp47b9dd52010-11-24 18:12:06 +00003373
glennrpa6a06632011-01-19 15:15:34 +00003374#if 1 /* Should have already been done above, but glennrp problem P10
3375 * needs this.
3376 */
glennrp0fe50b42010-11-16 03:52:51 +00003377 else
3378 {
3379 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003380 {
glennrp0fe50b42010-11-16 03:52:51 +00003381 image->storage_class=storage_class;
3382 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3383
cristy16ea1392012-03-21 20:38:41 +00003384 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003385 break;
3386
glennrp0fe50b42010-11-16 03:52:51 +00003387
glennrpa6a06632011-01-19 15:15:34 +00003388 /* Caution: on a Q8 build, this does not distinguish between
3389 * 16-bit colors that differ only in the low byte
3390 */
glennrp0fe50b42010-11-16 03:52:51 +00003391 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3392 {
cristy16ea1392012-03-21 20:38:41 +00003393 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3394 transparent_color.red &&
3395 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3396 transparent_color.green &&
3397 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3398 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003399 {
cristy16ea1392012-03-21 20:38:41 +00003400 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003401 }
glennrp0fe50b42010-11-16 03:52:51 +00003402
glennrp67b9c1a2011-04-22 18:47:36 +00003403#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003404 else
glennrp4f25bd02011-01-01 18:51:28 +00003405 {
cristy16ea1392012-03-21 20:38:41 +00003406 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003407 }
glennrpa6a06632011-01-19 15:15:34 +00003408#endif
glennrp0fe50b42010-11-16 03:52:51 +00003409
cristy16ea1392012-03-21 20:38:41 +00003410 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003411 }
3412
3413 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3414 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003415 }
glennrp0fe50b42010-11-16 03:52:51 +00003416 }
glennrpa6a06632011-01-19 15:15:34 +00003417#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003418
cristy3ed852e2009-09-05 21:47:34 +00003419 image->storage_class=DirectClass;
3420 }
glennrp3c218112010-11-27 15:31:26 +00003421
cristyeb3b22a2011-03-31 20:16:11 +00003422 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003423 {
3424 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003425 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3426 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003427 else
glennrpa0ed0092011-04-18 16:36:29 +00003428 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3429 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003430
glennrp4eb39312011-03-30 21:34:55 +00003431 if (status != MagickFalse)
3432 for (i=0; i < (ssize_t) num_text; i++)
3433 {
3434 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003435
glennrp4eb39312011-03-30 21:34:55 +00003436 if (logging != MagickFalse)
3437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3438 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003439
glennrp4eb39312011-03-30 21:34:55 +00003440 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003441 {
glennrpedaa0382012-04-12 14:16:21 +00003442 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3443 (int) i,exception);
glennrp4eb39312011-03-30 21:34:55 +00003444 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003445 }
glennrp0fe50b42010-11-16 03:52:51 +00003446
glennrp4eb39312011-03-30 21:34:55 +00003447 else
3448 {
3449 char
3450 *value;
3451
3452 length=text[i].text_length;
3453 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3454 sizeof(*value));
3455 if (value == (char *) NULL)
3456 {
glennrpedaa0382012-04-12 14:16:21 +00003457 png_error(ping,"Memory allocation failed");
glennrp4eb39312011-03-30 21:34:55 +00003458 break;
3459 }
3460 *value='\0';
3461 (void) ConcatenateMagickString(value,text[i].text,length+2);
3462
3463 /* Don't save "density" or "units" property if we have a pHYs
3464 * chunk
3465 */
3466 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3467 (LocaleCompare(text[i].key,"density") != 0 &&
3468 LocaleCompare(text[i].key,"units") != 0))
cristy16ea1392012-03-21 20:38:41 +00003469 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003470
3471 if (logging != MagickFalse)
3472 {
3473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3474 " length: %lu",(unsigned long) length);
3475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3476 " Keyword: %s",text[i].key);
3477 }
3478
3479 value=DestroyString(value);
3480 }
3481 }
3482 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003483 }
glennrp3c218112010-11-27 15:31:26 +00003484
cristy3ed852e2009-09-05 21:47:34 +00003485#ifdef MNG_OBJECT_BUFFERS
3486 /*
3487 Store the object if necessary.
3488 */
3489 if (object_id && !mng_info->frozen[object_id])
3490 {
3491 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3492 {
3493 /*
3494 create a new object buffer.
3495 */
3496 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003497 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003498
cristy3ed852e2009-09-05 21:47:34 +00003499 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3500 {
3501 mng_info->ob[object_id]->image=(Image *) NULL;
3502 mng_info->ob[object_id]->reference_count=1;
3503 }
3504 }
glennrp47b9dd52010-11-24 18:12:06 +00003505
cristy3ed852e2009-09-05 21:47:34 +00003506 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3507 mng_info->ob[object_id]->frozen)
3508 {
3509 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003510 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003511
cristy3ed852e2009-09-05 21:47:34 +00003512 if (mng_info->ob[object_id]->frozen)
glennrpedaa0382012-04-12 14:16:21 +00003513 png_error(ping,"Cannot overwrite frozen MNG object buffer");
cristy3ed852e2009-09-05 21:47:34 +00003514 }
glennrp0fe50b42010-11-16 03:52:51 +00003515
cristy3ed852e2009-09-05 21:47:34 +00003516 else
3517 {
cristy3ed852e2009-09-05 21:47:34 +00003518
3519 if (mng_info->ob[object_id]->image != (Image *) NULL)
3520 mng_info->ob[object_id]->image=DestroyImage
3521 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003522
cristy3ed852e2009-09-05 21:47:34 +00003523 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristy16ea1392012-03-21 20:38:41 +00003524 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003525
cristy3ed852e2009-09-05 21:47:34 +00003526 if (mng_info->ob[object_id]->image != (Image *) NULL)
3527 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003528
cristy3ed852e2009-09-05 21:47:34 +00003529 else
glennrpedaa0382012-04-12 14:16:21 +00003530 png_error(ping, "Cloning image for object buffer failed");
glennrp0fe50b42010-11-16 03:52:51 +00003531
glennrpfaa852b2010-03-30 12:17:00 +00003532 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003533 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003534
glennrpfaa852b2010-03-30 12:17:00 +00003535 mng_info->ob[object_id]->width=ping_width;
3536 mng_info->ob[object_id]->height=ping_height;
3537 mng_info->ob[object_id]->color_type=ping_color_type;
3538 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3539 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3540 mng_info->ob[object_id]->compression_method=
3541 ping_compression_method;
3542 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003543
glennrpfaa852b2010-03-30 12:17:00 +00003544 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003545 {
cristy3ed852e2009-09-05 21:47:34 +00003546 png_colorp
3547 plte;
3548
3549 /*
3550 Copy the PLTE to the object buffer.
3551 */
3552 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3553 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003554
cristy3ed852e2009-09-05 21:47:34 +00003555 for (i=0; i < number_colors; i++)
3556 {
3557 mng_info->ob[object_id]->plte[i]=plte[i];
3558 }
3559 }
glennrp47b9dd52010-11-24 18:12:06 +00003560
cristy3ed852e2009-09-05 21:47:34 +00003561 else
3562 mng_info->ob[object_id]->plte_length=0;
3563 }
3564 }
3565#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003566
cristy8a46d822012-08-28 23:32:39 +00003567 /* Set image->alpha_trait to MagickTrue if the input colortype supports
glennrp0a55b4c2011-03-23 12:38:38 +00003568 * alpha or if a valid tRNS chunk is present, no matter whether there
3569 * is actual transparency present.
3570 */
cristy8a46d822012-08-28 23:32:39 +00003571 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
glennrp0a55b4c2011-03-23 12:38:38 +00003572 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3573 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
cristyb0a657e2012-08-29 00:45:37 +00003574 BlendPixelTrait : UndefinedPixelTrait;
glennrp0a55b4c2011-03-23 12:38:38 +00003575
glennrpcb395ac2011-03-30 19:50:23 +00003576 /* Set more properties for identify to retrieve */
3577 {
3578 char
3579 msg[MaxTextExtent];
3580
glennrp4eb39312011-03-30 21:34:55 +00003581 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003582 {
3583 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003584 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003585 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy16ea1392012-03-21 20:38:41 +00003586 (void) SetImageProperty(image,"png:text ",msg,
3587 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003588 }
3589
3590 if (num_raw_profiles != 0)
3591 {
cristy3b6fd2e2011-05-20 12:53:50 +00003592 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003593 "%d were found", num_raw_profiles);
cristy16ea1392012-03-21 20:38:41 +00003594 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3595 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003596 }
3597
glennrp98b83d42012-07-23 02:50:31 +00003598 if (ping_found_cHRM != MagickFalse)
glennrp59612252011-03-30 21:45:21 +00003599 {
cristy3b6fd2e2011-05-20 12:53:50 +00003600 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003601 "chunk was found (see Chromaticity, above)");
cristy16ea1392012-03-21 20:38:41 +00003602 (void) SetImageProperty(image,"png:cHRM ",msg,
3603 exception);
glennrp59612252011-03-30 21:45:21 +00003604 }
glennrpcb395ac2011-03-30 19:50:23 +00003605
3606 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003607 {
cristy3b6fd2e2011-05-20 12:53:50 +00003608 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003609 "chunk was found (see Background color, above)");
cristy16ea1392012-03-21 20:38:41 +00003610 (void) SetImageProperty(image,"png:bKGD ",msg,
3611 exception);
glennrp59612252011-03-30 21:45:21 +00003612 }
3613
cristy3b6fd2e2011-05-20 12:53:50 +00003614 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003615 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003616
glennrp98b83d42012-07-23 02:50:31 +00003617#if defined(PNG_iCCP_SUPPORTED)
3618 if (ping_found_iCCP != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00003619 (void) SetImageProperty(image,"png:iCCP ",msg,
3620 exception);
glennrp98b83d42012-07-23 02:50:31 +00003621#endif
glennrpcb395ac2011-03-30 19:50:23 +00003622
glennrpcb395ac2011-03-30 19:50:23 +00003623 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy16ea1392012-03-21 20:38:41 +00003624 (void) SetImageProperty(image,"png:tRNS ",msg,
3625 exception);
glennrp4eb39312011-03-30 21:34:55 +00003626
3627#if defined(PNG_sRGB_SUPPORTED)
glennrp98b83d42012-07-23 02:50:31 +00003628 if (ping_found_sRGB != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003629 {
cristy3b6fd2e2011-05-20 12:53:50 +00003630 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp98b83d42012-07-23 02:50:31 +00003631 "intent=%d (%s)",
3632 (int) intent,
3633 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
cristy16ea1392012-03-21 20:38:41 +00003634 (void) SetImageProperty(image,"png:sRGB ",msg,
glennrp98b83d42012-07-23 02:50:31 +00003635 exception);
glennrp4eb39312011-03-30 21:34:55 +00003636 }
3637#endif
3638
glennrp98b83d42012-07-23 02:50:31 +00003639 if (ping_found_gAMA != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003640 {
cristy3b6fd2e2011-05-20 12:53:50 +00003641 (void) FormatLocaleString(msg,MaxTextExtent,
cristy16ea1392012-03-21 20:38:41 +00003642 "gamma=%.8g (See Gamma, above)",
3643 file_gamma);
3644 (void) SetImageProperty(image,"png:gAMA ",msg,
3645 exception);
glennrp4eb39312011-03-30 21:34:55 +00003646 }
3647
3648#if defined(PNG_pHYs_SUPPORTED)
3649 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3650 {
cristy3b6fd2e2011-05-20 12:53:50 +00003651 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003652 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003653 (double) x_resolution,(double) y_resolution, unit_type);
cristy16ea1392012-03-21 20:38:41 +00003654 (void) SetImageProperty(image,"png:pHYs ",msg,
3655 exception);
glennrp4eb39312011-03-30 21:34:55 +00003656 }
3657#endif
3658
3659#if defined(PNG_oFFs_SUPPORTED)
3660 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3661 {
cristy3b6fd2e2011-05-20 12:53:50 +00003662 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003663 (double) image->page.x,(double) image->page.y);
cristy16ea1392012-03-21 20:38:41 +00003664 (void) SetImageProperty(image,"png:oFFs ",msg,
3665 exception);
glennrp4eb39312011-03-30 21:34:55 +00003666 }
3667#endif
3668
glennrp07523c72011-03-31 18:12:10 +00003669 if ((image->page.width != 0 && image->page.width != image->columns) ||
3670 (image->page.height != 0 && image->page.height != image->rows))
3671 {
cristy3b6fd2e2011-05-20 12:53:50 +00003672 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003673 "width=%.20g, height=%.20g",
3674 (double) image->page.width,(double) image->page.height);
cristy16ea1392012-03-21 20:38:41 +00003675 (void) SetImageProperty(image,"png:vpAg ",msg,
3676 exception);
glennrp07523c72011-03-31 18:12:10 +00003677 }
glennrpcb395ac2011-03-30 19:50:23 +00003678 }
3679
cristy3ed852e2009-09-05 21:47:34 +00003680 /*
3681 Relinquish resources.
3682 */
3683 png_destroy_read_struct(&ping,&ping_info,&end_info);
3684
glennrpcf002022011-01-30 02:38:15 +00003685 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003686
3687 if (logging != MagickFalse)
3688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3689 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003690
glennrpedaa0382012-04-12 14:16:21 +00003691#ifdef PNG_SETJMP_NOT_THREAD_SAFE
3692 UnlockSemaphoreInfo(ping_semaphore);
3693#endif
3694
3695 /* } for navigation to beginning of SETJMP-protected block, revert to
3696 * Throwing an Exception when an error occurs.
3697 */
3698
cristy3ed852e2009-09-05 21:47:34 +00003699 return(image);
3700
3701/* end of reading one PNG image */
3702}
3703
3704static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3705{
3706 Image
3707 *image,
3708 *previous;
3709
3710 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003711 have_mng_structure,
3712 logging,
cristy3ed852e2009-09-05 21:47:34 +00003713 status;
3714
3715 MngInfo
3716 *mng_info;
3717
3718 char
3719 magic_number[MaxTextExtent];
3720
cristy3ed852e2009-09-05 21:47:34 +00003721 ssize_t
3722 count;
3723
3724 /*
3725 Open image file.
3726 */
3727 assert(image_info != (const ImageInfo *) NULL);
3728 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003729
cristy3ed852e2009-09-05 21:47:34 +00003730 if (image_info->debug != MagickFalse)
3731 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3732 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003733
cristy3ed852e2009-09-05 21:47:34 +00003734 assert(exception != (ExceptionInfo *) NULL);
3735 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003736 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy16ea1392012-03-21 20:38:41 +00003737 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003738 mng_info=(MngInfo *) NULL;
3739 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003740
cristy3ed852e2009-09-05 21:47:34 +00003741 if (status == MagickFalse)
3742 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003743
cristy3ed852e2009-09-05 21:47:34 +00003744 /*
3745 Verify PNG signature.
3746 */
3747 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003748
glennrpdde35db2011-02-21 12:06:32 +00003749 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003750 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003751
cristy3ed852e2009-09-05 21:47:34 +00003752 /*
3753 Allocate a MngInfo structure.
3754 */
3755 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003756 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003757
cristy3ed852e2009-09-05 21:47:34 +00003758 if (mng_info == (MngInfo *) NULL)
3759 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003760
cristy3ed852e2009-09-05 21:47:34 +00003761 /*
3762 Initialize members of the MngInfo structure.
3763 */
3764 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3765 mng_info->image=image;
3766 have_mng_structure=MagickTrue;
3767
3768 previous=image;
3769 image=ReadOnePNGImage(mng_info,image_info,exception);
3770 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003771
cristy3ed852e2009-09-05 21:47:34 +00003772 if (image == (Image *) NULL)
3773 {
3774 if (previous != (Image *) NULL)
3775 {
3776 if (previous->signature != MagickSignature)
3777 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003778
cristy3ed852e2009-09-05 21:47:34 +00003779 (void) CloseBlob(previous);
3780 (void) DestroyImageList(previous);
3781 }
glennrp0fe50b42010-11-16 03:52:51 +00003782
cristy3ed852e2009-09-05 21:47:34 +00003783 if (logging != MagickFalse)
3784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3785 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003786
cristy3ed852e2009-09-05 21:47:34 +00003787 return((Image *) NULL);
3788 }
glennrp47b9dd52010-11-24 18:12:06 +00003789
cristy3ed852e2009-09-05 21:47:34 +00003790 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003791
cristy3ed852e2009-09-05 21:47:34 +00003792 if ((image->columns == 0) || (image->rows == 0))
3793 {
3794 if (logging != MagickFalse)
3795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3796 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003797
cristy3ed852e2009-09-05 21:47:34 +00003798 ThrowReaderException(CorruptImageError,"CorruptImage");
3799 }
glennrp47b9dd52010-11-24 18:12:06 +00003800
cristy72715f52012-06-26 17:55:16 +00003801 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
glennrpa8036d62012-11-04 01:46:06 +00003802 ((image->gamma < .45) || (image->gamma > .46)))
cristy72715f52012-06-26 17:55:16 +00003803 SetImageColorspace(image,RGBColorspace,exception);
3804
cristy3ed852e2009-09-05 21:47:34 +00003805 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3806 {
cristy16ea1392012-03-21 20:38:41 +00003807 (void) SetImageType(image,TrueColorType,exception);
cristy8a46d822012-08-28 23:32:39 +00003808 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00003809 }
glennrp0fe50b42010-11-16 03:52:51 +00003810
cristy3ed852e2009-09-05 21:47:34 +00003811 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy16ea1392012-03-21 20:38:41 +00003812 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003813
cristy3ed852e2009-09-05 21:47:34 +00003814 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3816 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3817 (double) image->page.width,(double) image->page.height,
3818 (double) image->page.x,(double) image->page.y);
3819
3820 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003822
cristy3ed852e2009-09-05 21:47:34 +00003823 return(image);
3824}
3825
3826
3827
3828#if defined(JNG_SUPPORTED)
3829/*
3830%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3831% %
3832% %
3833% %
3834% R e a d O n e J N G I m a g e %
3835% %
3836% %
3837% %
3838%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3839%
3840% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3841% (minus the 8-byte signature) and returns it. It allocates the memory
3842% necessary for the new Image structure and returns a pointer to the new
3843% image.
3844%
3845% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3846%
3847% The format of the ReadOneJNGImage method is:
3848%
3849% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3850% ExceptionInfo *exception)
3851%
3852% A description of each parameter follows:
3853%
3854% o mng_info: Specifies a pointer to a MngInfo structure.
3855%
3856% o image_info: the image info.
3857%
3858% o exception: return any errors or warnings in this structure.
3859%
3860*/
3861static Image *ReadOneJNGImage(MngInfo *mng_info,
3862 const ImageInfo *image_info, ExceptionInfo *exception)
3863{
3864 Image
3865 *alpha_image,
3866 *color_image,
3867 *image,
3868 *jng_image;
3869
3870 ImageInfo
3871 *alpha_image_info,
3872 *color_image_info;
3873
cristy4383ec82011-01-05 15:42:32 +00003874 MagickBooleanType
3875 logging;
3876
cristybb503372010-05-27 20:51:26 +00003877 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003878 y;
3879
3880 MagickBooleanType
3881 status;
3882
3883 png_uint_32
3884 jng_height,
3885 jng_width;
3886
3887 png_byte
3888 jng_color_type,
3889 jng_image_sample_depth,
3890 jng_image_compression_method,
3891 jng_image_interlace_method,
3892 jng_alpha_sample_depth,
3893 jng_alpha_compression_method,
3894 jng_alpha_filter_method,
3895 jng_alpha_interlace_method;
3896
cristy16ea1392012-03-21 20:38:41 +00003897 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003898 *s;
3899
cristybb503372010-05-27 20:51:26 +00003900 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003901 i,
3902 x;
3903
cristy16ea1392012-03-21 20:38:41 +00003904 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003905 *q;
3906
3907 register unsigned char
3908 *p;
3909
3910 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003911 read_JSEP,
3912 reading_idat,
3913 skip_to_iend;
3914
cristybb503372010-05-27 20:51:26 +00003915 size_t
cristy3ed852e2009-09-05 21:47:34 +00003916 length;
3917
3918 jng_alpha_compression_method=0;
3919 jng_alpha_sample_depth=8;
3920 jng_color_type=0;
3921 jng_height=0;
3922 jng_width=0;
3923 alpha_image=(Image *) NULL;
3924 color_image=(Image *) NULL;
3925 alpha_image_info=(ImageInfo *) NULL;
3926 color_image_info=(ImageInfo *) NULL;
3927
3928 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003929 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003930
3931 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003932
cristy16ea1392012-03-21 20:38:41 +00003933 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003934 {
3935 /*
3936 Allocate next image structure.
3937 */
3938 if (logging != MagickFalse)
3939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3940 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003941
cristy16ea1392012-03-21 20:38:41 +00003942 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003943
cristy3ed852e2009-09-05 21:47:34 +00003944 if (GetNextImageInList(image) == (Image *) NULL)
3945 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003946
cristy3ed852e2009-09-05 21:47:34 +00003947 image=SyncNextImageInList(image);
3948 }
3949 mng_info->image=image;
3950
3951 /*
3952 Signature bytes have already been read.
3953 */
3954
3955 read_JSEP=MagickFalse;
3956 reading_idat=MagickFalse;
3957 skip_to_iend=MagickFalse;
3958 for (;;)
3959 {
3960 char
3961 type[MaxTextExtent];
3962
3963 unsigned char
3964 *chunk;
3965
3966 unsigned int
3967 count;
3968
3969 /*
3970 Read a new JNG chunk.
3971 */
3972 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3973 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003974
cristy3ed852e2009-09-05 21:47:34 +00003975 if (status == MagickFalse)
3976 break;
glennrp0fe50b42010-11-16 03:52:51 +00003977
cristy3ed852e2009-09-05 21:47:34 +00003978 type[0]='\0';
3979 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3980 length=ReadBlobMSBLong(image);
3981 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3982
3983 if (logging != MagickFalse)
3984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003985 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3986 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003987
3988 if (length > PNG_UINT_31_MAX || count == 0)
3989 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003990
cristy3ed852e2009-09-05 21:47:34 +00003991 p=NULL;
3992 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003993
cristy3ed852e2009-09-05 21:47:34 +00003994 if (length)
3995 {
3996 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003997
cristy3ed852e2009-09-05 21:47:34 +00003998 if (chunk == (unsigned char *) NULL)
3999 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004000
cristybb503372010-05-27 20:51:26 +00004001 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004002 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00004003
cristy3ed852e2009-09-05 21:47:34 +00004004 p=chunk;
4005 }
glennrp47b9dd52010-11-24 18:12:06 +00004006
cristy3ed852e2009-09-05 21:47:34 +00004007 (void) ReadBlobMSBLong(image); /* read crc word */
4008
4009 if (skip_to_iend)
4010 {
4011 if (length)
4012 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004013
cristy3ed852e2009-09-05 21:47:34 +00004014 continue;
4015 }
4016
4017 if (memcmp(type,mng_JHDR,4) == 0)
4018 {
4019 if (length == 16)
4020 {
cristybb503372010-05-27 20:51:26 +00004021 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004022 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00004023 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004024 (p[6] << 8) | p[7]);
4025 jng_color_type=p[8];
4026 jng_image_sample_depth=p[9];
4027 jng_image_compression_method=p[10];
4028 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00004029
cristy3ed852e2009-09-05 21:47:34 +00004030 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4031 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00004032
cristy3ed852e2009-09-05 21:47:34 +00004033 jng_alpha_sample_depth=p[12];
4034 jng_alpha_compression_method=p[13];
4035 jng_alpha_filter_method=p[14];
4036 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00004037
cristy3ed852e2009-09-05 21:47:34 +00004038 if (logging != MagickFalse)
4039 {
4040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004041 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00004042
cristy3ed852e2009-09-05 21:47:34 +00004043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004044 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00004045
cristy3ed852e2009-09-05 21:47:34 +00004046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4047 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00004048
cristy3ed852e2009-09-05 21:47:34 +00004049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4050 " jng_image_sample_depth: %3d",
4051 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004052
cristy3ed852e2009-09-05 21:47:34 +00004053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4054 " jng_image_compression_method:%3d",
4055 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004056
cristy3ed852e2009-09-05 21:47:34 +00004057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4058 " jng_image_interlace_method: %3d",
4059 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00004060
cristy3ed852e2009-09-05 21:47:34 +00004061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4062 " jng_alpha_sample_depth: %3d",
4063 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004064
cristy3ed852e2009-09-05 21:47:34 +00004065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4066 " jng_alpha_compression_method:%3d",
4067 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004068
cristy3ed852e2009-09-05 21:47:34 +00004069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4070 " jng_alpha_filter_method: %3d",
4071 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004072
cristy3ed852e2009-09-05 21:47:34 +00004073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4074 " jng_alpha_interlace_method: %3d",
4075 jng_alpha_interlace_method);
4076 }
4077 }
glennrp47b9dd52010-11-24 18:12:06 +00004078
cristy3ed852e2009-09-05 21:47:34 +00004079 if (length)
4080 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004081
cristy3ed852e2009-09-05 21:47:34 +00004082 continue;
4083 }
4084
4085
4086 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4087 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4088 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4089 {
4090 /*
4091 o create color_image
4092 o open color_blob, attached to color_image
4093 o if (color type has alpha)
4094 open alpha_blob, attached to alpha_image
4095 */
4096
cristy73bd4a52010-10-05 11:24:23 +00004097 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004098
cristy3ed852e2009-09-05 21:47:34 +00004099 if (color_image_info == (ImageInfo *) NULL)
4100 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004101
cristy3ed852e2009-09-05 21:47:34 +00004102 GetImageInfo(color_image_info);
cristy16ea1392012-03-21 20:38:41 +00004103 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004104
cristy3ed852e2009-09-05 21:47:34 +00004105 if (color_image == (Image *) NULL)
4106 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4107
4108 if (logging != MagickFalse)
4109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4110 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004111
cristy3ed852e2009-09-05 21:47:34 +00004112 (void) AcquireUniqueFilename(color_image->filename);
4113 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4114 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004115
cristy3ed852e2009-09-05 21:47:34 +00004116 if (status == MagickFalse)
4117 return((Image *) NULL);
4118
4119 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4120 {
4121 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004122 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004123
cristy3ed852e2009-09-05 21:47:34 +00004124 if (alpha_image_info == (ImageInfo *) NULL)
4125 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004126
cristy3ed852e2009-09-05 21:47:34 +00004127 GetImageInfo(alpha_image_info);
cristy16ea1392012-03-21 20:38:41 +00004128 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004129
cristy3ed852e2009-09-05 21:47:34 +00004130 if (alpha_image == (Image *) NULL)
4131 {
4132 alpha_image=DestroyImage(alpha_image);
4133 ThrowReaderException(ResourceLimitError,
4134 "MemoryAllocationFailed");
4135 }
glennrp0fe50b42010-11-16 03:52:51 +00004136
cristy3ed852e2009-09-05 21:47:34 +00004137 if (logging != MagickFalse)
4138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4139 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004140
cristy3ed852e2009-09-05 21:47:34 +00004141 (void) AcquireUniqueFilename(alpha_image->filename);
4142 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4143 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004144
cristy3ed852e2009-09-05 21:47:34 +00004145 if (status == MagickFalse)
4146 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004147
cristy3ed852e2009-09-05 21:47:34 +00004148 if (jng_alpha_compression_method == 0)
4149 {
4150 unsigned char
4151 data[18];
4152
4153 if (logging != MagickFalse)
4154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4155 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004156
cristy3ed852e2009-09-05 21:47:34 +00004157 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4158 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004159
cristy3ed852e2009-09-05 21:47:34 +00004160 (void) WriteBlobMSBULong(alpha_image,13L);
4161 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004162 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004163 PNGLong(data+4,jng_width);
4164 PNGLong(data+8,jng_height);
4165 data[12]=jng_alpha_sample_depth;
4166 data[13]=0; /* color_type gray */
4167 data[14]=0; /* compression method 0 */
4168 data[15]=0; /* filter_method 0 */
4169 data[16]=0; /* interlace_method 0 */
4170 (void) WriteBlob(alpha_image,17,data);
4171 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4172 }
4173 }
4174 reading_idat=MagickTrue;
4175 }
4176
4177 if (memcmp(type,mng_JDAT,4) == 0)
4178 {
glennrp47b9dd52010-11-24 18:12:06 +00004179 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004180
4181 if (logging != MagickFalse)
4182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4183 " Copying JDAT chunk data to color_blob.");
4184
4185 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004186
cristy3ed852e2009-09-05 21:47:34 +00004187 if (length)
4188 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004189
cristy3ed852e2009-09-05 21:47:34 +00004190 continue;
4191 }
4192
4193 if (memcmp(type,mng_IDAT,4) == 0)
4194 {
4195 png_byte
4196 data[5];
4197
glennrp47b9dd52010-11-24 18:12:06 +00004198 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004199
4200 if (image_info->ping == MagickFalse)
4201 {
4202 if (logging != MagickFalse)
4203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4204 " Copying IDAT chunk data to alpha_blob.");
4205
cristybb503372010-05-27 20:51:26 +00004206 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004207 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004208 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004209 (void) WriteBlob(alpha_image,4,data);
4210 (void) WriteBlob(alpha_image,length,chunk);
4211 (void) WriteBlobMSBULong(alpha_image,
4212 crc32(crc32(0,data,4),chunk,(uInt) length));
4213 }
glennrp0fe50b42010-11-16 03:52:51 +00004214
cristy3ed852e2009-09-05 21:47:34 +00004215 if (length)
4216 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004217
cristy3ed852e2009-09-05 21:47:34 +00004218 continue;
4219 }
4220
4221 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4222 {
glennrp47b9dd52010-11-24 18:12:06 +00004223 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004224
4225 if (image_info->ping == MagickFalse)
4226 {
4227 if (logging != MagickFalse)
4228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4229 " Copying JDAA chunk data to alpha_blob.");
4230
4231 (void) WriteBlob(alpha_image,length,chunk);
4232 }
glennrp0fe50b42010-11-16 03:52:51 +00004233
cristy3ed852e2009-09-05 21:47:34 +00004234 if (length)
4235 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004236
cristy3ed852e2009-09-05 21:47:34 +00004237 continue;
4238 }
4239
4240 if (memcmp(type,mng_JSEP,4) == 0)
4241 {
4242 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004243
cristy3ed852e2009-09-05 21:47:34 +00004244 if (length)
4245 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004246
cristy3ed852e2009-09-05 21:47:34 +00004247 continue;
4248 }
4249
4250 if (memcmp(type,mng_bKGD,4) == 0)
4251 {
4252 if (length == 2)
4253 {
4254 image->background_color.red=ScaleCharToQuantum(p[1]);
4255 image->background_color.green=image->background_color.red;
4256 image->background_color.blue=image->background_color.red;
4257 }
glennrp0fe50b42010-11-16 03:52:51 +00004258
cristy3ed852e2009-09-05 21:47:34 +00004259 if (length == 6)
4260 {
4261 image->background_color.red=ScaleCharToQuantum(p[1]);
4262 image->background_color.green=ScaleCharToQuantum(p[3]);
4263 image->background_color.blue=ScaleCharToQuantum(p[5]);
4264 }
glennrp0fe50b42010-11-16 03:52:51 +00004265
cristy3ed852e2009-09-05 21:47:34 +00004266 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4267 continue;
4268 }
4269
4270 if (memcmp(type,mng_gAMA,4) == 0)
4271 {
4272 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004273 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004274
cristy3ed852e2009-09-05 21:47:34 +00004275 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4276 continue;
4277 }
4278
4279 if (memcmp(type,mng_cHRM,4) == 0)
4280 {
4281 if (length == 32)
4282 {
cristy8182b072010-05-30 20:10:53 +00004283 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4284 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4285 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4286 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4287 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4288 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4289 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4290 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004291 }
glennrp47b9dd52010-11-24 18:12:06 +00004292
cristy3ed852e2009-09-05 21:47:34 +00004293 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4294 continue;
4295 }
4296
4297 if (memcmp(type,mng_sRGB,4) == 0)
4298 {
4299 if (length == 1)
4300 {
glennrpe610a072010-08-05 17:08:46 +00004301 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004302 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristyda7803d2012-05-03 01:22:45 +00004303 image->gamma=1.000f/2.200f;
cristy3ed852e2009-09-05 21:47:34 +00004304 image->chromaticity.red_primary.x=0.6400f;
4305 image->chromaticity.red_primary.y=0.3300f;
4306 image->chromaticity.green_primary.x=0.3000f;
4307 image->chromaticity.green_primary.y=0.6000f;
4308 image->chromaticity.blue_primary.x=0.1500f;
4309 image->chromaticity.blue_primary.y=0.0600f;
4310 image->chromaticity.white_point.x=0.3127f;
4311 image->chromaticity.white_point.y=0.3290f;
4312 }
glennrp47b9dd52010-11-24 18:12:06 +00004313
cristy3ed852e2009-09-05 21:47:34 +00004314 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4315 continue;
4316 }
4317
4318 if (memcmp(type,mng_oFFs,4) == 0)
4319 {
4320 if (length > 8)
4321 {
glennrp5eae7602011-02-22 15:21:32 +00004322 image->page.x=(ssize_t) mng_get_long(p);
4323 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004324
cristy3ed852e2009-09-05 21:47:34 +00004325 if ((int) p[8] != 0)
4326 {
4327 image->page.x/=10000;
4328 image->page.y/=10000;
4329 }
4330 }
glennrp47b9dd52010-11-24 18:12:06 +00004331
cristy3ed852e2009-09-05 21:47:34 +00004332 if (length)
4333 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004334
cristy3ed852e2009-09-05 21:47:34 +00004335 continue;
4336 }
4337
4338 if (memcmp(type,mng_pHYs,4) == 0)
4339 {
4340 if (length > 8)
4341 {
cristy16ea1392012-03-21 20:38:41 +00004342 image->resolution.x=(double) mng_get_long(p);
4343 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004344 if ((int) p[8] == PNG_RESOLUTION_METER)
4345 {
4346 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00004347 image->resolution.x=image->resolution.x/100.0f;
4348 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004349 }
4350 }
glennrp0fe50b42010-11-16 03:52:51 +00004351
cristy3ed852e2009-09-05 21:47:34 +00004352 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4353 continue;
4354 }
4355
4356#if 0
4357 if (memcmp(type,mng_iCCP,4) == 0)
4358 {
glennrpfd05d622011-02-25 04:10:33 +00004359 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004360 if (length)
4361 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004362
cristy3ed852e2009-09-05 21:47:34 +00004363 continue;
4364 }
4365#endif
4366
4367 if (length)
4368 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4369
4370 if (memcmp(type,mng_IEND,4))
4371 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004372
cristy3ed852e2009-09-05 21:47:34 +00004373 break;
4374 }
4375
4376
4377 /* IEND found */
4378
4379 /*
4380 Finish up reading image data:
4381
4382 o read main image from color_blob.
4383
4384 o close color_blob.
4385
4386 o if (color_type has alpha)
4387 if alpha_encoding is PNG
4388 read secondary image from alpha_blob via ReadPNG
4389 if alpha_encoding is JPEG
4390 read secondary image from alpha_blob via ReadJPEG
4391
4392 o close alpha_blob.
4393
4394 o copy intensity of secondary image into
cristy16ea1392012-03-21 20:38:41 +00004395 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004396
4397 o destroy the secondary image.
4398 */
4399
4400 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004401
cristy3ed852e2009-09-05 21:47:34 +00004402 if (logging != MagickFalse)
4403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4404 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004405
cristy3b6fd2e2011-05-20 12:53:50 +00004406 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004407 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004408
cristy3ed852e2009-09-05 21:47:34 +00004409 color_image_info->ping=MagickFalse; /* To do: avoid this */
4410 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004411
cristy3ed852e2009-09-05 21:47:34 +00004412 if (jng_image == (Image *) NULL)
4413 return((Image *) NULL);
4414
4415 (void) RelinquishUniqueFileResource(color_image->filename);
4416 color_image=DestroyImage(color_image);
4417 color_image_info=DestroyImageInfo(color_image_info);
4418
4419 if (jng_image == (Image *) NULL)
4420 return((Image *) NULL);
4421
4422 if (logging != MagickFalse)
4423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4424 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004425
cristy3ed852e2009-09-05 21:47:34 +00004426 image->rows=jng_height;
4427 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004428
cristybb503372010-05-27 20:51:26 +00004429 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004430 {
cristy16ea1392012-03-21 20:38:41 +00004431 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004432 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00004433 for (x=(ssize_t) image->columns; x != 0; x--)
4434 {
4435 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4436 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4437 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4438 q+=GetPixelChannels(image);
4439 s+=GetPixelChannels(jng_image);
4440 }
glennrp47b9dd52010-11-24 18:12:06 +00004441
cristy3ed852e2009-09-05 21:47:34 +00004442 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4443 break;
4444 }
glennrp0fe50b42010-11-16 03:52:51 +00004445
cristy3ed852e2009-09-05 21:47:34 +00004446 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004447
cristy3ed852e2009-09-05 21:47:34 +00004448 if (image_info->ping == MagickFalse)
4449 {
4450 if (jng_color_type >= 12)
4451 {
4452 if (jng_alpha_compression_method == 0)
4453 {
4454 png_byte
4455 data[5];
4456 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4457 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004458 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004459 (void) WriteBlob(alpha_image,4,data);
4460 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4461 }
glennrp0fe50b42010-11-16 03:52:51 +00004462
cristy3ed852e2009-09-05 21:47:34 +00004463 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004464
cristy3ed852e2009-09-05 21:47:34 +00004465 if (logging != MagickFalse)
4466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00004467 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004468
cristy3b6fd2e2011-05-20 12:53:50 +00004469 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004470 "%s",alpha_image->filename);
4471
4472 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004473
cristy3ed852e2009-09-05 21:47:34 +00004474 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004475 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004476 {
4477 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy16ea1392012-03-21 20:38:41 +00004478 exception);
cristy3ed852e2009-09-05 21:47:34 +00004479 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004480
cristy8a46d822012-08-28 23:32:39 +00004481 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00004482 for (x=(ssize_t) image->columns; x != 0; x--)
4483 {
4484 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4485 q+=GetPixelChannels(image);
4486 s+=GetPixelChannels(jng_image);
4487 }
glennrp0fe50b42010-11-16 03:52:51 +00004488
cristy3ed852e2009-09-05 21:47:34 +00004489 else
cristy16ea1392012-03-21 20:38:41 +00004490 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004491 {
cristy16ea1392012-03-21 20:38:41 +00004492 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4493 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy8a46d822012-08-28 23:32:39 +00004494 image->alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00004495 q+=GetPixelChannels(image);
4496 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004497 }
glennrp0fe50b42010-11-16 03:52:51 +00004498
cristy3ed852e2009-09-05 21:47:34 +00004499 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4500 break;
4501 }
4502 (void) RelinquishUniqueFileResource(alpha_image->filename);
4503 alpha_image=DestroyImage(alpha_image);
4504 alpha_image_info=DestroyImageInfo(alpha_image_info);
4505 if (jng_image != (Image *) NULL)
4506 jng_image=DestroyImage(jng_image);
4507 }
4508 }
4509
glennrp47b9dd52010-11-24 18:12:06 +00004510 /* Read the JNG image. */
4511
cristy3ed852e2009-09-05 21:47:34 +00004512 if (mng_info->mng_type == 0)
4513 {
4514 mng_info->mng_width=jng_width;
4515 mng_info->mng_height=jng_height;
4516 }
glennrp0fe50b42010-11-16 03:52:51 +00004517
cristy3ed852e2009-09-05 21:47:34 +00004518 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004519 {
4520 image->page.width=jng_width;
4521 image->page.height=jng_height;
4522 }
4523
cristy3ed852e2009-09-05 21:47:34 +00004524 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004525 {
4526 image->page.x=mng_info->x_off[mng_info->object_id];
4527 image->page.y=mng_info->y_off[mng_info->object_id];
4528 }
4529
cristy3ed852e2009-09-05 21:47:34 +00004530 else
glennrp0fe50b42010-11-16 03:52:51 +00004531 {
4532 image->page.y=mng_info->y_off[mng_info->object_id];
4533 }
4534
cristy3ed852e2009-09-05 21:47:34 +00004535 mng_info->image_found++;
4536 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4537 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004538
cristy3ed852e2009-09-05 21:47:34 +00004539 if (logging != MagickFalse)
4540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4541 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004542
cristy3ed852e2009-09-05 21:47:34 +00004543 return(image);
4544}
4545
4546/*
4547%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4548% %
4549% %
4550% %
4551% R e a d J N G I m a g e %
4552% %
4553% %
4554% %
4555%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4556%
4557% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4558% (including the 8-byte signature) and returns it. It allocates the memory
4559% necessary for the new Image structure and returns a pointer to the new
4560% image.
4561%
4562% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4563%
4564% The format of the ReadJNGImage method is:
4565%
4566% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4567% *exception)
4568%
4569% A description of each parameter follows:
4570%
4571% o image_info: the image info.
4572%
4573% o exception: return any errors or warnings in this structure.
4574%
4575*/
4576
4577static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4578{
4579 Image
4580 *image,
4581 *previous;
4582
4583 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004584 have_mng_structure,
4585 logging,
cristy3ed852e2009-09-05 21:47:34 +00004586 status;
4587
4588 MngInfo
4589 *mng_info;
4590
4591 char
4592 magic_number[MaxTextExtent];
4593
cristy3ed852e2009-09-05 21:47:34 +00004594 size_t
4595 count;
4596
4597 /*
4598 Open image file.
4599 */
4600 assert(image_info != (const ImageInfo *) NULL);
4601 assert(image_info->signature == MagickSignature);
4602 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4603 assert(exception != (ExceptionInfo *) NULL);
4604 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004605 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004606 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004607 mng_info=(MngInfo *) NULL;
4608 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004609
cristy3ed852e2009-09-05 21:47:34 +00004610 if (status == MagickFalse)
4611 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004612
cristy3ed852e2009-09-05 21:47:34 +00004613 if (LocaleCompare(image_info->magick,"JNG") != 0)
4614 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004615
glennrp47b9dd52010-11-24 18:12:06 +00004616 /* Verify JNG signature. */
4617
cristy3ed852e2009-09-05 21:47:34 +00004618 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004619
glennrp3b8763e2011-02-21 12:08:18 +00004620 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004621 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004622
glennrp47b9dd52010-11-24 18:12:06 +00004623 /* Allocate a MngInfo structure. */
4624
cristy3ed852e2009-09-05 21:47:34 +00004625 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004626 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004627
cristy3ed852e2009-09-05 21:47:34 +00004628 if (mng_info == (MngInfo *) NULL)
4629 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004630
glennrp47b9dd52010-11-24 18:12:06 +00004631 /* Initialize members of the MngInfo structure. */
4632
cristy3ed852e2009-09-05 21:47:34 +00004633 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4634 have_mng_structure=MagickTrue;
4635
4636 mng_info->image=image;
4637 previous=image;
4638 image=ReadOneJNGImage(mng_info,image_info,exception);
4639 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004640
cristy3ed852e2009-09-05 21:47:34 +00004641 if (image == (Image *) NULL)
4642 {
4643 if (IsImageObject(previous) != MagickFalse)
4644 {
4645 (void) CloseBlob(previous);
4646 (void) DestroyImageList(previous);
4647 }
glennrp0fe50b42010-11-16 03:52:51 +00004648
cristy3ed852e2009-09-05 21:47:34 +00004649 if (logging != MagickFalse)
4650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4651 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004652
cristy3ed852e2009-09-05 21:47:34 +00004653 return((Image *) NULL);
4654 }
4655 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004656
cristy3ed852e2009-09-05 21:47:34 +00004657 if (image->columns == 0 || image->rows == 0)
4658 {
4659 if (logging != MagickFalse)
4660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4661 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004662
cristy3ed852e2009-09-05 21:47:34 +00004663 ThrowReaderException(CorruptImageError,"CorruptImage");
4664 }
glennrp0fe50b42010-11-16 03:52:51 +00004665
cristy3ed852e2009-09-05 21:47:34 +00004666 if (logging != MagickFalse)
4667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004668
cristy3ed852e2009-09-05 21:47:34 +00004669 return(image);
4670}
4671#endif
4672
4673static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4674{
4675 char
4676 page_geometry[MaxTextExtent];
4677
4678 Image
4679 *image,
4680 *previous;
4681
cristy4383ec82011-01-05 15:42:32 +00004682 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004683 logging,
4684 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004685
cristy3ed852e2009-09-05 21:47:34 +00004686 volatile int
4687 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004688 object_id,
4689 term_chunk_found,
4690 skip_to_iend;
4691
cristybb503372010-05-27 20:51:26 +00004692 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004693 image_count=0;
4694
4695 MagickBooleanType
4696 status;
4697
4698 MagickOffsetType
4699 offset;
4700
4701 MngInfo
4702 *mng_info;
4703
4704 MngBox
4705 default_fb,
4706 fb,
4707 previous_fb;
4708
4709#if defined(MNG_INSERT_LAYERS)
cristy16ea1392012-03-21 20:38:41 +00004710 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004711 mng_background_color;
4712#endif
4713
4714 register unsigned char
4715 *p;
4716
cristybb503372010-05-27 20:51:26 +00004717 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004718 i;
4719
4720 size_t
4721 count;
4722
cristybb503372010-05-27 20:51:26 +00004723 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004724 loop_level;
4725
4726 volatile short
4727 skipping_loop;
4728
4729#if defined(MNG_INSERT_LAYERS)
4730 unsigned int
4731 mandatory_back=0;
4732#endif
4733
4734 volatile unsigned int
4735#ifdef MNG_OBJECT_BUFFERS
4736 mng_background_object=0,
4737#endif
4738 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4739
cristybb503372010-05-27 20:51:26 +00004740 size_t
cristy3ed852e2009-09-05 21:47:34 +00004741 default_frame_timeout,
4742 frame_timeout,
4743#if defined(MNG_INSERT_LAYERS)
4744 image_height,
4745 image_width,
4746#endif
4747 length;
4748
glennrp38ea0832010-06-02 18:50:28 +00004749 /* These delays are all measured in image ticks_per_second,
4750 * not in MNG ticks_per_second
4751 */
cristybb503372010-05-27 20:51:26 +00004752 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004753 default_frame_delay,
4754 final_delay,
4755 final_image_delay,
4756 frame_delay,
4757#if defined(MNG_INSERT_LAYERS)
4758 insert_layers,
4759#endif
4760 mng_iterations=1,
4761 simplicity=0,
4762 subframe_height=0,
4763 subframe_width=0;
4764
4765 previous_fb.top=0;
4766 previous_fb.bottom=0;
4767 previous_fb.left=0;
4768 previous_fb.right=0;
4769 default_fb.top=0;
4770 default_fb.bottom=0;
4771 default_fb.left=0;
4772 default_fb.right=0;
4773
glennrp47b9dd52010-11-24 18:12:06 +00004774 /* Open image file. */
4775
cristy3ed852e2009-09-05 21:47:34 +00004776 assert(image_info != (const ImageInfo *) NULL);
4777 assert(image_info->signature == MagickSignature);
4778 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4779 assert(exception != (ExceptionInfo *) NULL);
4780 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004781 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004782 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004783 mng_info=(MngInfo *) NULL;
4784 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004785
cristy3ed852e2009-09-05 21:47:34 +00004786 if (status == MagickFalse)
4787 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004788
cristy3ed852e2009-09-05 21:47:34 +00004789 first_mng_object=MagickFalse;
4790 skipping_loop=(-1);
4791 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004792
4793 /* Allocate a MngInfo structure. */
4794
cristy73bd4a52010-10-05 11:24:23 +00004795 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004796
cristy3ed852e2009-09-05 21:47:34 +00004797 if (mng_info == (MngInfo *) NULL)
4798 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004799
glennrp47b9dd52010-11-24 18:12:06 +00004800 /* Initialize members of the MngInfo structure. */
4801
cristy3ed852e2009-09-05 21:47:34 +00004802 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4803 mng_info->image=image;
4804 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004805
4806 if (LocaleCompare(image_info->magick,"MNG") == 0)
4807 {
4808 char
4809 magic_number[MaxTextExtent];
4810
glennrp47b9dd52010-11-24 18:12:06 +00004811 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004812 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4813 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4814 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004815
4816 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004817 for (i=0; i < MNG_MAX_OBJECTS; i++)
4818 {
cristybb503372010-05-27 20:51:26 +00004819 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4820 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004821 }
4822 mng_info->exists[0]=MagickTrue;
4823 }
glennrp47b9dd52010-11-24 18:12:06 +00004824
cristy3ed852e2009-09-05 21:47:34 +00004825 first_mng_object=MagickTrue;
4826 mng_type=0;
4827#if defined(MNG_INSERT_LAYERS)
4828 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4829#endif
4830 default_frame_delay=0;
4831 default_frame_timeout=0;
4832 frame_delay=0;
4833 final_delay=1;
4834 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4835 object_id=0;
4836 skip_to_iend=MagickFalse;
4837 term_chunk_found=MagickFalse;
4838 mng_info->framing_mode=1;
4839#if defined(MNG_INSERT_LAYERS)
4840 mandatory_back=MagickFalse;
4841#endif
4842#if defined(MNG_INSERT_LAYERS)
4843 mng_background_color=image->background_color;
4844#endif
4845 default_fb=mng_info->frame;
4846 previous_fb=mng_info->frame;
4847 do
4848 {
4849 char
4850 type[MaxTextExtent];
4851
4852 if (LocaleCompare(image_info->magick,"MNG") == 0)
4853 {
4854 unsigned char
4855 *chunk;
4856
4857 /*
4858 Read a new chunk.
4859 */
4860 type[0]='\0';
4861 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4862 length=ReadBlobMSBLong(image);
4863 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4864
4865 if (logging != MagickFalse)
4866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004867 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4868 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004869
4870 if (length > PNG_UINT_31_MAX)
4871 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004872
cristy3ed852e2009-09-05 21:47:34 +00004873 if (count == 0)
4874 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004875
cristy3ed852e2009-09-05 21:47:34 +00004876 p=NULL;
4877 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004878
cristy3ed852e2009-09-05 21:47:34 +00004879 if (length)
4880 {
4881 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004882
cristy3ed852e2009-09-05 21:47:34 +00004883 if (chunk == (unsigned char *) NULL)
4884 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004885
cristybb503372010-05-27 20:51:26 +00004886 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004887 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004888
cristy3ed852e2009-09-05 21:47:34 +00004889 p=chunk;
4890 }
glennrp0fe50b42010-11-16 03:52:51 +00004891
cristy3ed852e2009-09-05 21:47:34 +00004892 (void) ReadBlobMSBLong(image); /* read crc word */
4893
4894#if !defined(JNG_SUPPORTED)
4895 if (memcmp(type,mng_JHDR,4) == 0)
4896 {
4897 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004898
cristy3ed852e2009-09-05 21:47:34 +00004899 if (mng_info->jhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00004900 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004901 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004902
cristy3ed852e2009-09-05 21:47:34 +00004903 mng_info->jhdr_warning++;
4904 }
4905#endif
4906 if (memcmp(type,mng_DHDR,4) == 0)
4907 {
4908 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004909
cristy3ed852e2009-09-05 21:47:34 +00004910 if (mng_info->dhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00004911 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004912 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004913
cristy3ed852e2009-09-05 21:47:34 +00004914 mng_info->dhdr_warning++;
4915 }
4916 if (memcmp(type,mng_MEND,4) == 0)
4917 break;
glennrp47b9dd52010-11-24 18:12:06 +00004918
cristy3ed852e2009-09-05 21:47:34 +00004919 if (skip_to_iend)
4920 {
4921 if (memcmp(type,mng_IEND,4) == 0)
4922 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004923
cristy3ed852e2009-09-05 21:47:34 +00004924 if (length)
4925 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004926
cristy3ed852e2009-09-05 21:47:34 +00004927 if (logging != MagickFalse)
4928 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4929 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004930
cristy3ed852e2009-09-05 21:47:34 +00004931 continue;
4932 }
glennrp0fe50b42010-11-16 03:52:51 +00004933
cristy3ed852e2009-09-05 21:47:34 +00004934 if (memcmp(type,mng_MHDR,4) == 0)
4935 {
cristybb503372010-05-27 20:51:26 +00004936 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004937 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004938
cristybb503372010-05-27 20:51:26 +00004939 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004940 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004941
cristy3ed852e2009-09-05 21:47:34 +00004942 if (logging != MagickFalse)
4943 {
4944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004945 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004947 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004948 }
glennrp0fe50b42010-11-16 03:52:51 +00004949
cristy3ed852e2009-09-05 21:47:34 +00004950 p+=8;
cristy8182b072010-05-30 20:10:53 +00004951 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004952
cristy3ed852e2009-09-05 21:47:34 +00004953 if (mng_info->ticks_per_second == 0)
4954 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004955
cristy3ed852e2009-09-05 21:47:34 +00004956 else
4957 default_frame_delay=1UL*image->ticks_per_second/
4958 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004959
cristy3ed852e2009-09-05 21:47:34 +00004960 frame_delay=default_frame_delay;
4961 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004962
cristy3ed852e2009-09-05 21:47:34 +00004963 if (length > 16)
4964 {
4965 p+=16;
cristy8182b072010-05-30 20:10:53 +00004966 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004967 }
glennrp0fe50b42010-11-16 03:52:51 +00004968
cristy3ed852e2009-09-05 21:47:34 +00004969 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004970
cristy3ed852e2009-09-05 21:47:34 +00004971 if ((simplicity != 0) && ((simplicity | 11) == 11))
4972 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004973
cristy3ed852e2009-09-05 21:47:34 +00004974 if ((simplicity != 0) && ((simplicity | 9) == 9))
4975 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004976
cristy3ed852e2009-09-05 21:47:34 +00004977#if defined(MNG_INSERT_LAYERS)
4978 if (mng_type != 3)
4979 insert_layers=MagickTrue;
4980#endif
cristy16ea1392012-03-21 20:38:41 +00004981 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004982 {
glennrp47b9dd52010-11-24 18:12:06 +00004983 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00004984 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004985
cristy3ed852e2009-09-05 21:47:34 +00004986 if (GetNextImageInList(image) == (Image *) NULL)
4987 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004988
cristy3ed852e2009-09-05 21:47:34 +00004989 image=SyncNextImageInList(image);
4990 mng_info->image=image;
4991 }
4992
4993 if ((mng_info->mng_width > 65535L) ||
4994 (mng_info->mng_height > 65535L))
4995 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004996
cristy3b6fd2e2011-05-20 12:53:50 +00004997 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004998 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004999 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00005000
cristy3ed852e2009-09-05 21:47:34 +00005001 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00005002 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00005003 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00005004 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00005005 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005006
cristy3ed852e2009-09-05 21:47:34 +00005007 for (i=0; i < MNG_MAX_OBJECTS; i++)
5008 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005009
cristy3ed852e2009-09-05 21:47:34 +00005010 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5011 continue;
5012 }
5013
5014 if (memcmp(type,mng_TERM,4) == 0)
5015 {
5016 int
5017 repeat=0;
5018
5019
5020 if (length)
5021 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005022
cristy3ed852e2009-09-05 21:47:34 +00005023 if (repeat == 3)
5024 {
cristy8182b072010-05-30 20:10:53 +00005025 final_delay=(png_uint_32) mng_get_long(&p[2]);
5026 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00005027
cristy3ed852e2009-09-05 21:47:34 +00005028 if (mng_iterations == PNG_UINT_31_MAX)
5029 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00005030
cristy3ed852e2009-09-05 21:47:34 +00005031 image->iterations=mng_iterations;
5032 term_chunk_found=MagickTrue;
5033 }
glennrp0fe50b42010-11-16 03:52:51 +00005034
cristy3ed852e2009-09-05 21:47:34 +00005035 if (logging != MagickFalse)
5036 {
5037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5038 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00005039
cristy3ed852e2009-09-05 21:47:34 +00005040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005041 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00005042
cristy3ed852e2009-09-05 21:47:34 +00005043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005044 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00005045 }
glennrp0fe50b42010-11-16 03:52:51 +00005046
cristy3ed852e2009-09-05 21:47:34 +00005047 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5048 continue;
5049 }
5050 if (memcmp(type,mng_DEFI,4) == 0)
5051 {
5052 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005053 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005054 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5055 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005056
cristy3ed852e2009-09-05 21:47:34 +00005057 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005058
cristy3ed852e2009-09-05 21:47:34 +00005059 if (mng_type == 2 && object_id != 0)
cristy16ea1392012-03-21 20:38:41 +00005060 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005061 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5062 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005063
cristy3ed852e2009-09-05 21:47:34 +00005064 if (object_id > MNG_MAX_OBJECTS)
5065 {
5066 /*
glennrpedaa0382012-04-12 14:16:21 +00005067 Instead of using a warning we should allocate a larger
cristy3ed852e2009-09-05 21:47:34 +00005068 MngInfo structure and continue.
5069 */
cristy16ea1392012-03-21 20:38:41 +00005070 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005071 CoderError,"object id too large","`%s'",image->filename);
5072 object_id=MNG_MAX_OBJECTS;
5073 }
glennrp0fe50b42010-11-16 03:52:51 +00005074
cristy3ed852e2009-09-05 21:47:34 +00005075 if (mng_info->exists[object_id])
5076 if (mng_info->frozen[object_id])
5077 {
5078 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristy16ea1392012-03-21 20:38:41 +00005079 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005080 GetMagickModule(),CoderError,
5081 "DEFI cannot redefine a frozen MNG object","`%s'",
5082 image->filename);
5083 continue;
5084 }
glennrp0fe50b42010-11-16 03:52:51 +00005085
cristy3ed852e2009-09-05 21:47:34 +00005086 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005087
cristy3ed852e2009-09-05 21:47:34 +00005088 if (length > 2)
5089 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005090
cristy3ed852e2009-09-05 21:47:34 +00005091 /*
5092 Extract object offset info.
5093 */
5094 if (length > 11)
5095 {
glennrp0fe50b42010-11-16 03:52:51 +00005096 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5097 (p[5] << 16) | (p[6] << 8) | p[7]);
5098
5099 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5100 (p[9] << 16) | (p[10] << 8) | p[11]);
5101
cristy3ed852e2009-09-05 21:47:34 +00005102 if (logging != MagickFalse)
5103 {
5104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005105 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005106 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005107
cristy3ed852e2009-09-05 21:47:34 +00005108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005109 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005110 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005111 }
5112 }
glennrp0fe50b42010-11-16 03:52:51 +00005113
cristy3ed852e2009-09-05 21:47:34 +00005114 /*
5115 Extract object clipping info.
5116 */
5117 if (length > 27)
5118 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5119 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005120
cristy3ed852e2009-09-05 21:47:34 +00005121 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5122 continue;
5123 }
5124 if (memcmp(type,mng_bKGD,4) == 0)
5125 {
5126 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005127
cristy3ed852e2009-09-05 21:47:34 +00005128 if (length > 5)
5129 {
5130 mng_info->mng_global_bkgd.red=
5131 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005132
cristy3ed852e2009-09-05 21:47:34 +00005133 mng_info->mng_global_bkgd.green=
5134 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005135
cristy3ed852e2009-09-05 21:47:34 +00005136 mng_info->mng_global_bkgd.blue=
5137 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005138
cristy3ed852e2009-09-05 21:47:34 +00005139 mng_info->have_global_bkgd=MagickTrue;
5140 }
glennrp0fe50b42010-11-16 03:52:51 +00005141
cristy3ed852e2009-09-05 21:47:34 +00005142 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5143 continue;
5144 }
5145 if (memcmp(type,mng_BACK,4) == 0)
5146 {
5147#if defined(MNG_INSERT_LAYERS)
5148 if (length > 6)
5149 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005150
cristy3ed852e2009-09-05 21:47:34 +00005151 else
5152 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005153
cristy3ed852e2009-09-05 21:47:34 +00005154 if (mandatory_back && length > 5)
5155 {
5156 mng_background_color.red=
5157 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005158
cristy3ed852e2009-09-05 21:47:34 +00005159 mng_background_color.green=
5160 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005161
cristy3ed852e2009-09-05 21:47:34 +00005162 mng_background_color.blue=
5163 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005164
cristy16ea1392012-03-21 20:38:41 +00005165 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005166 }
glennrp0fe50b42010-11-16 03:52:51 +00005167
cristy3ed852e2009-09-05 21:47:34 +00005168#ifdef MNG_OBJECT_BUFFERS
5169 if (length > 8)
5170 mng_background_object=(p[7] << 8) | p[8];
5171#endif
5172#endif
5173 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5174 continue;
5175 }
glennrp47b9dd52010-11-24 18:12:06 +00005176
cristy3ed852e2009-09-05 21:47:34 +00005177 if (memcmp(type,mng_PLTE,4) == 0)
5178 {
glennrp47b9dd52010-11-24 18:12:06 +00005179 /* Read global PLTE. */
5180
cristy3ed852e2009-09-05 21:47:34 +00005181 if (length && (length < 769))
5182 {
5183 if (mng_info->global_plte == (png_colorp) NULL)
5184 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5185 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005186
cristybb503372010-05-27 20:51:26 +00005187 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005188 {
5189 mng_info->global_plte[i].red=p[3*i];
5190 mng_info->global_plte[i].green=p[3*i+1];
5191 mng_info->global_plte[i].blue=p[3*i+2];
5192 }
glennrp0fe50b42010-11-16 03:52:51 +00005193
cristy35ef8242010-06-03 16:24:13 +00005194 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005195 }
5196#ifdef MNG_LOOSE
5197 for ( ; i < 256; i++)
5198 {
5199 mng_info->global_plte[i].red=i;
5200 mng_info->global_plte[i].green=i;
5201 mng_info->global_plte[i].blue=i;
5202 }
glennrp0fe50b42010-11-16 03:52:51 +00005203
cristy3ed852e2009-09-05 21:47:34 +00005204 if (length)
5205 mng_info->global_plte_length=256;
5206#endif
5207 else
5208 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005209
cristy3ed852e2009-09-05 21:47:34 +00005210 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5211 continue;
5212 }
glennrp47b9dd52010-11-24 18:12:06 +00005213
cristy3ed852e2009-09-05 21:47:34 +00005214 if (memcmp(type,mng_tRNS,4) == 0)
5215 {
5216 /* read global tRNS */
5217
5218 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005219 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005220 mng_info->global_trns[i]=p[i];
5221
5222#ifdef MNG_LOOSE
5223 for ( ; i < 256; i++)
5224 mng_info->global_trns[i]=255;
5225#endif
cristy12560f32010-06-03 16:51:08 +00005226 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005227 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5228 continue;
5229 }
5230 if (memcmp(type,mng_gAMA,4) == 0)
5231 {
5232 if (length == 4)
5233 {
cristybb503372010-05-27 20:51:26 +00005234 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005235 igamma;
5236
cristy8182b072010-05-30 20:10:53 +00005237 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005238 mng_info->global_gamma=((float) igamma)*0.00001;
5239 mng_info->have_global_gama=MagickTrue;
5240 }
glennrp0fe50b42010-11-16 03:52:51 +00005241
cristy3ed852e2009-09-05 21:47:34 +00005242 else
5243 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005244
cristy3ed852e2009-09-05 21:47:34 +00005245 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5246 continue;
5247 }
5248
5249 if (memcmp(type,mng_cHRM,4) == 0)
5250 {
glennrp47b9dd52010-11-24 18:12:06 +00005251 /* Read global cHRM */
5252
cristy3ed852e2009-09-05 21:47:34 +00005253 if (length == 32)
5254 {
cristy8182b072010-05-30 20:10:53 +00005255 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5256 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5257 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005258 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005259 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005260 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005261 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005262 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005263 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005264 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005265 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005266 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005267 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005268 mng_info->have_global_chrm=MagickTrue;
5269 }
5270 else
5271 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005272
cristy3ed852e2009-09-05 21:47:34 +00005273 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5274 continue;
5275 }
glennrp47b9dd52010-11-24 18:12:06 +00005276
cristy3ed852e2009-09-05 21:47:34 +00005277 if (memcmp(type,mng_sRGB,4) == 0)
5278 {
5279 /*
5280 Read global sRGB.
5281 */
5282 if (length)
5283 {
glennrpe610a072010-08-05 17:08:46 +00005284 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005285 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005286 mng_info->have_global_srgb=MagickTrue;
5287 }
5288 else
5289 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005290
cristy3ed852e2009-09-05 21:47:34 +00005291 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5292 continue;
5293 }
glennrp47b9dd52010-11-24 18:12:06 +00005294
cristy3ed852e2009-09-05 21:47:34 +00005295 if (memcmp(type,mng_iCCP,4) == 0)
5296 {
glennrpfd05d622011-02-25 04:10:33 +00005297 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005298
5299 /*
5300 Read global iCCP.
5301 */
5302 if (length)
5303 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005304
cristy3ed852e2009-09-05 21:47:34 +00005305 continue;
5306 }
glennrp47b9dd52010-11-24 18:12:06 +00005307
cristy3ed852e2009-09-05 21:47:34 +00005308 if (memcmp(type,mng_FRAM,4) == 0)
5309 {
5310 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005311 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005312 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5313 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005314
cristy3ed852e2009-09-05 21:47:34 +00005315 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5316 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005317
cristy3ed852e2009-09-05 21:47:34 +00005318 frame_delay=default_frame_delay;
5319 frame_timeout=default_frame_timeout;
5320 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005321
cristy3ed852e2009-09-05 21:47:34 +00005322 if (length)
5323 if (p[0])
5324 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005325
cristy3ed852e2009-09-05 21:47:34 +00005326 if (logging != MagickFalse)
5327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5328 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005329
cristy3ed852e2009-09-05 21:47:34 +00005330 if (length > 6)
5331 {
glennrp47b9dd52010-11-24 18:12:06 +00005332 /* Note the delay and frame clipping boundaries. */
5333
cristy3ed852e2009-09-05 21:47:34 +00005334 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005335
cristybb503372010-05-27 20:51:26 +00005336 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005337 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005338
cristy3ed852e2009-09-05 21:47:34 +00005339 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005340
cristybb503372010-05-27 20:51:26 +00005341 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005342 {
5343 int
5344 change_delay,
5345 change_timeout,
5346 change_clipping;
5347
5348 change_delay=(*p++);
5349 change_timeout=(*p++);
5350 change_clipping=(*p++);
5351 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005352
cristy3ed852e2009-09-05 21:47:34 +00005353 if (change_delay)
5354 {
cristy8182b072010-05-30 20:10:53 +00005355 frame_delay=1UL*image->ticks_per_second*
5356 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005357
cristy8182b072010-05-30 20:10:53 +00005358 if (mng_info->ticks_per_second != 0)
5359 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005360
glennrpbb010dd2010-06-01 13:07:15 +00005361 else
5362 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005363
cristy3ed852e2009-09-05 21:47:34 +00005364 if (change_delay == 2)
5365 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005366
cristy3ed852e2009-09-05 21:47:34 +00005367 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005368
cristy3ed852e2009-09-05 21:47:34 +00005369 if (logging != MagickFalse)
5370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005371 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005372 }
glennrp47b9dd52010-11-24 18:12:06 +00005373
cristy3ed852e2009-09-05 21:47:34 +00005374 if (change_timeout)
5375 {
glennrpbb010dd2010-06-01 13:07:15 +00005376 frame_timeout=1UL*image->ticks_per_second*
5377 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005378
glennrpbb010dd2010-06-01 13:07:15 +00005379 if (mng_info->ticks_per_second != 0)
5380 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005381
glennrpbb010dd2010-06-01 13:07:15 +00005382 else
5383 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005384
cristy3ed852e2009-09-05 21:47:34 +00005385 if (change_delay == 2)
5386 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005387
cristy3ed852e2009-09-05 21:47:34 +00005388 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005389
cristy3ed852e2009-09-05 21:47:34 +00005390 if (logging != MagickFalse)
5391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005392 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005393 }
glennrp47b9dd52010-11-24 18:12:06 +00005394
cristy3ed852e2009-09-05 21:47:34 +00005395 if (change_clipping)
5396 {
5397 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5398 p+=17;
5399 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005400
cristy3ed852e2009-09-05 21:47:34 +00005401 if (logging != MagickFalse)
5402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005403 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005404 (double) fb.left,(double) fb.right,(double) fb.top,
5405 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005406
cristy3ed852e2009-09-05 21:47:34 +00005407 if (change_clipping == 2)
5408 default_fb=fb;
5409 }
5410 }
5411 }
5412 mng_info->clip=fb;
5413 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005414
cristybb503372010-05-27 20:51:26 +00005415 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005416 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005417
cristybb503372010-05-27 20:51:26 +00005418 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005419 -mng_info->clip.top);
5420 /*
5421 Insert a background layer behind the frame if framing_mode is 4.
5422 */
5423#if defined(MNG_INSERT_LAYERS)
5424 if (logging != MagickFalse)
5425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005426 " subframe_width=%.20g, subframe_height=%.20g",(double)
5427 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005428
cristy3ed852e2009-09-05 21:47:34 +00005429 if (insert_layers && (mng_info->framing_mode == 4) &&
5430 (subframe_width) && (subframe_height))
5431 {
glennrp47b9dd52010-11-24 18:12:06 +00005432 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005433 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005434 {
cristy16ea1392012-03-21 20:38:41 +00005435 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005436
cristy3ed852e2009-09-05 21:47:34 +00005437 if (GetNextImageInList(image) == (Image *) NULL)
5438 {
5439 image=DestroyImageList(image);
5440 MngInfoFreeStruct(mng_info,&have_mng_structure);
5441 return((Image *) NULL);
5442 }
glennrp47b9dd52010-11-24 18:12:06 +00005443
cristy3ed852e2009-09-05 21:47:34 +00005444 image=SyncNextImageInList(image);
5445 }
glennrp0fe50b42010-11-16 03:52:51 +00005446
cristy3ed852e2009-09-05 21:47:34 +00005447 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005448
cristy3ed852e2009-09-05 21:47:34 +00005449 if (term_chunk_found)
5450 {
5451 image->start_loop=MagickTrue;
5452 image->iterations=mng_iterations;
5453 term_chunk_found=MagickFalse;
5454 }
glennrp0fe50b42010-11-16 03:52:51 +00005455
cristy3ed852e2009-09-05 21:47:34 +00005456 else
5457 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005458
cristy3ed852e2009-09-05 21:47:34 +00005459 image->columns=subframe_width;
5460 image->rows=subframe_height;
5461 image->page.width=subframe_width;
5462 image->page.height=subframe_height;
5463 image->page.x=mng_info->clip.left;
5464 image->page.y=mng_info->clip.top;
5465 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00005466 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00005467 image->delay=0;
cristy16ea1392012-03-21 20:38:41 +00005468 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005469
cristy3ed852e2009-09-05 21:47:34 +00005470 if (logging != MagickFalse)
5471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005472 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005473 (double) mng_info->clip.left,(double) mng_info->clip.right,
5474 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005475 }
5476#endif
5477 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5478 continue;
5479 }
5480 if (memcmp(type,mng_CLIP,4) == 0)
5481 {
5482 unsigned int
5483 first_object,
5484 last_object;
5485
5486 /*
5487 Read CLIP.
5488 */
5489 first_object=(p[0] << 8) | p[1];
5490 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005491
cristy3ed852e2009-09-05 21:47:34 +00005492 for (i=(int) first_object; i <= (int) last_object; i++)
5493 {
5494 if (mng_info->exists[i] && !mng_info->frozen[i])
5495 {
5496 MngBox
5497 box;
5498
5499 box=mng_info->object_clip[i];
5500 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5501 }
5502 }
glennrp47b9dd52010-11-24 18:12:06 +00005503
cristy3ed852e2009-09-05 21:47:34 +00005504 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5505 continue;
5506 }
5507 if (memcmp(type,mng_SAVE,4) == 0)
5508 {
5509 for (i=1; i < MNG_MAX_OBJECTS; i++)
5510 if (mng_info->exists[i])
5511 {
5512 mng_info->frozen[i]=MagickTrue;
5513#ifdef MNG_OBJECT_BUFFERS
5514 if (mng_info->ob[i] != (MngBuffer *) NULL)
5515 mng_info->ob[i]->frozen=MagickTrue;
5516#endif
5517 }
glennrp0fe50b42010-11-16 03:52:51 +00005518
cristy3ed852e2009-09-05 21:47:34 +00005519 if (length)
5520 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005521
cristy3ed852e2009-09-05 21:47:34 +00005522 continue;
5523 }
5524
5525 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5526 {
glennrp47b9dd52010-11-24 18:12:06 +00005527 /* Read DISC or SEEK. */
5528
cristy3ed852e2009-09-05 21:47:34 +00005529 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5530 {
5531 for (i=1; i < MNG_MAX_OBJECTS; i++)
5532 MngInfoDiscardObject(mng_info,i);
5533 }
glennrp0fe50b42010-11-16 03:52:51 +00005534
cristy3ed852e2009-09-05 21:47:34 +00005535 else
5536 {
cristybb503372010-05-27 20:51:26 +00005537 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005538 j;
5539
cristybb503372010-05-27 20:51:26 +00005540 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005541 {
5542 i=p[j] << 8 | p[j+1];
5543 MngInfoDiscardObject(mng_info,i);
5544 }
5545 }
glennrp0fe50b42010-11-16 03:52:51 +00005546
cristy3ed852e2009-09-05 21:47:34 +00005547 if (length)
5548 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005549
cristy3ed852e2009-09-05 21:47:34 +00005550 continue;
5551 }
glennrp47b9dd52010-11-24 18:12:06 +00005552
cristy3ed852e2009-09-05 21:47:34 +00005553 if (memcmp(type,mng_MOVE,4) == 0)
5554 {
cristybb503372010-05-27 20:51:26 +00005555 size_t
cristy3ed852e2009-09-05 21:47:34 +00005556 first_object,
5557 last_object;
5558
glennrp47b9dd52010-11-24 18:12:06 +00005559 /* read MOVE */
5560
cristy3ed852e2009-09-05 21:47:34 +00005561 first_object=(p[0] << 8) | p[1];
5562 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005563 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005564 {
5565 if (mng_info->exists[i] && !mng_info->frozen[i])
5566 {
5567 MngPair
5568 new_pair;
5569
5570 MngPair
5571 old_pair;
5572
5573 old_pair.a=mng_info->x_off[i];
5574 old_pair.b=mng_info->y_off[i];
5575 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5576 mng_info->x_off[i]=new_pair.a;
5577 mng_info->y_off[i]=new_pair.b;
5578 }
5579 }
glennrp47b9dd52010-11-24 18:12:06 +00005580
cristy3ed852e2009-09-05 21:47:34 +00005581 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5582 continue;
5583 }
5584
5585 if (memcmp(type,mng_LOOP,4) == 0)
5586 {
cristybb503372010-05-27 20:51:26 +00005587 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005588 loop_level=chunk[0];
5589 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005590
5591 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005592 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005593
cristy3ed852e2009-09-05 21:47:34 +00005594 if (logging != MagickFalse)
5595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005596 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5597 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005598
cristy3ed852e2009-09-05 21:47:34 +00005599 if (loop_iters == 0)
5600 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005601
cristy3ed852e2009-09-05 21:47:34 +00005602 else
5603 {
5604 mng_info->loop_jump[loop_level]=TellBlob(image);
5605 mng_info->loop_count[loop_level]=loop_iters;
5606 }
glennrp0fe50b42010-11-16 03:52:51 +00005607
cristy3ed852e2009-09-05 21:47:34 +00005608 mng_info->loop_iteration[loop_level]=0;
5609 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5610 continue;
5611 }
glennrp47b9dd52010-11-24 18:12:06 +00005612
cristy3ed852e2009-09-05 21:47:34 +00005613 if (memcmp(type,mng_ENDL,4) == 0)
5614 {
5615 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005616
cristy3ed852e2009-09-05 21:47:34 +00005617 if (skipping_loop > 0)
5618 {
5619 if (skipping_loop == loop_level)
5620 {
5621 /*
5622 Found end of zero-iteration loop.
5623 */
5624 skipping_loop=(-1);
5625 mng_info->loop_active[loop_level]=0;
5626 }
5627 }
glennrp47b9dd52010-11-24 18:12:06 +00005628
cristy3ed852e2009-09-05 21:47:34 +00005629 else
5630 {
5631 if (mng_info->loop_active[loop_level] == 1)
5632 {
5633 mng_info->loop_count[loop_level]--;
5634 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005635
cristy3ed852e2009-09-05 21:47:34 +00005636 if (logging != MagickFalse)
5637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005638 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005639 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005640 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005641
cristy3ed852e2009-09-05 21:47:34 +00005642 if (mng_info->loop_count[loop_level] != 0)
5643 {
5644 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5645 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005646
cristy3ed852e2009-09-05 21:47:34 +00005647 if (offset < 0)
5648 ThrowReaderException(CorruptImageError,
5649 "ImproperImageHeader");
5650 }
glennrp47b9dd52010-11-24 18:12:06 +00005651
cristy3ed852e2009-09-05 21:47:34 +00005652 else
5653 {
5654 short
5655 last_level;
5656
5657 /*
5658 Finished loop.
5659 */
5660 mng_info->loop_active[loop_level]=0;
5661 last_level=(-1);
5662 for (i=0; i < loop_level; i++)
5663 if (mng_info->loop_active[i] == 1)
5664 last_level=(short) i;
5665 loop_level=last_level;
5666 }
5667 }
5668 }
glennrp47b9dd52010-11-24 18:12:06 +00005669
cristy3ed852e2009-09-05 21:47:34 +00005670 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5671 continue;
5672 }
glennrp47b9dd52010-11-24 18:12:06 +00005673
cristy3ed852e2009-09-05 21:47:34 +00005674 if (memcmp(type,mng_CLON,4) == 0)
5675 {
5676 if (mng_info->clon_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005677 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005678 CoderError,"CLON is not implemented yet","`%s'",
5679 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005680
cristy3ed852e2009-09-05 21:47:34 +00005681 mng_info->clon_warning++;
5682 }
glennrp47b9dd52010-11-24 18:12:06 +00005683
cristy3ed852e2009-09-05 21:47:34 +00005684 if (memcmp(type,mng_MAGN,4) == 0)
5685 {
5686 png_uint_16
5687 magn_first,
5688 magn_last,
5689 magn_mb,
5690 magn_ml,
5691 magn_mr,
5692 magn_mt,
5693 magn_mx,
5694 magn_my,
5695 magn_methx,
5696 magn_methy;
5697
5698 if (length > 1)
5699 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005700
cristy3ed852e2009-09-05 21:47:34 +00005701 else
5702 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005703
cristy3ed852e2009-09-05 21:47:34 +00005704 if (length > 3)
5705 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005706
cristy3ed852e2009-09-05 21:47:34 +00005707 else
5708 magn_last=magn_first;
5709#ifndef MNG_OBJECT_BUFFERS
5710 if (magn_first || magn_last)
5711 if (mng_info->magn_warning == 0)
5712 {
cristy16ea1392012-03-21 20:38:41 +00005713 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005714 GetMagickModule(),CoderError,
5715 "MAGN is not implemented yet for nonzero objects",
5716 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005717
cristy3ed852e2009-09-05 21:47:34 +00005718 mng_info->magn_warning++;
5719 }
5720#endif
5721 if (length > 4)
5722 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005723
cristy3ed852e2009-09-05 21:47:34 +00005724 else
5725 magn_methx=0;
5726
5727 if (length > 6)
5728 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005729
cristy3ed852e2009-09-05 21:47:34 +00005730 else
5731 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005732
cristy3ed852e2009-09-05 21:47:34 +00005733 if (magn_mx == 0)
5734 magn_mx=1;
5735
5736 if (length > 8)
5737 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005738
cristy3ed852e2009-09-05 21:47:34 +00005739 else
5740 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005741
cristy3ed852e2009-09-05 21:47:34 +00005742 if (magn_my == 0)
5743 magn_my=1;
5744
5745 if (length > 10)
5746 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005747
cristy3ed852e2009-09-05 21:47:34 +00005748 else
5749 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005750
cristy3ed852e2009-09-05 21:47:34 +00005751 if (magn_ml == 0)
5752 magn_ml=1;
5753
5754 if (length > 12)
5755 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005756
cristy3ed852e2009-09-05 21:47:34 +00005757 else
5758 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005759
cristy3ed852e2009-09-05 21:47:34 +00005760 if (magn_mr == 0)
5761 magn_mr=1;
5762
5763 if (length > 14)
5764 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005765
cristy3ed852e2009-09-05 21:47:34 +00005766 else
5767 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005768
cristy3ed852e2009-09-05 21:47:34 +00005769 if (magn_mt == 0)
5770 magn_mt=1;
5771
5772 if (length > 16)
5773 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005774
cristy3ed852e2009-09-05 21:47:34 +00005775 else
5776 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005777
cristy3ed852e2009-09-05 21:47:34 +00005778 if (magn_mb == 0)
5779 magn_mb=1;
5780
5781 if (length > 17)
5782 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005783
cristy3ed852e2009-09-05 21:47:34 +00005784 else
5785 magn_methy=magn_methx;
5786
glennrp47b9dd52010-11-24 18:12:06 +00005787
cristy3ed852e2009-09-05 21:47:34 +00005788 if (magn_methx > 5 || magn_methy > 5)
5789 if (mng_info->magn_warning == 0)
5790 {
cristy16ea1392012-03-21 20:38:41 +00005791 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005792 GetMagickModule(),CoderError,
5793 "Unknown MAGN method in MNG datastream","`%s'",
5794 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005795
cristy3ed852e2009-09-05 21:47:34 +00005796 mng_info->magn_warning++;
5797 }
5798#ifdef MNG_OBJECT_BUFFERS
5799 /* Magnify existing objects in the range magn_first to magn_last */
5800#endif
5801 if (magn_first == 0 || magn_last == 0)
5802 {
5803 /* Save the magnification factors for object 0 */
5804 mng_info->magn_mb=magn_mb;
5805 mng_info->magn_ml=magn_ml;
5806 mng_info->magn_mr=magn_mr;
5807 mng_info->magn_mt=magn_mt;
5808 mng_info->magn_mx=magn_mx;
5809 mng_info->magn_my=magn_my;
5810 mng_info->magn_methx=magn_methx;
5811 mng_info->magn_methy=magn_methy;
5812 }
5813 }
glennrp47b9dd52010-11-24 18:12:06 +00005814
cristy3ed852e2009-09-05 21:47:34 +00005815 if (memcmp(type,mng_PAST,4) == 0)
5816 {
5817 if (mng_info->past_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005818 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005819 CoderError,"PAST is not implemented yet","`%s'",
5820 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005821
cristy3ed852e2009-09-05 21:47:34 +00005822 mng_info->past_warning++;
5823 }
glennrp47b9dd52010-11-24 18:12:06 +00005824
cristy3ed852e2009-09-05 21:47:34 +00005825 if (memcmp(type,mng_SHOW,4) == 0)
5826 {
5827 if (mng_info->show_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005828 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005829 CoderError,"SHOW is not implemented yet","`%s'",
5830 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005831
cristy3ed852e2009-09-05 21:47:34 +00005832 mng_info->show_warning++;
5833 }
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristy3ed852e2009-09-05 21:47:34 +00005835 if (memcmp(type,mng_sBIT,4) == 0)
5836 {
5837 if (length < 4)
5838 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005839
cristy3ed852e2009-09-05 21:47:34 +00005840 else
5841 {
5842 mng_info->global_sbit.gray=p[0];
5843 mng_info->global_sbit.red=p[0];
5844 mng_info->global_sbit.green=p[1];
5845 mng_info->global_sbit.blue=p[2];
5846 mng_info->global_sbit.alpha=p[3];
5847 mng_info->have_global_sbit=MagickTrue;
5848 }
5849 }
5850 if (memcmp(type,mng_pHYs,4) == 0)
5851 {
5852 if (length > 8)
5853 {
5854 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005855 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005856 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005857 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005858 mng_info->global_phys_unit_type=p[8];
5859 mng_info->have_global_phys=MagickTrue;
5860 }
glennrp47b9dd52010-11-24 18:12:06 +00005861
cristy3ed852e2009-09-05 21:47:34 +00005862 else
5863 mng_info->have_global_phys=MagickFalse;
5864 }
5865 if (memcmp(type,mng_pHYg,4) == 0)
5866 {
5867 if (mng_info->phyg_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005868 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005869 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005870
cristy3ed852e2009-09-05 21:47:34 +00005871 mng_info->phyg_warning++;
5872 }
5873 if (memcmp(type,mng_BASI,4) == 0)
5874 {
5875 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005876
cristy3ed852e2009-09-05 21:47:34 +00005877 if (mng_info->basi_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005878 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005879 CoderError,"BASI is not implemented yet","`%s'",
5880 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005881
cristy3ed852e2009-09-05 21:47:34 +00005882 mng_info->basi_warning++;
5883#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005884 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005885 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005886 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005887 (p[6] << 8) | p[7]);
5888 basi_color_type=p[8];
5889 basi_compression_method=p[9];
5890 basi_filter_type=p[10];
5891 basi_interlace_method=p[11];
5892 if (length > 11)
5893 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005894
cristy3ed852e2009-09-05 21:47:34 +00005895 else
5896 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005897
cristy3ed852e2009-09-05 21:47:34 +00005898 if (length > 13)
5899 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005900
cristy3ed852e2009-09-05 21:47:34 +00005901 else
5902 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005903
cristy3ed852e2009-09-05 21:47:34 +00005904 if (length > 15)
5905 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005906
cristy3ed852e2009-09-05 21:47:34 +00005907 else
5908 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005909
cristy3ed852e2009-09-05 21:47:34 +00005910 if (length > 17)
5911 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005912
cristy3ed852e2009-09-05 21:47:34 +00005913 else
5914 {
5915 if (basi_sample_depth == 16)
5916 basi_alpha=65535L;
5917 else
5918 basi_alpha=255;
5919 }
glennrp47b9dd52010-11-24 18:12:06 +00005920
cristy3ed852e2009-09-05 21:47:34 +00005921 if (length > 19)
5922 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005923
cristy3ed852e2009-09-05 21:47:34 +00005924 else
5925 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005926
cristy3ed852e2009-09-05 21:47:34 +00005927#endif
5928 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5929 continue;
5930 }
glennrp47b9dd52010-11-24 18:12:06 +00005931
cristy3ed852e2009-09-05 21:47:34 +00005932 if (memcmp(type,mng_IHDR,4)
5933#if defined(JNG_SUPPORTED)
5934 && memcmp(type,mng_JHDR,4)
5935#endif
5936 )
5937 {
5938 /* Not an IHDR or JHDR chunk */
5939 if (length)
5940 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005941
cristy3ed852e2009-09-05 21:47:34 +00005942 continue;
5943 }
5944/* Process IHDR */
5945 if (logging != MagickFalse)
5946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5947 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005948
cristy3ed852e2009-09-05 21:47:34 +00005949 mng_info->exists[object_id]=MagickTrue;
5950 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005951
cristy3ed852e2009-09-05 21:47:34 +00005952 if (mng_info->invisible[object_id])
5953 {
5954 if (logging != MagickFalse)
5955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5956 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005957
cristy3ed852e2009-09-05 21:47:34 +00005958 skip_to_iend=MagickTrue;
5959 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5960 continue;
5961 }
5962#if defined(MNG_INSERT_LAYERS)
5963 if (length < 8)
5964 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005965
cristy8182b072010-05-30 20:10:53 +00005966 image_width=(size_t) mng_get_long(p);
5967 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005968#endif
5969 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5970
5971 /*
5972 Insert a transparent background layer behind the entire animation
5973 if it is not full screen.
5974 */
5975#if defined(MNG_INSERT_LAYERS)
5976 if (insert_layers && mng_type && first_mng_object)
5977 {
5978 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5979 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005980 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005981 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005982 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005983 {
cristy16ea1392012-03-21 20:38:41 +00005984 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005985 {
5986 /*
5987 Allocate next image structure.
5988 */
cristy16ea1392012-03-21 20:38:41 +00005989 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005990
cristy3ed852e2009-09-05 21:47:34 +00005991 if (GetNextImageInList(image) == (Image *) NULL)
5992 {
5993 image=DestroyImageList(image);
5994 MngInfoFreeStruct(mng_info,&have_mng_structure);
5995 return((Image *) NULL);
5996 }
glennrp47b9dd52010-11-24 18:12:06 +00005997
cristy3ed852e2009-09-05 21:47:34 +00005998 image=SyncNextImageInList(image);
5999 }
6000 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00006001
cristy3ed852e2009-09-05 21:47:34 +00006002 if (term_chunk_found)
6003 {
6004 image->start_loop=MagickTrue;
6005 image->iterations=mng_iterations;
6006 term_chunk_found=MagickFalse;
6007 }
glennrp47b9dd52010-11-24 18:12:06 +00006008
cristy3ed852e2009-09-05 21:47:34 +00006009 else
6010 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006011
6012 /* Make a background rectangle. */
6013
cristy3ed852e2009-09-05 21:47:34 +00006014 image->delay=0;
6015 image->columns=mng_info->mng_width;
6016 image->rows=mng_info->mng_height;
6017 image->page.width=mng_info->mng_width;
6018 image->page.height=mng_info->mng_height;
6019 image->page.x=0;
6020 image->page.y=0;
6021 image->background_color=mng_background_color;
cristy16ea1392012-03-21 20:38:41 +00006022 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006023 if (logging != MagickFalse)
6024 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006025 " Inserted transparent background layer, W=%.20g, H=%.20g",
6026 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00006027 }
6028 }
6029 /*
6030 Insert a background layer behind the upcoming image if
6031 framing_mode is 3, and we haven't already inserted one.
6032 */
6033 if (insert_layers && (mng_info->framing_mode == 3) &&
6034 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6035 (simplicity & 0x08)))
6036 {
cristy16ea1392012-03-21 20:38:41 +00006037 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006038 {
6039 /*
6040 Allocate next image structure.
6041 */
cristy16ea1392012-03-21 20:38:41 +00006042 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006043
cristy3ed852e2009-09-05 21:47:34 +00006044 if (GetNextImageInList(image) == (Image *) NULL)
6045 {
6046 image=DestroyImageList(image);
6047 MngInfoFreeStruct(mng_info,&have_mng_structure);
6048 return((Image *) NULL);
6049 }
glennrp47b9dd52010-11-24 18:12:06 +00006050
cristy3ed852e2009-09-05 21:47:34 +00006051 image=SyncNextImageInList(image);
6052 }
glennrp0fe50b42010-11-16 03:52:51 +00006053
cristy3ed852e2009-09-05 21:47:34 +00006054 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00006055
cristy3ed852e2009-09-05 21:47:34 +00006056 if (term_chunk_found)
6057 {
6058 image->start_loop=MagickTrue;
6059 image->iterations=mng_iterations;
6060 term_chunk_found=MagickFalse;
6061 }
glennrp0fe50b42010-11-16 03:52:51 +00006062
cristy3ed852e2009-09-05 21:47:34 +00006063 else
6064 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006065
cristy3ed852e2009-09-05 21:47:34 +00006066 image->delay=0;
6067 image->columns=subframe_width;
6068 image->rows=subframe_height;
6069 image->page.width=subframe_width;
6070 image->page.height=subframe_height;
6071 image->page.x=mng_info->clip.left;
6072 image->page.y=mng_info->clip.top;
6073 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00006074 image->alpha_trait=UndefinedPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00006075 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006076
cristy3ed852e2009-09-05 21:47:34 +00006077 if (logging != MagickFalse)
6078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006079 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006080 (double) mng_info->clip.left,(double) mng_info->clip.right,
6081 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006082 }
6083#endif /* MNG_INSERT_LAYERS */
6084 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006085
cristy16ea1392012-03-21 20:38:41 +00006086 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006087 {
6088 /*
6089 Allocate next image structure.
6090 */
cristy16ea1392012-03-21 20:38:41 +00006091 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006092
cristy3ed852e2009-09-05 21:47:34 +00006093 if (GetNextImageInList(image) == (Image *) NULL)
6094 {
6095 image=DestroyImageList(image);
6096 MngInfoFreeStruct(mng_info,&have_mng_structure);
6097 return((Image *) NULL);
6098 }
glennrp47b9dd52010-11-24 18:12:06 +00006099
cristy3ed852e2009-09-05 21:47:34 +00006100 image=SyncNextImageInList(image);
6101 }
6102 mng_info->image=image;
6103 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6104 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006105
cristy3ed852e2009-09-05 21:47:34 +00006106 if (status == MagickFalse)
6107 break;
glennrp0fe50b42010-11-16 03:52:51 +00006108
cristy3ed852e2009-09-05 21:47:34 +00006109 if (term_chunk_found)
6110 {
6111 image->start_loop=MagickTrue;
6112 term_chunk_found=MagickFalse;
6113 }
glennrp0fe50b42010-11-16 03:52:51 +00006114
cristy3ed852e2009-09-05 21:47:34 +00006115 else
6116 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006117
cristy3ed852e2009-09-05 21:47:34 +00006118 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6119 {
6120 image->delay=frame_delay;
6121 frame_delay=default_frame_delay;
6122 }
glennrp0fe50b42010-11-16 03:52:51 +00006123
cristy3ed852e2009-09-05 21:47:34 +00006124 else
6125 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006126
cristy3ed852e2009-09-05 21:47:34 +00006127 image->page.width=mng_info->mng_width;
6128 image->page.height=mng_info->mng_height;
6129 image->page.x=mng_info->x_off[object_id];
6130 image->page.y=mng_info->y_off[object_id];
6131 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006132
cristy3ed852e2009-09-05 21:47:34 +00006133 /*
6134 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6135 */
glennrp47b9dd52010-11-24 18:12:06 +00006136
cristy3ed852e2009-09-05 21:47:34 +00006137 if (logging != MagickFalse)
6138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6139 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6140 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006141
cristybb503372010-05-27 20:51:26 +00006142 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006143
cristy3ed852e2009-09-05 21:47:34 +00006144 if (offset < 0)
6145 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6146 }
6147
6148 previous=image;
6149 mng_info->image=image;
6150 mng_info->mng_type=mng_type;
6151 mng_info->object_id=object_id;
6152
6153 if (memcmp(type,mng_IHDR,4) == 0)
6154 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006155
cristy3ed852e2009-09-05 21:47:34 +00006156#if defined(JNG_SUPPORTED)
6157 else
6158 image=ReadOneJNGImage(mng_info,image_info,exception);
6159#endif
6160
6161 if (image == (Image *) NULL)
6162 {
6163 if (IsImageObject(previous) != MagickFalse)
6164 {
6165 (void) DestroyImageList(previous);
6166 (void) CloseBlob(previous);
6167 }
glennrp47b9dd52010-11-24 18:12:06 +00006168
cristy3ed852e2009-09-05 21:47:34 +00006169 MngInfoFreeStruct(mng_info,&have_mng_structure);
6170 return((Image *) NULL);
6171 }
glennrp0fe50b42010-11-16 03:52:51 +00006172
cristy3ed852e2009-09-05 21:47:34 +00006173 if (image->columns == 0 || image->rows == 0)
6174 {
6175 (void) CloseBlob(image);
6176 image=DestroyImageList(image);
6177 MngInfoFreeStruct(mng_info,&have_mng_structure);
6178 return((Image *) NULL);
6179 }
glennrp0fe50b42010-11-16 03:52:51 +00006180
cristy3ed852e2009-09-05 21:47:34 +00006181 mng_info->image=image;
6182
6183 if (mng_type)
6184 {
6185 MngBox
6186 crop_box;
6187
6188 if (mng_info->magn_methx || mng_info->magn_methy)
6189 {
6190 png_uint_32
6191 magnified_height,
6192 magnified_width;
6193
6194 if (logging != MagickFalse)
6195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6196 " Processing MNG MAGN chunk");
6197
6198 if (mng_info->magn_methx == 1)
6199 {
6200 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006201
cristy3ed852e2009-09-05 21:47:34 +00006202 if (image->columns > 1)
6203 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006204
cristy3ed852e2009-09-05 21:47:34 +00006205 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006206 magnified_width += (png_uint_32)
6207 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006208 }
glennrp47b9dd52010-11-24 18:12:06 +00006209
cristy3ed852e2009-09-05 21:47:34 +00006210 else
6211 {
cristy4e5bc842010-06-09 13:56:01 +00006212 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006213
cristy3ed852e2009-09-05 21:47:34 +00006214 if (image->columns > 1)
6215 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006216
cristy3ed852e2009-09-05 21:47:34 +00006217 if (image->columns > 2)
6218 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006219
cristy3ed852e2009-09-05 21:47:34 +00006220 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006221 magnified_width += (png_uint_32)
6222 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006223 }
glennrp47b9dd52010-11-24 18:12:06 +00006224
cristy3ed852e2009-09-05 21:47:34 +00006225 if (mng_info->magn_methy == 1)
6226 {
6227 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006228
cristy3ed852e2009-09-05 21:47:34 +00006229 if (image->rows > 1)
6230 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006231
cristy3ed852e2009-09-05 21:47:34 +00006232 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006233 magnified_height += (png_uint_32)
6234 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006235 }
glennrp47b9dd52010-11-24 18:12:06 +00006236
cristy3ed852e2009-09-05 21:47:34 +00006237 else
6238 {
cristy4e5bc842010-06-09 13:56:01 +00006239 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006240
cristy3ed852e2009-09-05 21:47:34 +00006241 if (image->rows > 1)
6242 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006243
cristy3ed852e2009-09-05 21:47:34 +00006244 if (image->rows > 2)
6245 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006246
cristy3ed852e2009-09-05 21:47:34 +00006247 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006248 magnified_height += (png_uint_32)
6249 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006250 }
glennrp47b9dd52010-11-24 18:12:06 +00006251
cristy3ed852e2009-09-05 21:47:34 +00006252 if (magnified_height > image->rows ||
6253 magnified_width > image->columns)
6254 {
6255 Image
6256 *large_image;
6257
6258 int
6259 yy;
6260
cristy16ea1392012-03-21 20:38:41 +00006261 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006262 *next,
6263 *prev;
6264
6265 png_uint_16
6266 magn_methx,
6267 magn_methy;
6268
cristy16ea1392012-03-21 20:38:41 +00006269 ssize_t
6270 m,
6271 y;
6272
6273 register Quantum
6274 *n,
6275 *q;
6276
6277 register ssize_t
6278 x;
6279
glennrp47b9dd52010-11-24 18:12:06 +00006280 /* Allocate next image structure. */
6281
cristy3ed852e2009-09-05 21:47:34 +00006282 if (logging != MagickFalse)
6283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6284 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006285
cristy16ea1392012-03-21 20:38:41 +00006286 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006287
cristy3ed852e2009-09-05 21:47:34 +00006288 if (GetNextImageInList(image) == (Image *) NULL)
6289 {
6290 image=DestroyImageList(image);
6291 MngInfoFreeStruct(mng_info,&have_mng_structure);
6292 return((Image *) NULL);
6293 }
6294
6295 large_image=SyncNextImageInList(image);
6296
6297 large_image->columns=magnified_width;
6298 large_image->rows=magnified_height;
6299
6300 magn_methx=mng_info->magn_methx;
6301 magn_methy=mng_info->magn_methy;
6302
glennrp3faa9a32011-04-23 14:00:25 +00006303#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006304#define QM unsigned short
6305 if (magn_methx != 1 || magn_methy != 1)
6306 {
6307 /*
6308 Scale pixels to unsigned shorts to prevent
6309 overflow of intermediate values of interpolations
6310 */
cristybb503372010-05-27 20:51:26 +00006311 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006312 {
6313 q=GetAuthenticPixels(image,0,y,image->columns,1,
6314 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006315
cristybb503372010-05-27 20:51:26 +00006316 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006317 {
cristy16ea1392012-03-21 20:38:41 +00006318 SetPixelRed(image,ScaleQuantumToShort(
6319 GetPixelRed(image,q)),q);
6320 SetPixelGreen(image,ScaleQuantumToShort(
6321 GetPixelGreen(image,q)),q);
6322 SetPixelBlue(image,ScaleQuantumToShort(
6323 GetPixelBlue(image,q)),q);
6324 SetPixelAlpha(image,ScaleQuantumToShort(
6325 GetPixelAlpha(image,q)),q);
6326 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006327 }
glennrp47b9dd52010-11-24 18:12:06 +00006328
cristy3ed852e2009-09-05 21:47:34 +00006329 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6330 break;
6331 }
6332 }
6333#else
6334#define QM Quantum
6335#endif
6336
cristy8a46d822012-08-28 23:32:39 +00006337 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006338 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006339
cristy3ed852e2009-09-05 21:47:34 +00006340 else
6341 {
cristy16ea1392012-03-21 20:38:41 +00006342 large_image->background_color.alpha=OpaqueAlpha;
6343 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006344
cristy3ed852e2009-09-05 21:47:34 +00006345 if (magn_methx == 4)
6346 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006347
cristy3ed852e2009-09-05 21:47:34 +00006348 if (magn_methx == 5)
6349 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006350
cristy3ed852e2009-09-05 21:47:34 +00006351 if (magn_methy == 4)
6352 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006353
cristy3ed852e2009-09-05 21:47:34 +00006354 if (magn_methy == 5)
6355 magn_methy=3;
6356 }
6357
6358 /* magnify the rows into the right side of the large image */
6359
6360 if (logging != MagickFalse)
6361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006362 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006363 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006364 yy=0;
cristy16ea1392012-03-21 20:38:41 +00006365 length=(size_t) image->columns*GetPixelChannels(image);
6366 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6367 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006368
cristy16ea1392012-03-21 20:38:41 +00006369 if ((prev == (Quantum *) NULL) ||
6370 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006371 {
6372 image=DestroyImageList(image);
6373 MngInfoFreeStruct(mng_info,&have_mng_structure);
6374 ThrowReaderException(ResourceLimitError,
6375 "MemoryAllocationFailed");
6376 }
glennrp47b9dd52010-11-24 18:12:06 +00006377
cristy3ed852e2009-09-05 21:47:34 +00006378 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6379 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006380
cristybb503372010-05-27 20:51:26 +00006381 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006382 {
6383 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006384 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006385
cristybb503372010-05-27 20:51:26 +00006386 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6387 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006388
cristybb503372010-05-27 20:51:26 +00006389 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6390 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006391
cristybb503372010-05-27 20:51:26 +00006392 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006393 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006394
cristy3ed852e2009-09-05 21:47:34 +00006395 else
cristybb503372010-05-27 20:51:26 +00006396 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006397
cristy3ed852e2009-09-05 21:47:34 +00006398 n=prev;
6399 prev=next;
6400 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006401
cristybb503372010-05-27 20:51:26 +00006402 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006403 {
6404 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6405 exception);
6406 (void) CopyMagickMemory(next,n,length);
6407 }
glennrp47b9dd52010-11-24 18:12:06 +00006408
cristy3ed852e2009-09-05 21:47:34 +00006409 for (i=0; i < m; i++, yy++)
6410 {
cristy16ea1392012-03-21 20:38:41 +00006411 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006412 *pixels;
6413
cristybb503372010-05-27 20:51:26 +00006414 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006415 pixels=prev;
6416 n=next;
6417 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006418 1,exception);
cristy16ea1392012-03-21 20:38:41 +00006419 q+=(large_image->columns-image->columns)*
6420 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006421
cristybb503372010-05-27 20:51:26 +00006422 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006423 {
glennrpfd05d622011-02-25 04:10:33 +00006424 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006425 /*
6426 if (image->storage_class == PseudoClass)
6427 {
6428 }
6429 */
6430
6431 if (magn_methy <= 1)
6432 {
glennrpbb4f99d2011-05-22 11:13:17 +00006433 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006434 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6435 SetPixelGreen(large_image,GetPixelGreen(image,
6436 pixels),q);
6437 SetPixelBlue(large_image,GetPixelBlue(image,
6438 pixels),q);
6439 SetPixelAlpha(large_image,GetPixelAlpha(image,
6440 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006441 }
glennrp47b9dd52010-11-24 18:12:06 +00006442
cristy3ed852e2009-09-05 21:47:34 +00006443 else if (magn_methy == 2 || magn_methy == 4)
6444 {
6445 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006446 {
cristy16ea1392012-03-21 20:38:41 +00006447 SetPixelRed(large_image,GetPixelRed(image,
6448 pixels),q);
6449 SetPixelGreen(large_image,GetPixelGreen(image,
6450 pixels),q);
6451 SetPixelBlue(large_image,GetPixelBlue(image,
6452 pixels),q);
6453 SetPixelAlpha(large_image,GetPixelAlpha(image,
6454 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006455 }
glennrp47b9dd52010-11-24 18:12:06 +00006456
cristy3ed852e2009-09-05 21:47:34 +00006457 else
6458 {
6459 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006460 SetPixelRed(large_image,((QM) (((ssize_t)
6461 (2*i*(GetPixelRed(image,n)
6462 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006463 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006464 +GetPixelRed(image,pixels)))),q);
6465 SetPixelGreen(large_image,((QM) (((ssize_t)
6466 (2*i*(GetPixelGreen(image,n)
6467 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006468 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006469 +GetPixelGreen(image,pixels)))),q);
6470 SetPixelBlue(large_image,((QM) (((ssize_t)
6471 (2*i*(GetPixelBlue(image,n)
6472 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006473 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006474 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006475
cristy8a46d822012-08-28 23:32:39 +00006476 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006477 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6478 (2*i*(GetPixelAlpha(image,n)
6479 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006480 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006481 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006482 }
glennrp47b9dd52010-11-24 18:12:06 +00006483
cristy3ed852e2009-09-05 21:47:34 +00006484 if (magn_methy == 4)
6485 {
6486 /* Replicate nearest */
6487 if (i <= ((m+1) << 1))
cristy16ea1392012-03-21 20:38:41 +00006488 SetPixelAlpha(large_image,GetPixelAlpha(image,
6489 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006490 else
cristy16ea1392012-03-21 20:38:41 +00006491 SetPixelAlpha(large_image,GetPixelAlpha(image,
6492 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006493 }
6494 }
glennrp47b9dd52010-11-24 18:12:06 +00006495
cristy3ed852e2009-09-05 21:47:34 +00006496 else /* if (magn_methy == 3 || magn_methy == 5) */
6497 {
6498 /* Replicate nearest */
6499 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006500 {
cristy16ea1392012-03-21 20:38:41 +00006501 SetPixelRed(large_image,GetPixelRed(image,
6502 pixels),q);
6503 SetPixelGreen(large_image,GetPixelGreen(image,
6504 pixels),q);
6505 SetPixelBlue(large_image,GetPixelBlue(image,
6506 pixels),q);
6507 SetPixelAlpha(large_image,GetPixelAlpha(image,
6508 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006509 }
glennrp47b9dd52010-11-24 18:12:06 +00006510
cristy3ed852e2009-09-05 21:47:34 +00006511 else
glennrpbb4f99d2011-05-22 11:13:17 +00006512 {
cristy16ea1392012-03-21 20:38:41 +00006513 SetPixelRed(large_image,GetPixelRed(image,n),q);
6514 SetPixelGreen(large_image,GetPixelGreen(image,n),
6515 q);
6516 SetPixelBlue(large_image,GetPixelBlue(image,n),
6517 q);
6518 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6519 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006520 }
glennrp47b9dd52010-11-24 18:12:06 +00006521
cristy3ed852e2009-09-05 21:47:34 +00006522 if (magn_methy == 5)
6523 {
cristy16ea1392012-03-21 20:38:41 +00006524 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6525 (GetPixelAlpha(image,n)
6526 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006527 +m))/((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006528 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006529 }
6530 }
cristy16ea1392012-03-21 20:38:41 +00006531 n+=GetPixelChannels(image);
6532 q+=GetPixelChannels(large_image);
6533 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006534 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006535
cristy3ed852e2009-09-05 21:47:34 +00006536 if (SyncAuthenticPixels(large_image,exception) == 0)
6537 break;
glennrp47b9dd52010-11-24 18:12:06 +00006538
cristy3ed852e2009-09-05 21:47:34 +00006539 } /* i */
6540 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006541
cristy16ea1392012-03-21 20:38:41 +00006542 prev=(Quantum *) RelinquishMagickMemory(prev);
6543 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006544
6545 length=image->columns;
6546
6547 if (logging != MagickFalse)
6548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6549 " Delete original image");
6550
6551 DeleteImageFromList(&image);
6552
6553 image=large_image;
6554
6555 mng_info->image=image;
6556
6557 /* magnify the columns */
6558 if (logging != MagickFalse)
6559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006560 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006561
cristybb503372010-05-27 20:51:26 +00006562 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006563 {
cristy16ea1392012-03-21 20:38:41 +00006564 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006565 *pixels;
6566
6567 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00006568 pixels=q+(image->columns-length)*GetPixelChannels(image);
6569 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006570
cristybb503372010-05-27 20:51:26 +00006571 for (x=(ssize_t) (image->columns-length);
6572 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006573 {
cristy16ea1392012-03-21 20:38:41 +00006574 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006575
cristybb503372010-05-27 20:51:26 +00006576 if (x == (ssize_t) (image->columns-length))
6577 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006578
cristybb503372010-05-27 20:51:26 +00006579 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6580 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006581
cristybb503372010-05-27 20:51:26 +00006582 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6583 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006584
cristybb503372010-05-27 20:51:26 +00006585 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006586 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006587
cristy3ed852e2009-09-05 21:47:34 +00006588 else
cristybb503372010-05-27 20:51:26 +00006589 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006590
cristy3ed852e2009-09-05 21:47:34 +00006591 for (i=0; i < m; i++)
6592 {
6593 if (magn_methx <= 1)
6594 {
6595 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006596 SetPixelRed(image,GetPixelRed(image,pixels),q);
6597 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6598 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6599 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006600 }
glennrp47b9dd52010-11-24 18:12:06 +00006601
cristy3ed852e2009-09-05 21:47:34 +00006602 else if (magn_methx == 2 || magn_methx == 4)
6603 {
6604 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006605 {
cristy16ea1392012-03-21 20:38:41 +00006606 SetPixelRed(image,GetPixelRed(image,pixels),q);
6607 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6608 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6609 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006610 }
glennrp47b9dd52010-11-24 18:12:06 +00006611
cristy16ea1392012-03-21 20:38:41 +00006612 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006613 else
6614 {
6615 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006616 SetPixelRed(image,(QM) ((2*i*(
6617 GetPixelRed(image,n)
6618 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006619 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006620 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006621
cristy16ea1392012-03-21 20:38:41 +00006622 SetPixelGreen(image,(QM) ((2*i*(
6623 GetPixelGreen(image,n)
6624 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006625 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006626 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006627
cristy16ea1392012-03-21 20:38:41 +00006628 SetPixelBlue(image,(QM) ((2*i*(
6629 GetPixelBlue(image,n)
6630 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006631 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006632 GetPixelBlue(image,pixels)),q);
cristy8a46d822012-08-28 23:32:39 +00006633 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006634 SetPixelAlpha(image,(QM) ((2*i*(
6635 GetPixelAlpha(image,n)
6636 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006637 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006638 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006639 }
glennrp47b9dd52010-11-24 18:12:06 +00006640
cristy3ed852e2009-09-05 21:47:34 +00006641 if (magn_methx == 4)
6642 {
6643 /* Replicate nearest */
6644 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006645 {
cristy16ea1392012-03-21 20:38:41 +00006646 SetPixelAlpha(image,
6647 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006648 }
cristy3ed852e2009-09-05 21:47:34 +00006649 else
glennrpbb4f99d2011-05-22 11:13:17 +00006650 {
cristy16ea1392012-03-21 20:38:41 +00006651 SetPixelAlpha(image,
6652 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006653 }
cristy3ed852e2009-09-05 21:47:34 +00006654 }
6655 }
glennrp47b9dd52010-11-24 18:12:06 +00006656
cristy3ed852e2009-09-05 21:47:34 +00006657 else /* if (magn_methx == 3 || magn_methx == 5) */
6658 {
6659 /* Replicate nearest */
6660 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006661 {
cristy16ea1392012-03-21 20:38:41 +00006662 SetPixelRed(image,GetPixelRed(image,pixels),q);
6663 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6664 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6665 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006666 }
glennrp47b9dd52010-11-24 18:12:06 +00006667
cristy3ed852e2009-09-05 21:47:34 +00006668 else
glennrpbb4f99d2011-05-22 11:13:17 +00006669 {
cristy16ea1392012-03-21 20:38:41 +00006670 SetPixelRed(image,GetPixelRed(image,n),q);
6671 SetPixelGreen(image,GetPixelGreen(image,n),q);
6672 SetPixelBlue(image,GetPixelBlue(image,n),q);
6673 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006674 }
glennrp47b9dd52010-11-24 18:12:06 +00006675
cristy3ed852e2009-09-05 21:47:34 +00006676 if (magn_methx == 5)
6677 {
6678 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006679 SetPixelAlpha(image,
6680 (QM) ((2*i*( GetPixelAlpha(image,n)
6681 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006682 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006683 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006684 }
6685 }
cristy16ea1392012-03-21 20:38:41 +00006686 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006687 }
cristy16ea1392012-03-21 20:38:41 +00006688 n+=GetPixelChannels(image);
6689 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006690 }
glennrp47b9dd52010-11-24 18:12:06 +00006691
cristy3ed852e2009-09-05 21:47:34 +00006692 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6693 break;
6694 }
glennrp3faa9a32011-04-23 14:00:25 +00006695#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006696 if (magn_methx != 1 || magn_methy != 1)
6697 {
6698 /*
6699 Rescale pixels to Quantum
6700 */
cristybb503372010-05-27 20:51:26 +00006701 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006702 {
6703 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006704
cristybb503372010-05-27 20:51:26 +00006705 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006706 {
cristy16ea1392012-03-21 20:38:41 +00006707 SetPixelRed(image,ScaleShortToQuantum(
6708 GetPixelRed(image,q)),q);
6709 SetPixelGreen(image,ScaleShortToQuantum(
6710 GetPixelGreen(image,q)),q);
6711 SetPixelBlue(image,ScaleShortToQuantum(
6712 GetPixelBlue(image,q)),q);
6713 SetPixelAlpha(image,ScaleShortToQuantum(
6714 GetPixelAlpha(image,q)),q);
6715 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006716 }
glennrp47b9dd52010-11-24 18:12:06 +00006717
cristy3ed852e2009-09-05 21:47:34 +00006718 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6719 break;
6720 }
6721 }
6722#endif
6723 if (logging != MagickFalse)
6724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6725 " Finished MAGN processing");
6726 }
6727 }
6728
6729 /*
6730 Crop_box is with respect to the upper left corner of the MNG.
6731 */
6732 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6733 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6734 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6735 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6736 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6737 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6738 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6739 if ((crop_box.left != (mng_info->image_box.left
6740 +mng_info->x_off[object_id])) ||
6741 (crop_box.right != (mng_info->image_box.right
6742 +mng_info->x_off[object_id])) ||
6743 (crop_box.top != (mng_info->image_box.top
6744 +mng_info->y_off[object_id])) ||
6745 (crop_box.bottom != (mng_info->image_box.bottom
6746 +mng_info->y_off[object_id])))
6747 {
6748 if (logging != MagickFalse)
6749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6750 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006751
cristy3ed852e2009-09-05 21:47:34 +00006752 if ((crop_box.left < crop_box.right) &&
6753 (crop_box.top < crop_box.bottom))
6754 {
6755 Image
6756 *im;
6757
6758 RectangleInfo
6759 crop_info;
6760
6761 /*
6762 Crop_info is with respect to the upper left corner of
6763 the image.
6764 */
6765 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6766 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006767 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6768 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006769 image->page.width=image->columns;
6770 image->page.height=image->rows;
6771 image->page.x=0;
6772 image->page.y=0;
6773 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006774
cristy3ed852e2009-09-05 21:47:34 +00006775 if (im != (Image *) NULL)
6776 {
6777 image->columns=im->columns;
6778 image->rows=im->rows;
6779 im=DestroyImage(im);
6780 image->page.width=image->columns;
6781 image->page.height=image->rows;
6782 image->page.x=crop_box.left;
6783 image->page.y=crop_box.top;
6784 }
6785 }
glennrp47b9dd52010-11-24 18:12:06 +00006786
cristy3ed852e2009-09-05 21:47:34 +00006787 else
6788 {
6789 /*
6790 No pixels in crop area. The MNG spec still requires
6791 a layer, though, so make a single transparent pixel in
6792 the top left corner.
6793 */
6794 image->columns=1;
6795 image->rows=1;
6796 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00006797 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006798 image->page.width=1;
6799 image->page.height=1;
6800 image->page.x=0;
6801 image->page.y=0;
6802 }
6803 }
6804#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6805 image=mng_info->image;
6806#endif
6807 }
6808
glennrp2b013e42010-11-24 16:55:50 +00006809#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6810 /* PNG does not handle depths greater than 16 so reduce it even
cristy16ea1392012-03-21 20:38:41 +00006811 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00006812 */
6813 if (image->depth > 16)
6814 image->depth=16;
6815#endif
6816
glennrp3faa9a32011-04-23 14:00:25 +00006817#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00006818 if (image->depth > 8)
6819 {
6820 /* To do: fill low byte properly */
6821 image->depth=16;
6822 }
6823
cristy16ea1392012-03-21 20:38:41 +00006824 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006825 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006826#endif
glennrpd6afd542010-11-19 01:53:05 +00006827
cristy3ed852e2009-09-05 21:47:34 +00006828 if (image_info->number_scenes != 0)
6829 {
6830 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006831 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006832 break;
6833 }
glennrpd6afd542010-11-19 01:53:05 +00006834
cristy3ed852e2009-09-05 21:47:34 +00006835 if (logging != MagickFalse)
6836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6837 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006838
cristy3ed852e2009-09-05 21:47:34 +00006839 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006840
cristy3ed852e2009-09-05 21:47:34 +00006841 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006842
cristy3ed852e2009-09-05 21:47:34 +00006843 if (logging != MagickFalse)
6844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6845 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006846
cristy3ed852e2009-09-05 21:47:34 +00006847#if defined(MNG_INSERT_LAYERS)
6848 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6849 (mng_info->mng_height))
6850 {
6851 /*
6852 Insert a background layer if nothing else was found.
6853 */
6854 if (logging != MagickFalse)
6855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6856 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006857
cristy16ea1392012-03-21 20:38:41 +00006858 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006859 {
6860 /*
6861 Allocate next image structure.
6862 */
cristy16ea1392012-03-21 20:38:41 +00006863 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006864 if (GetNextImageInList(image) == (Image *) NULL)
6865 {
6866 image=DestroyImageList(image);
6867 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006868
cristy3ed852e2009-09-05 21:47:34 +00006869 if (logging != MagickFalse)
6870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6871 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006872
cristy3ed852e2009-09-05 21:47:34 +00006873 return((Image *) NULL);
6874 }
6875 image=SyncNextImageInList(image);
6876 }
6877 image->columns=mng_info->mng_width;
6878 image->rows=mng_info->mng_height;
6879 image->page.width=mng_info->mng_width;
6880 image->page.height=mng_info->mng_height;
6881 image->page.x=0;
6882 image->page.y=0;
6883 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00006884 image->alpha_trait=UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00006885
cristy3ed852e2009-09-05 21:47:34 +00006886 if (image_info->ping == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006887 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006888
cristy3ed852e2009-09-05 21:47:34 +00006889 mng_info->image_found++;
6890 }
6891#endif
6892 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006893
cristy3ed852e2009-09-05 21:47:34 +00006894 if (mng_iterations == 1)
6895 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006896
cristy3ed852e2009-09-05 21:47:34 +00006897 while (GetPreviousImageInList(image) != (Image *) NULL)
6898 {
6899 image_count++;
6900 if (image_count > 10*mng_info->image_found)
6901 {
6902 if (logging != MagickFalse)
6903 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006904
cristy16ea1392012-03-21 20:38:41 +00006905 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006906 CoderError,"Linked list is corrupted, beginning of list not found",
6907 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006908
cristy3ed852e2009-09-05 21:47:34 +00006909 return((Image *) NULL);
6910 }
glennrp0fe50b42010-11-16 03:52:51 +00006911
cristy3ed852e2009-09-05 21:47:34 +00006912 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006913
cristy3ed852e2009-09-05 21:47:34 +00006914 if (GetNextImageInList(image) == (Image *) NULL)
6915 {
6916 if (logging != MagickFalse)
6917 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006918
cristy16ea1392012-03-21 20:38:41 +00006919 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006920 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6921 image_info->filename);
6922 }
6923 }
glennrp47b9dd52010-11-24 18:12:06 +00006924
cristy3ed852e2009-09-05 21:47:34 +00006925 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6926 GetNextImageInList(image) ==
6927 (Image *) NULL)
6928 {
6929 if (logging != MagickFalse)
6930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6931 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006932
cristy16ea1392012-03-21 20:38:41 +00006933 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006934 CoderError,"image->next for first image is NULL but shouldn't be.",
6935 "`%s'",image_info->filename);
6936 }
glennrp47b9dd52010-11-24 18:12:06 +00006937
cristy3ed852e2009-09-05 21:47:34 +00006938 if (mng_info->image_found == 0)
6939 {
6940 if (logging != MagickFalse)
6941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6942 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006943
cristy16ea1392012-03-21 20:38:41 +00006944 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006945 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006946
cristy3ed852e2009-09-05 21:47:34 +00006947 if (image != (Image *) NULL)
6948 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006949
cristy3ed852e2009-09-05 21:47:34 +00006950 MngInfoFreeStruct(mng_info,&have_mng_structure);
6951 return((Image *) NULL);
6952 }
6953
6954 if (mng_info->ticks_per_second)
6955 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6956 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006957
cristy3ed852e2009-09-05 21:47:34 +00006958 else
6959 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006960
cristy3ed852e2009-09-05 21:47:34 +00006961 /* Find final nonzero image delay */
6962 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006963
cristy3ed852e2009-09-05 21:47:34 +00006964 while (GetNextImageInList(image) != (Image *) NULL)
6965 {
6966 if (image->delay)
6967 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006968
cristy3ed852e2009-09-05 21:47:34 +00006969 image=GetNextImageInList(image);
6970 }
glennrp0fe50b42010-11-16 03:52:51 +00006971
cristy3ed852e2009-09-05 21:47:34 +00006972 if (final_delay < final_image_delay)
6973 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006974
cristy3ed852e2009-09-05 21:47:34 +00006975 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006976
cristy3ed852e2009-09-05 21:47:34 +00006977 if (logging != MagickFalse)
6978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006979 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6980 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006981
cristy3ed852e2009-09-05 21:47:34 +00006982 if (logging != MagickFalse)
6983 {
6984 int
6985 scene;
6986
6987 scene=0;
6988 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006989
cristy3ed852e2009-09-05 21:47:34 +00006990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6991 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006992
cristy3ed852e2009-09-05 21:47:34 +00006993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006994 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006995
cristy3ed852e2009-09-05 21:47:34 +00006996 while (GetNextImageInList(image) != (Image *) NULL)
6997 {
6998 image=GetNextImageInList(image);
6999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007000 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00007001 }
7002 }
7003
7004 image=GetFirstImageInList(image);
7005#ifdef MNG_COALESCE_LAYERS
7006 if (insert_layers)
7007 {
7008 Image
7009 *next_image,
7010 *next;
7011
cristybb503372010-05-27 20:51:26 +00007012 size_t
cristy3ed852e2009-09-05 21:47:34 +00007013 scene;
7014
7015 if (logging != MagickFalse)
7016 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00007017
cristy3ed852e2009-09-05 21:47:34 +00007018 scene=image->scene;
cristy16ea1392012-03-21 20:38:41 +00007019 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00007020
cristy3ed852e2009-09-05 21:47:34 +00007021 if (next_image == (Image *) NULL)
7022 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00007023
cristy3ed852e2009-09-05 21:47:34 +00007024 image=DestroyImageList(image);
7025 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00007026
cristy3ed852e2009-09-05 21:47:34 +00007027 for (next=image; next != (Image *) NULL; next=next_image)
7028 {
7029 next->page.width=mng_info->mng_width;
7030 next->page.height=mng_info->mng_height;
7031 next->page.x=0;
7032 next->page.y=0;
7033 next->scene=scene++;
7034 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00007035
cristy3ed852e2009-09-05 21:47:34 +00007036 if (next_image == (Image *) NULL)
7037 break;
glennrp47b9dd52010-11-24 18:12:06 +00007038
cristy3ed852e2009-09-05 21:47:34 +00007039 if (next->delay == 0)
7040 {
7041 scene--;
7042 next_image->previous=GetPreviousImageInList(next);
7043 if (GetPreviousImageInList(next) == (Image *) NULL)
7044 image=next_image;
7045 else
7046 next->previous->next=next_image;
7047 next=DestroyImage(next);
7048 }
7049 }
7050 }
7051#endif
7052
7053 while (GetNextImageInList(image) != (Image *) NULL)
7054 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007055
cristy3ed852e2009-09-05 21:47:34 +00007056 image->dispose=BackgroundDispose;
7057
7058 if (logging != MagickFalse)
7059 {
7060 int
7061 scene;
7062
7063 scene=0;
7064 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007065
cristy3ed852e2009-09-05 21:47:34 +00007066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7067 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007068
cristy3ed852e2009-09-05 21:47:34 +00007069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007070 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7071 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007072
cristy3ed852e2009-09-05 21:47:34 +00007073 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007074 {
7075 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007076
cristyf2faecf2010-05-28 19:19:36 +00007077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007078 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7079 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007080 }
7081 }
glennrp47b9dd52010-11-24 18:12:06 +00007082
cristy3ed852e2009-09-05 21:47:34 +00007083 image=GetFirstImageInList(image);
7084 MngInfoFreeStruct(mng_info,&have_mng_structure);
7085 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007086
cristy3ed852e2009-09-05 21:47:34 +00007087 if (logging != MagickFalse)
7088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007089
cristy3ed852e2009-09-05 21:47:34 +00007090 return(GetFirstImageInList(image));
7091}
glennrp25c1e2b2010-03-25 01:39:56 +00007092#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007093static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7094{
7095 printf("Your PNG library is too old: You have libpng-%s\n",
7096 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007097
cristy3ed852e2009-09-05 21:47:34 +00007098 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7099 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007100
cristy3ed852e2009-09-05 21:47:34 +00007101 return(Image *) NULL;
7102}
glennrp47b9dd52010-11-24 18:12:06 +00007103
cristy3ed852e2009-09-05 21:47:34 +00007104static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7105{
7106 return(ReadPNGImage(image_info,exception));
7107}
glennrp25c1e2b2010-03-25 01:39:56 +00007108#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007109#endif
7110
7111/*
7112%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7113% %
7114% %
7115% %
7116% R e g i s t e r P N G I m a g e %
7117% %
7118% %
7119% %
7120%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7121%
7122% RegisterPNGImage() adds properties for the PNG image format to
7123% the list of supported formats. The properties include the image format
7124% tag, a method to read and/or write the format, whether the format
7125% supports the saving of more than one frame to the same file or blob,
7126% whether the format supports native in-memory I/O, and a brief
7127% description of the format.
7128%
7129% The format of the RegisterPNGImage method is:
7130%
cristybb503372010-05-27 20:51:26 +00007131% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007132%
7133*/
cristybb503372010-05-27 20:51:26 +00007134ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007135{
7136 char
7137 version[MaxTextExtent];
7138
7139 MagickInfo
7140 *entry;
7141
7142 static const char
7143 *PNGNote=
7144 {
7145 "See http://www.libpng.org/ for details about the PNG format."
7146 },
glennrp47b9dd52010-11-24 18:12:06 +00007147
cristy3ed852e2009-09-05 21:47:34 +00007148 *JNGNote=
7149 {
7150 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7151 "format."
7152 },
glennrp47b9dd52010-11-24 18:12:06 +00007153
cristy3ed852e2009-09-05 21:47:34 +00007154 *MNGNote=
7155 {
7156 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7157 "format."
7158 };
7159
7160 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007161
cristy3ed852e2009-09-05 21:47:34 +00007162#if defined(PNG_LIBPNG_VER_STRING)
7163 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7164 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007165
cristy3ed852e2009-09-05 21:47:34 +00007166 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7167 {
7168 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7169 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7170 MaxTextExtent);
7171 }
7172#endif
glennrp47b9dd52010-11-24 18:12:06 +00007173
cristy3ed852e2009-09-05 21:47:34 +00007174 entry=SetMagickInfo("MNG");
7175 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007176
cristy3ed852e2009-09-05 21:47:34 +00007177#if defined(MAGICKCORE_PNG_DELEGATE)
7178 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7179 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7180#endif
glennrp47b9dd52010-11-24 18:12:06 +00007181
cristy3ed852e2009-09-05 21:47:34 +00007182 entry->magick=(IsImageFormatHandler *) IsMNG;
7183 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007184
cristy3ed852e2009-09-05 21:47:34 +00007185 if (*version != '\0')
7186 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007187
cristy3ed852e2009-09-05 21:47:34 +00007188 entry->module=ConstantString("PNG");
7189 entry->note=ConstantString(MNGNote);
7190 (void) RegisterMagickInfo(entry);
7191
7192 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007193
cristy3ed852e2009-09-05 21:47:34 +00007194#if defined(MAGICKCORE_PNG_DELEGATE)
7195 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7196 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7197#endif
glennrp47b9dd52010-11-24 18:12:06 +00007198
cristy3ed852e2009-09-05 21:47:34 +00007199 entry->magick=(IsImageFormatHandler *) IsPNG;
7200 entry->adjoin=MagickFalse;
7201 entry->description=ConstantString("Portable Network Graphics");
7202 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007203
cristy3ed852e2009-09-05 21:47:34 +00007204 if (*version != '\0')
7205 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007206
cristy3ed852e2009-09-05 21:47:34 +00007207 entry->note=ConstantString(PNGNote);
7208 (void) RegisterMagickInfo(entry);
7209
7210 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007211
cristy3ed852e2009-09-05 21:47:34 +00007212#if defined(MAGICKCORE_PNG_DELEGATE)
7213 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7214 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7215#endif
glennrp47b9dd52010-11-24 18:12:06 +00007216
cristy3ed852e2009-09-05 21:47:34 +00007217 entry->magick=(IsImageFormatHandler *) IsPNG;
7218 entry->adjoin=MagickFalse;
7219 entry->description=ConstantString(
7220 "8-bit indexed with optional binary transparency");
7221 entry->module=ConstantString("PNG");
7222 (void) RegisterMagickInfo(entry);
7223
7224 entry=SetMagickInfo("PNG24");
7225 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007226
cristy3ed852e2009-09-05 21:47:34 +00007227#if defined(ZLIB_VERSION)
7228 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7229 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007230
cristy3ed852e2009-09-05 21:47:34 +00007231 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7232 {
7233 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7234 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7235 }
7236#endif
glennrp47b9dd52010-11-24 18:12:06 +00007237
cristy3ed852e2009-09-05 21:47:34 +00007238 if (*version != '\0')
7239 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007240
cristy3ed852e2009-09-05 21:47:34 +00007241#if defined(MAGICKCORE_PNG_DELEGATE)
7242 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7243 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7244#endif
glennrp47b9dd52010-11-24 18:12:06 +00007245
cristy3ed852e2009-09-05 21:47:34 +00007246 entry->magick=(IsImageFormatHandler *) IsPNG;
7247 entry->adjoin=MagickFalse;
7248 entry->description=ConstantString("opaque 24-bit RGB");
7249 entry->module=ConstantString("PNG");
7250 (void) RegisterMagickInfo(entry);
7251
7252 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007253
cristy3ed852e2009-09-05 21:47:34 +00007254#if defined(MAGICKCORE_PNG_DELEGATE)
7255 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7256 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7257#endif
glennrp47b9dd52010-11-24 18:12:06 +00007258
cristy3ed852e2009-09-05 21:47:34 +00007259 entry->magick=(IsImageFormatHandler *) IsPNG;
7260 entry->adjoin=MagickFalse;
7261 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7262 entry->module=ConstantString("PNG");
7263 (void) RegisterMagickInfo(entry);
7264
7265 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007266
cristy3ed852e2009-09-05 21:47:34 +00007267#if defined(JNG_SUPPORTED)
7268#if defined(MAGICKCORE_PNG_DELEGATE)
7269 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7270 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7271#endif
7272#endif
glennrp47b9dd52010-11-24 18:12:06 +00007273
cristy3ed852e2009-09-05 21:47:34 +00007274 entry->magick=(IsImageFormatHandler *) IsJNG;
7275 entry->adjoin=MagickFalse;
7276 entry->description=ConstantString("JPEG Network Graphics");
7277 entry->module=ConstantString("PNG");
7278 entry->note=ConstantString(JNGNote);
7279 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007280
glennrpedaa0382012-04-12 14:16:21 +00007281#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007282 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007283#endif
glennrp47b9dd52010-11-24 18:12:06 +00007284
cristy3ed852e2009-09-05 21:47:34 +00007285 return(MagickImageCoderSignature);
7286}
7287
7288/*
7289%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7290% %
7291% %
7292% %
7293% U n r e g i s t e r P N G I m a g e %
7294% %
7295% %
7296% %
7297%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7298%
7299% UnregisterPNGImage() removes format registrations made by the
7300% PNG module from the list of supported formats.
7301%
7302% The format of the UnregisterPNGImage method is:
7303%
7304% UnregisterPNGImage(void)
7305%
7306*/
7307ModuleExport void UnregisterPNGImage(void)
7308{
7309 (void) UnregisterMagickInfo("MNG");
7310 (void) UnregisterMagickInfo("PNG");
7311 (void) UnregisterMagickInfo("PNG8");
7312 (void) UnregisterMagickInfo("PNG24");
7313 (void) UnregisterMagickInfo("PNG32");
7314 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007315
glennrpedaa0382012-04-12 14:16:21 +00007316#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007317 if (ping_semaphore != (SemaphoreInfo *) NULL)
7318 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007319#endif
7320}
7321
7322#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007323#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007324/*
7325%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7326% %
7327% %
7328% %
7329% W r i t e M N G I m a g e %
7330% %
7331% %
7332% %
7333%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7334%
7335% WriteMNGImage() writes an image in the Portable Network Graphics
7336% Group's "Multiple-image Network Graphics" encoded image format.
7337%
7338% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7339%
7340% The format of the WriteMNGImage method is:
7341%
cristy16ea1392012-03-21 20:38:41 +00007342% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7343% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007344%
7345% A description of each parameter follows.
7346%
7347% o image_info: the image info.
7348%
7349% o image: The image.
7350%
cristy16ea1392012-03-21 20:38:41 +00007351% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007352%
7353% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7354% "To do" under ReadPNGImage):
7355%
cristy3ed852e2009-09-05 21:47:34 +00007356% Preserve all unknown and not-yet-handled known chunks found in input
7357% PNG file and copy them into output PNG files according to the PNG
7358% copying rules.
7359%
7360% Write the iCCP chunk at MNG level when (icc profile length > 0)
7361%
7362% Improve selection of color type (use indexed-colour or indexed-colour
7363% with tRNS when 256 or fewer unique RGBA values are present).
7364%
7365% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7366% This will be complicated if we limit ourselves to generating MNG-LC
7367% files. For now we ignore disposal method 3 and simply overlay the next
7368% image on it.
7369%
7370% Check for identical PLTE's or PLTE/tRNS combinations and use a
7371% global MNG PLTE or PLTE/tRNS combination when appropriate.
7372% [mostly done 15 June 1999 but still need to take care of tRNS]
7373%
7374% Check for identical sRGB and replace with a global sRGB (and remove
7375% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7376% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7377% local gAMA/cHRM with local sRGB if appropriate).
7378%
7379% Check for identical sBIT chunks and write global ones.
7380%
7381% Provide option to skip writing the signature tEXt chunks.
7382%
7383% Use signatures to detect identical objects and reuse the first
7384% instance of such objects instead of writing duplicate objects.
7385%
7386% Use a smaller-than-32k value of compression window size when
7387% appropriate.
7388%
7389% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7390% ancillary text chunks and save profiles.
7391%
7392% Provide an option to force LC files (to ensure exact framing rate)
7393% instead of VLC.
7394%
7395% Provide an option to force VLC files instead of LC, even when offsets
7396% are present. This will involve expanding the embedded images with a
7397% transparent region at the top and/or left.
7398*/
7399
cristy3ed852e2009-09-05 21:47:34 +00007400static void
glennrpcf002022011-01-30 02:38:15 +00007401Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007402 png_info *ping_info, unsigned char *profile_type, unsigned char
7403 *profile_description, unsigned char *profile_data, png_uint_32 length)
7404{
cristy3ed852e2009-09-05 21:47:34 +00007405 png_textp
7406 text;
7407
cristybb503372010-05-27 20:51:26 +00007408 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007409 i;
7410
7411 unsigned char
7412 *sp;
7413
7414 png_charp
7415 dp;
7416
7417 png_uint_32
7418 allocated_length,
7419 description_length;
7420
7421 unsigned char
7422 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007423
cristy3ed852e2009-09-05 21:47:34 +00007424 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7425 return;
7426
7427 if (image_info->verbose)
7428 {
glennrp0fe50b42010-11-16 03:52:51 +00007429 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7430 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007431 }
glennrp0fe50b42010-11-16 03:52:51 +00007432
cristya865ccd2012-07-28 00:33:10 +00007433#if PNG_LIBPNG_VER >= 14000
7434 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7435#else
7436 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7437#endif
cristy3ed852e2009-09-05 21:47:34 +00007438 description_length=(png_uint_32) strlen((const char *) profile_description);
7439 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7440 + description_length);
cristya865ccd2012-07-28 00:33:10 +00007441#if PNG_LIBPNG_VER >= 14000
7442 text[0].text=(png_charp) png_malloc(ping,
7443 (png_alloc_size_t) allocated_length);
7444 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7445#else
7446 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7447 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7448#endif
cristy3ed852e2009-09-05 21:47:34 +00007449 text[0].key[0]='\0';
7450 (void) ConcatenateMagickString(text[0].key,
7451 "Raw profile type ",MaxTextExtent);
7452 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7453 sp=profile_data;
7454 dp=text[0].text;
7455 *dp++='\n';
7456 (void) CopyMagickString(dp,(const char *) profile_description,
7457 allocated_length);
7458 dp+=description_length;
7459 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007460 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007461 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007462 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007463
cristybb503372010-05-27 20:51:26 +00007464 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007465 {
7466 if (i%36 == 0)
7467 *dp++='\n';
7468 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7469 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7470 }
glennrp47b9dd52010-11-24 18:12:06 +00007471
cristy3ed852e2009-09-05 21:47:34 +00007472 *dp++='\n';
7473 *dp='\0';
7474 text[0].text_length=(png_size_t) (dp-text[0].text);
7475 text[0].compression=image_info->compression == NoCompression ||
7476 (image_info->compression == UndefinedCompression &&
7477 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007478
cristy3ed852e2009-09-05 21:47:34 +00007479 if (text[0].text_length <= allocated_length)
7480 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007481
cristy3ed852e2009-09-05 21:47:34 +00007482 png_free(ping,text[0].text);
7483 png_free(ping,text[0].key);
7484 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007485}
7486
glennrpcf002022011-01-30 02:38:15 +00007487static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007488 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007489{
7490 char
7491 *name;
7492
7493 const StringInfo
7494 *profile;
7495
7496 unsigned char
7497 *data;
7498
7499 png_uint_32 length;
7500
7501 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007502
7503 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7504 {
cristy3ed852e2009-09-05 21:47:34 +00007505 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007506
cristy3ed852e2009-09-05 21:47:34 +00007507 if (profile != (const StringInfo *) NULL)
7508 {
7509 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007510 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007511
glennrp47b9dd52010-11-24 18:12:06 +00007512 if (LocaleNCompare(name,string,11) == 0)
7513 {
7514 if (logging != MagickFalse)
7515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7516 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007517
glennrpcf002022011-01-30 02:38:15 +00007518 ping_profile=CloneStringInfo(profile);
7519 data=GetStringInfoDatum(ping_profile),
7520 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007521 data[4]=data[3];
7522 data[3]=data[2];
7523 data[2]=data[1];
7524 data[1]=data[0];
7525 (void) WriteBlobMSBULong(image,length-5); /* data length */
7526 (void) WriteBlob(image,length-1,data+1);
7527 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007528 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007529 }
cristy3ed852e2009-09-05 21:47:34 +00007530 }
glennrp47b9dd52010-11-24 18:12:06 +00007531
cristy3ed852e2009-09-05 21:47:34 +00007532 name=GetNextImageProfile(image);
7533 }
glennrp47b9dd52010-11-24 18:12:06 +00007534
cristy3ed852e2009-09-05 21:47:34 +00007535 return(MagickTrue);
7536}
7537
glennrpb9cfe272010-12-21 15:08:06 +00007538
cristy3ed852e2009-09-05 21:47:34 +00007539/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007540static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +00007541 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007542{
cristy16ea1392012-03-21 20:38:41 +00007543 Image
7544 *image;
7545
7546 ImageInfo
7547 *image_info;
7548
cristy3ed852e2009-09-05 21:47:34 +00007549 char
7550 s[2];
7551
7552 const char
7553 *name,
7554 *property,
7555 *value;
7556
7557 const StringInfo
7558 *profile;
7559
cristy3ed852e2009-09-05 21:47:34 +00007560 int
cristy3ed852e2009-09-05 21:47:34 +00007561 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007562 pass;
7563
glennrpe9c26dc2010-05-30 01:56:35 +00007564 png_byte
7565 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007566
glennrp39992b42010-11-14 00:03:43 +00007567 png_color
7568 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007569
glennrp5af765f2010-03-30 11:12:18 +00007570 png_color_16
7571 ping_background,
7572 ping_trans_color;
7573
cristy3ed852e2009-09-05 21:47:34 +00007574 png_info
7575 *ping_info;
7576
7577 png_struct
7578 *ping;
7579
glennrp5af765f2010-03-30 11:12:18 +00007580 png_uint_32
7581 ping_height,
7582 ping_width;
7583
cristybb503372010-05-27 20:51:26 +00007584 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007585 y;
7586
7587 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007588 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007589 logging,
glennrp58e01762011-01-07 15:28:54 +00007590 matte,
7591
glennrpda8f3a72011-02-27 23:54:12 +00007592 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007593 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007594 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007595 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007596 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007597 ping_have_bKGD,
7598 ping_have_pHYs,
7599 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007600
7601 ping_exclude_bKGD,
7602 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007603 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007604 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007605 ping_exclude_gAMA,
7606 ping_exclude_iCCP,
7607 /* ping_exclude_iTXt, */
7608 ping_exclude_oFFs,
7609 ping_exclude_pHYs,
7610 ping_exclude_sRGB,
7611 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007612 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007613 ping_exclude_vpAg,
7614 ping_exclude_zCCP, /* hex-encoded iCCP */
7615 ping_exclude_zTXt,
7616
glennrp8d3d6e52011-04-19 04:39:51 +00007617 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007618 ping_need_colortype_warning,
7619
glennrp82b3c532011-03-22 19:20:54 +00007620 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007621 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007622 tried_333,
7623 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007624
7625 QuantumInfo
7626 *quantum_info;
7627
cristy16ea1392012-03-21 20:38:41 +00007628 PNGErrorInfo
7629 error_info;
7630
cristybb503372010-05-27 20:51:26 +00007631 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007632 i,
7633 x;
7634
7635 unsigned char
cristy75fc68f2012-10-08 16:26:00 +00007636 *volatile ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007637
glennrp5af765f2010-03-30 11:12:18 +00007638 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007639 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007640 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007641 ping_color_type,
7642 ping_interlace_method,
7643 ping_compression_method,
7644 ping_filter_method,
7645 ping_num_trans;
7646
cristybb503372010-05-27 20:51:26 +00007647 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007648 image_depth,
7649 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007650
cristybb503372010-05-27 20:51:26 +00007651 size_t
cristy3ed852e2009-09-05 21:47:34 +00007652 quality,
7653 rowbytes,
7654 save_image_depth;
7655
glennrpdfd70802010-11-14 01:23:35 +00007656 int
glennrpfd05d622011-02-25 04:10:33 +00007657 j,
glennrpf09bded2011-01-08 01:15:59 +00007658 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007659 number_opaque,
7660 number_semitransparent,
7661 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007662 ping_pHYs_unit_type;
7663
7664 png_uint_32
7665 ping_pHYs_x_resolution,
7666 ping_pHYs_y_resolution;
7667
cristy3ed852e2009-09-05 21:47:34 +00007668 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007669 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007670
cristy16ea1392012-03-21 20:38:41 +00007671 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7672 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7673 if (image_info == (ImageInfo *) NULL)
7674 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007675
glennrp5af765f2010-03-30 11:12:18 +00007676 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007677 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007678 ping_color_type=0,
7679 ping_interlace_method=0,
7680 ping_compression_method=0,
7681 ping_filter_method=0,
7682 ping_num_trans = 0;
7683
7684 ping_background.red = 0;
7685 ping_background.green = 0;
7686 ping_background.blue = 0;
7687 ping_background.gray = 0;
7688 ping_background.index = 0;
7689
7690 ping_trans_color.red=0;
7691 ping_trans_color.green=0;
7692 ping_trans_color.blue=0;
7693 ping_trans_color.gray=0;
7694
glennrpdfd70802010-11-14 01:23:35 +00007695 ping_pHYs_unit_type = 0;
7696 ping_pHYs_x_resolution = 0;
7697 ping_pHYs_y_resolution = 0;
7698
glennrpda8f3a72011-02-27 23:54:12 +00007699 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007700 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007701 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007702 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007703 ping_have_bKGD=MagickFalse;
7704 ping_have_pHYs=MagickFalse;
7705 ping_have_tRNS=MagickFalse;
7706
glennrp0e8ea192010-12-24 18:00:33 +00007707 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7708 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007709 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007710 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007711 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007712 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7713 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7714 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7715 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7716 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7717 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007718 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007719 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7720 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7721 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7722
glennrp8d3d6e52011-04-19 04:39:51 +00007723 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007724 ping_need_colortype_warning = MagickFalse;
7725
cristy0d57eec2011-09-04 22:13:56 +00007726 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7727 * i.e., eliminate the ICC profile and set image->rendering_intent.
7728 * Note that this will not involve any changes to the actual pixels
7729 * but merely passes information to applications that read the resulting
7730 * PNG image.
7731 */
7732 if (ping_exclude_sRGB == MagickFalse)
7733 {
7734 char
7735 *name;
7736
7737 const StringInfo
7738 *profile;
7739
7740 ResetImageProfileIterator(image);
7741 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7742 {
7743 profile=GetImageProfile(image,name);
7744
7745 if (profile != (StringInfo *) NULL)
7746 {
7747 if ((LocaleCompare(name,"ICC") == 0) ||
cristy16ea1392012-03-21 20:38:41 +00007748 (LocaleCompare(name,"ICM") == 0))
7749 {
glennrpee7b4c02011-10-04 01:21:09 +00007750 int
7751 icheck;
7752
7753 /* 0: not a known sRGB profile
7754 * 1: HP-Microsoft sRGB v2
7755 * 2: ICC sRGB v4 perceptual
7756 * 3: ICC sRGB v2 perceptual no black-compensation
7757 */
7758 png_uint_32
7759 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7760 check_len[4] = {0, 3144, 60960, 3052};
7761
7762 png_uint_32
7763 length,
7764 profile_crc;
7765
cristy0d57eec2011-09-04 22:13:56 +00007766 unsigned char
7767 *data;
7768
glennrp29a106e2011-09-06 17:11:42 +00007769 length=(png_uint_32) GetStringInfoLength(profile);
7770
glennrpee7b4c02011-10-04 01:21:09 +00007771 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007772 {
glennrpee7b4c02011-10-04 01:21:09 +00007773 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007774 {
glennrpee7b4c02011-10-04 01:21:09 +00007775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7776 " Got a %lu-byte ICC profile (potentially sRGB)",
7777 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007778
glennrpee7b4c02011-10-04 01:21:09 +00007779 data=GetStringInfoDatum(profile);
7780 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007781
glennrpee7b4c02011-10-04 01:21:09 +00007782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007783 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007784
7785 if (profile_crc == check_crc[icheck])
7786 {
7787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7788 " It is sRGB.");
7789 if (image->rendering_intent==UndefinedIntent)
7790 image->rendering_intent=PerceptualIntent;
7791 break;
7792 }
glennrp29a106e2011-09-06 17:11:42 +00007793 }
glennrp29a106e2011-09-06 17:11:42 +00007794 }
glennrpee7b4c02011-10-04 01:21:09 +00007795 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007797 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007798 (unsigned long) length);
7799 }
cristy0d57eec2011-09-04 22:13:56 +00007800 }
7801 name=GetNextImageProfile(image);
7802 }
7803 }
7804
glennrp8bb3a022010-12-13 20:40:04 +00007805 number_opaque = 0;
7806 number_semitransparent = 0;
7807 number_transparent = 0;
7808
glennrpfd05d622011-02-25 04:10:33 +00007809 if (logging != MagickFalse)
7810 {
7811 if (image->storage_class == UndefinedClass)
7812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7813 " storage_class=UndefinedClass");
7814 if (image->storage_class == DirectClass)
7815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7816 " storage_class=DirectClass");
7817 if (image->storage_class == PseudoClass)
7818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7819 " storage_class=PseudoClass");
7820 }
glennrp28af3712011-04-06 18:07:30 +00007821
glennrp750105b2012-04-25 16:20:45 +00007822 if (image->storage_class == PseudoClass &&
glennrp7e65e932011-08-19 02:31:16 +00007823 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7824 (mng_info->write_png_colortype != 0 &&
7825 mng_info->write_png_colortype != 4)))
7826 {
cristy16ea1392012-03-21 20:38:41 +00007827 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007828 image->storage_class = DirectClass;
7829 }
7830
glennrpc6c391a2011-04-27 02:23:56 +00007831 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007832 {
glennrpc6c391a2011-04-27 02:23:56 +00007833 if (image->storage_class != PseudoClass && image->colormap != NULL)
7834 {
7835 /* Free the bogus colormap; it can cause trouble later */
7836 if (logging != MagickFalse)
7837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7838 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007839 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007840 image->colormap=NULL;
7841 }
glennrp28af3712011-04-06 18:07:30 +00007842 }
glennrpbb4f99d2011-05-22 11:13:17 +00007843
cristy3d9f5ba2012-06-26 13:37:31 +00007844 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00007845 (void) TransformImageColorspace(image,sRGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007846
glennrp3241bd02010-12-12 04:36:28 +00007847 /*
7848 Sometimes we get PseudoClass images whose RGB values don't match
7849 the colors in the colormap. This code syncs the RGB values.
7850 */
7851 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristy16ea1392012-03-21 20:38:41 +00007852 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007853
glennrpa6a06632011-01-19 15:15:34 +00007854#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7855 if (image->depth > 8)
7856 {
7857 if (logging != MagickFalse)
7858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7859 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7860
7861 image->depth=8;
7862 }
7863#endif
7864
glennrp8e58efd2011-05-20 12:16:29 +00007865 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007866 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7867 {
cristy16ea1392012-03-21 20:38:41 +00007868 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007869 *r;
7870
glennrp8e58efd2011-05-20 12:16:29 +00007871 if (image->depth > 8)
7872 {
7873#if MAGICKCORE_QUANTUM_DEPTH > 16
7874 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007875 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007876
7877 for (y=0; y < (ssize_t) image->rows; y++)
7878 {
cristy16ea1392012-03-21 20:38:41 +00007879 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007880
cristy16ea1392012-03-21 20:38:41 +00007881 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007882 break;
7883
7884 for (x=0; x < (ssize_t) image->columns; x++)
7885 {
cristy16ea1392012-03-21 20:38:41 +00007886 LBR16PixelRGBA(r);
7887 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007888 }
glennrpbb4f99d2011-05-22 11:13:17 +00007889
glennrp8e58efd2011-05-20 12:16:29 +00007890 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7891 break;
7892 }
7893
7894 if (image->storage_class == PseudoClass && image->colormap != NULL)
7895 {
cristy3e08f112011-05-24 13:19:30 +00007896 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007897 {
glennrp91d99252011-06-25 14:30:13 +00007898 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007899 }
7900 }
7901#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7902 }
7903
7904 else if (image->depth > 4)
7905 {
7906#if MAGICKCORE_QUANTUM_DEPTH > 8
7907 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007908 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007909
7910 for (y=0; y < (ssize_t) image->rows; y++)
7911 {
cristy16ea1392012-03-21 20:38:41 +00007912 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007913
cristy16ea1392012-03-21 20:38:41 +00007914 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007915 break;
7916
7917 for (x=0; x < (ssize_t) image->columns; x++)
7918 {
cristy16ea1392012-03-21 20:38:41 +00007919 LBR08PixelRGBA(r);
7920 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007921 }
glennrpbb4f99d2011-05-22 11:13:17 +00007922
glennrp8e58efd2011-05-20 12:16:29 +00007923 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7924 break;
7925 }
7926
7927 if (image->storage_class == PseudoClass && image->colormap != NULL)
7928 {
cristy3e08f112011-05-24 13:19:30 +00007929 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007930 {
glennrp91d99252011-06-25 14:30:13 +00007931 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007932 }
7933 }
7934#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7935 }
7936 else
7937 if (image->depth > 2)
7938 {
7939 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007940 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007941
7942 for (y=0; y < (ssize_t) image->rows; y++)
7943 {
cristy16ea1392012-03-21 20:38:41 +00007944 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007945
cristy16ea1392012-03-21 20:38:41 +00007946 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007947 break;
7948
7949 for (x=0; x < (ssize_t) image->columns; x++)
7950 {
cristy16ea1392012-03-21 20:38:41 +00007951 LBR04PixelRGBA(r);
7952 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007953 }
glennrpbb4f99d2011-05-22 11:13:17 +00007954
glennrp8e58efd2011-05-20 12:16:29 +00007955 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7956 break;
7957 }
7958
7959 if (image->storage_class == PseudoClass && image->colormap != NULL)
7960 {
cristy3e08f112011-05-24 13:19:30 +00007961 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007962 {
glennrp91d99252011-06-25 14:30:13 +00007963 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007964 }
7965 }
7966 }
7967
7968 else if (image->depth > 1)
7969 {
7970 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007971 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007972
7973 for (y=0; y < (ssize_t) image->rows; y++)
7974 {
cristy16ea1392012-03-21 20:38:41 +00007975 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007976
cristy16ea1392012-03-21 20:38:41 +00007977 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007978 break;
7979
7980 for (x=0; x < (ssize_t) image->columns; x++)
7981 {
cristy16ea1392012-03-21 20:38:41 +00007982 LBR02PixelRGBA(r);
7983 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007984 }
glennrpbb4f99d2011-05-22 11:13:17 +00007985
glennrp8e58efd2011-05-20 12:16:29 +00007986 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7987 break;
7988 }
7989
7990 if (image->storage_class == PseudoClass && image->colormap != NULL)
7991 {
cristy3e08f112011-05-24 13:19:30 +00007992 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007993 {
glennrp91d99252011-06-25 14:30:13 +00007994 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007995 }
7996 }
7997 }
7998 else
7999 {
8000 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00008001 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008002
8003 for (y=0; y < (ssize_t) image->rows; y++)
8004 {
cristy16ea1392012-03-21 20:38:41 +00008005 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008006
cristy16ea1392012-03-21 20:38:41 +00008007 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008008 break;
8009
8010 for (x=0; x < (ssize_t) image->columns; x++)
8011 {
cristy16ea1392012-03-21 20:38:41 +00008012 LBR01PixelRGBA(r);
8013 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008014 }
glennrpbb4f99d2011-05-22 11:13:17 +00008015
glennrp8e58efd2011-05-20 12:16:29 +00008016 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8017 break;
8018 }
8019
8020 if (image->storage_class == PseudoClass && image->colormap != NULL)
8021 {
cristy3e08f112011-05-24 13:19:30 +00008022 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008023 {
glennrp91d99252011-06-25 14:30:13 +00008024 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008025 }
8026 }
8027 }
glennrp9d0ea4d2011-04-22 18:35:57 +00008028 }
8029
glennrp67b9c1a2011-04-22 18:47:36 +00008030 /* To do: set to next higher multiple of 8 */
8031 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00008032 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00008033
glennrp2b013e42010-11-24 16:55:50 +00008034#if (MAGICKCORE_QUANTUM_DEPTH > 16)
8035 /* PNG does not handle depths greater than 16 so reduce it even
8036 * if lossy
8037 */
glennrp8e58efd2011-05-20 12:16:29 +00008038 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00008039 image->depth=16;
8040#endif
8041
glennrp3faa9a32011-04-23 14:00:25 +00008042#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00008043 if (image->depth > 8)
8044 {
8045 /* To do: fill low byte properly */
8046 image->depth=16;
8047 }
8048
glennrpc722dd82011-02-24 05:13:21 +00008049 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristy16ea1392012-03-21 20:38:41 +00008050 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00008051 image->depth = 8;
8052#endif
8053
glennrpa8036d62012-11-04 01:46:06 +00008054 if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8055 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8056 mng_info->write_png_colortype < 4 &&
8057 image->alpha_trait != BlendPixelTrait)))
8058 {
8059 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8060 * are not going to need the result.
8061 */
8062 image_colors=image->colors;
8063 number_opaque = image->colors;
8064 if (mng_info->write_png_colortype == 1 ||
8065 mng_info->write_png_colortype == 5)
8066 ping_have_color=MagickFalse;
8067 else
8068 ping_have_color=MagickTrue;
8069 ping_have_non_bw=MagickFalse;
8070
8071 if (image->alpha_trait == BlendPixelTrait)
8072 {
8073 number_transparent = 2;
8074 number_semitransparent = 1;
8075 }
8076
8077 else
8078 {
8079 number_transparent = 0;
8080 number_semitransparent = 0;
8081 }
8082 }
8083
8084 else
8085 {
8086 /* BUILD_PALETTE
8087 *
8088 * Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00008089 * we reduce the transparency to binary and run again, then if there
8090 * 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 +00008091 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8092 * palette. Then (To do) we take care of a final reduction that is only
8093 * needed if there are still 256 colors present and one of them has both
8094 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00008095 */
glennrp82b3c532011-03-22 19:20:54 +00008096
glennrp8ca51ad2011-05-12 21:22:32 +00008097 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008098 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00008099 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008100
glennrp8ca51ad2011-05-12 21:22:32 +00008101 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00008102 {
glennrpa8036d62012-11-04 01:46:06 +00008103 /*
glennrpd71e86a2011-02-24 01:28:37 +00008104 * Sometimes we get DirectClass images that have 256 colors or fewer.
8105 * This code will build a colormap.
8106 *
8107 * Also, sometimes we get PseudoClass images with an out-of-date
8108 * colormap. This code will replace the colormap with a new one.
8109 * Sometimes we get PseudoClass images that have more than 256 colors.
8110 * This code will delete the colormap and change the image to
8111 * DirectClass.
8112 *
cristy8a46d822012-08-28 23:32:39 +00008113 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008114 * even though it sometimes contains left-over non-opaque values.
8115 *
8116 * Also we gather some information (number of opaque, transparent,
8117 * and semitransparent pixels, and whether the image has any non-gray
8118 * pixels or only black-and-white pixels) that we might need later.
8119 *
8120 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8121 * we need to check for bogus non-opaque values, at least.
8122 */
glennrp3c218112010-11-27 15:31:26 +00008123
glennrpd71e86a2011-02-24 01:28:37 +00008124 int
8125 n;
glennrp3c218112010-11-27 15:31:26 +00008126
cristy16ea1392012-03-21 20:38:41 +00008127 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008128 opaque[260],
8129 semitransparent[260],
8130 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008131
cristy16ea1392012-03-21 20:38:41 +00008132 register const Quantum
8133 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008134
cristy16ea1392012-03-21 20:38:41 +00008135 register Quantum
8136 *q,
glennrpfd05d622011-02-25 04:10:33 +00008137 *r;
8138
glennrpd71e86a2011-02-24 01:28:37 +00008139 if (logging != MagickFalse)
8140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8141 " Enter BUILD_PALETTE:");
8142
8143 if (logging != MagickFalse)
8144 {
glennrp03812ae2010-12-24 01:31:34 +00008145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008146 " image->columns=%.20g",(double) image->columns);
8147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8148 " image->rows=%.20g",(double) image->rows);
8149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00008150 " image->alpha_trait=%.20g",(double) image->alpha_trait);
glennrpd71e86a2011-02-24 01:28:37 +00008151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8152 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008153
glennrpfd05d622011-02-25 04:10:33 +00008154 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008155 {
8156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008157 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008159 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008160
glennrpd71e86a2011-02-24 01:28:37 +00008161 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008162 {
glennrpd71e86a2011-02-24 01:28:37 +00008163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8164 " %d (%d,%d,%d,%d)",
8165 (int) i,
8166 (int) image->colormap[i].red,
8167 (int) image->colormap[i].green,
8168 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008169 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008170 }
glennrp2cc891a2010-12-24 13:44:32 +00008171
glennrpd71e86a2011-02-24 01:28:37 +00008172 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8173 {
8174 if (i > 255)
8175 {
8176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8177 " %d (%d,%d,%d,%d)",
8178 (int) i,
8179 (int) image->colormap[i].red,
8180 (int) image->colormap[i].green,
8181 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008182 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008183 }
8184 }
glennrp03812ae2010-12-24 01:31:34 +00008185 }
glennrp7ddcc222010-12-11 05:01:05 +00008186
glennrpd71e86a2011-02-24 01:28:37 +00008187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8188 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008189
glennrpd71e86a2011-02-24 01:28:37 +00008190 if (image->colors == 0)
cristy16ea1392012-03-21 20:38:41 +00008191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008193
glennrp8d3d6e52011-04-19 04:39:51 +00008194 if (ping_preserve_colormap == MagickFalse)
8195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8196 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008197 }
8198
glennrpd71e86a2011-02-24 01:28:37 +00008199 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008200 number_opaque = 0;
8201 number_semitransparent = 0;
8202 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008203
8204 for (y=0; y < (ssize_t) image->rows; y++)
8205 {
8206 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8207
cristy16ea1392012-03-21 20:38:41 +00008208 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008209 break;
8210
8211 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008212 {
cristy8a46d822012-08-28 23:32:39 +00008213 if (image->alpha_trait != BlendPixelTrait ||
cristy16ea1392012-03-21 20:38:41 +00008214 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008215 {
8216 if (number_opaque < 259)
8217 {
8218 if (number_opaque == 0)
8219 {
cristy16ea1392012-03-21 20:38:41 +00008220 GetPixelInfoPixel(image, q, opaque);
8221 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008222 number_opaque=1;
8223 }
glennrp2cc891a2010-12-24 13:44:32 +00008224
glennrpd71e86a2011-02-24 01:28:37 +00008225 for (i=0; i< (ssize_t) number_opaque; i++)
8226 {
cristy16ea1392012-03-21 20:38:41 +00008227 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008228 break;
8229 }
glennrp7ddcc222010-12-11 05:01:05 +00008230
cristy16ea1392012-03-21 20:38:41 +00008231 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008232 {
8233 number_opaque++;
cristy16ea1392012-03-21 20:38:41 +00008234 GetPixelInfoPixel(image, q, opaque+i);
8235 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008236 }
8237 }
8238 }
cristy16ea1392012-03-21 20:38:41 +00008239 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008240 {
8241 if (number_transparent < 259)
8242 {
8243 if (number_transparent == 0)
8244 {
cristy16ea1392012-03-21 20:38:41 +00008245 GetPixelInfoPixel(image, q, transparent);
8246 ping_trans_color.red=(unsigned short)
8247 GetPixelRed(image,q);
8248 ping_trans_color.green=(unsigned short)
8249 GetPixelGreen(image,q);
8250 ping_trans_color.blue=(unsigned short)
8251 GetPixelBlue(image,q);
8252 ping_trans_color.gray=(unsigned short)
cristy972d1c42012-07-14 23:29:14 +00008253 GetPixelGray(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008254 number_transparent = 1;
8255 }
8256
8257 for (i=0; i< (ssize_t) number_transparent; i++)
8258 {
cristy16ea1392012-03-21 20:38:41 +00008259 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008260 break;
8261 }
8262
8263 if (i == (ssize_t) number_transparent &&
8264 number_transparent < 259)
8265 {
8266 number_transparent++;
cristy16ea1392012-03-21 20:38:41 +00008267 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008268 }
8269 }
8270 }
8271 else
8272 {
8273 if (number_semitransparent < 259)
8274 {
8275 if (number_semitransparent == 0)
8276 {
cristy16ea1392012-03-21 20:38:41 +00008277 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008278 number_semitransparent = 1;
8279 }
8280
8281 for (i=0; i< (ssize_t) number_semitransparent; i++)
8282 {
cristy16ea1392012-03-21 20:38:41 +00008283 if (IsPixelEquivalent(image,q, semitransparent+i)
8284 && GetPixelAlpha(image,q) ==
8285 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008286 break;
8287 }
8288
8289 if (i == (ssize_t) number_semitransparent &&
8290 number_semitransparent < 259)
8291 {
8292 number_semitransparent++;
cristy16ea1392012-03-21 20:38:41 +00008293 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008294 }
8295 }
8296 }
cristy16ea1392012-03-21 20:38:41 +00008297 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008298 }
8299 }
8300
cristy4054bfb2011-08-29 23:41:39 +00008301 if (mng_info->write_png8 == MagickFalse &&
8302 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008303 {
8304 /* Add the background color to the palette, if it
8305 * isn't already there.
8306 */
glennrpc6c391a2011-04-27 02:23:56 +00008307 if (logging != MagickFalse)
8308 {
8309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8310 " Check colormap for background (%d,%d,%d)",
8311 (int) image->background_color.red,
8312 (int) image->background_color.green,
8313 (int) image->background_color.blue);
8314 }
glennrpd71e86a2011-02-24 01:28:37 +00008315 for (i=0; i<number_opaque; i++)
8316 {
glennrpca7ad3a2011-04-26 04:44:54 +00008317 if (opaque[i].red == image->background_color.red &&
8318 opaque[i].green == image->background_color.green &&
8319 opaque[i].blue == image->background_color.blue)
8320 break;
glennrpd71e86a2011-02-24 01:28:37 +00008321 }
glennrpd71e86a2011-02-24 01:28:37 +00008322 if (number_opaque < 259 && i == number_opaque)
8323 {
glennrp8e045c82011-04-27 16:40:27 +00008324 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008325 ping_background.index = i;
glennrp388a8c82012-06-27 13:41:51 +00008326 number_opaque++;
glennrpc6c391a2011-04-27 02:23:56 +00008327 if (logging != MagickFalse)
8328 {
8329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8330 " background_color index is %d",(int) i);
8331 }
8332
glennrpd71e86a2011-02-24 01:28:37 +00008333 }
glennrpa080bc32011-03-11 18:03:44 +00008334 else if (logging != MagickFalse)
8335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8336 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008337 }
8338
8339 image_colors=number_opaque+number_transparent+number_semitransparent;
8340
glennrpa080bc32011-03-11 18:03:44 +00008341 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8342 {
8343 /* No room for the background color; remove it. */
8344 number_opaque--;
8345 image_colors--;
8346 }
8347
glennrpd71e86a2011-02-24 01:28:37 +00008348 if (logging != MagickFalse)
8349 {
8350 if (image_colors > 256)
8351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8352 " image has more than 256 colors");
8353
8354 else
8355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8356 " image has %d colors",image_colors);
8357 }
8358
glennrp8d3d6e52011-04-19 04:39:51 +00008359 if (ping_preserve_colormap != MagickFalse)
8360 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008361
glennrpfd05d622011-02-25 04:10:33 +00008362 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008363 {
8364 ping_have_color=MagickFalse;
8365 ping_have_non_bw=MagickFalse;
8366
glennrp98b95772012-11-29 01:32:00 +00008367 if ((IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) ||
8368 (IssRGBColorspace(image->colorspace) != MagickFalse))
glennrp0fa25802012-07-20 14:01:06 +00008369 {
8370 ping_have_color=MagickTrue;
glennrp98b95772012-11-29 01:32:00 +00008371 ping_have_non_bw=MagickTrue;
glennrp0fa25802012-07-20 14:01:06 +00008372 }
8373
glennrpd71e86a2011-02-24 01:28:37 +00008374 if(image_colors > 256)
8375 {
8376 for (y=0; y < (ssize_t) image->rows; y++)
8377 {
8378 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8379
cristy16ea1392012-03-21 20:38:41 +00008380 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008381 break;
8382
glennrpe5e6b802011-07-20 14:44:40 +00008383 s=q;
8384 for (x=0; x < (ssize_t) image->columns; x++)
8385 {
cristy16ea1392012-03-21 20:38:41 +00008386 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8387 GetPixelRed(image,s) != GetPixelBlue(image,s))
glennrpe5e6b802011-07-20 14:44:40 +00008388 {
8389 ping_have_color=MagickTrue;
8390 ping_have_non_bw=MagickTrue;
8391 break;
8392 }
cristy16ea1392012-03-21 20:38:41 +00008393 s+=GetPixelChannels(image);
glennrpe5e6b802011-07-20 14:44:40 +00008394 }
8395
8396 if (ping_have_color != MagickFalse)
8397 break;
8398
glennrpd71e86a2011-02-24 01:28:37 +00008399 /* Worst case is black-and-white; we are looking at every
8400 * pixel twice.
8401 */
8402
glennrpd71e86a2011-02-24 01:28:37 +00008403 if (ping_have_non_bw == MagickFalse)
8404 {
8405 s=q;
8406 for (x=0; x < (ssize_t) image->columns; x++)
8407 {
cristy16ea1392012-03-21 20:38:41 +00008408 if (GetPixelRed(image,s) != 0 &&
8409 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008410 {
8411 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008412 break;
glennrpd71e86a2011-02-24 01:28:37 +00008413 }
cristy16ea1392012-03-21 20:38:41 +00008414 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008415 }
glennrpe5e6b802011-07-20 14:44:40 +00008416 }
glennrpd71e86a2011-02-24 01:28:37 +00008417 }
glennrpbb4f99d2011-05-22 11:13:17 +00008418 }
8419 }
glennrpd71e86a2011-02-24 01:28:37 +00008420
8421 if (image_colors < 257)
8422 {
cristy16ea1392012-03-21 20:38:41 +00008423 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008424 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008425
glennrpd71e86a2011-02-24 01:28:37 +00008426 /*
8427 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008428 */
8429
glennrpd71e86a2011-02-24 01:28:37 +00008430 if (logging != MagickFalse)
8431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8432 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008433
glennrpd71e86a2011-02-24 01:28:37 +00008434 /* Sort palette, transparent first */;
8435
8436 n = 0;
8437
8438 for (i=0; i<number_transparent; i++)
8439 colormap[n++] = transparent[i];
8440
8441 for (i=0; i<number_semitransparent; i++)
8442 colormap[n++] = semitransparent[i];
8443
8444 for (i=0; i<number_opaque; i++)
8445 colormap[n++] = opaque[i];
8446
glennrpc6c391a2011-04-27 02:23:56 +00008447 ping_background.index +=
8448 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008449
glennrpd71e86a2011-02-24 01:28:37 +00008450 /* image_colors < 257; search the colormap instead of the pixels
8451 * to get ping_have_color and ping_have_non_bw
8452 */
8453 for (i=0; i<n; i++)
8454 {
8455 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008456 {
glennrpd71e86a2011-02-24 01:28:37 +00008457 if (colormap[i].red != colormap[i].green ||
8458 colormap[i].red != colormap[i].blue)
8459 {
8460 ping_have_color=MagickTrue;
8461 ping_have_non_bw=MagickTrue;
8462 break;
8463 }
8464 }
8465
8466 if (ping_have_non_bw == MagickFalse)
8467 {
8468 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008469 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008470 }
glennrp8bb3a022010-12-13 20:40:04 +00008471 }
8472
glennrpd71e86a2011-02-24 01:28:37 +00008473 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8474 (number_transparent == 0 && number_semitransparent == 0)) &&
8475 (((mng_info->write_png_colortype-1) ==
8476 PNG_COLOR_TYPE_PALETTE) ||
8477 (mng_info->write_png_colortype == 0)))
8478 {
glennrp6185c532011-01-14 17:58:40 +00008479 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008480 {
glennrpd71e86a2011-02-24 01:28:37 +00008481 if (n != (ssize_t) image_colors)
8482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8483 " image_colors (%d) and n (%d) don't match",
8484 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008485
glennrpd71e86a2011-02-24 01:28:37 +00008486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8487 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008488 }
glennrp03812ae2010-12-24 01:31:34 +00008489
glennrpd71e86a2011-02-24 01:28:37 +00008490 image->colors = image_colors;
8491
cristy16ea1392012-03-21 20:38:41 +00008492 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008493 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008494 ThrowWriterException(ResourceLimitError,
8495 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008496
8497 for (i=0; i< (ssize_t) image_colors; i++)
8498 image->colormap[i] = colormap[i];
8499
8500 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008501 {
glennrpd71e86a2011-02-24 01:28:37 +00008502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8503 " image->colors=%d (%d)",
8504 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008505
glennrpd71e86a2011-02-24 01:28:37 +00008506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8507 " Update the pixel indexes");
8508 }
glennrp6185c532011-01-14 17:58:40 +00008509
glennrpfd05d622011-02-25 04:10:33 +00008510 /* Sync the pixel indices with the new colormap */
8511
glennrpd71e86a2011-02-24 01:28:37 +00008512 for (y=0; y < (ssize_t) image->rows; y++)
8513 {
cristy16ea1392012-03-21 20:38:41 +00008514 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008515
cristy16ea1392012-03-21 20:38:41 +00008516 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008517 break;
glennrp6185c532011-01-14 17:58:40 +00008518
glennrpd71e86a2011-02-24 01:28:37 +00008519 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008520 {
glennrpd71e86a2011-02-24 01:28:37 +00008521 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008522 {
cristy8a46d822012-08-28 23:32:39 +00008523 if ((image->alpha_trait != BlendPixelTrait ||
cristy16ea1392012-03-21 20:38:41 +00008524 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8525 image->colormap[i].red == GetPixelRed(image,q) &&
8526 image->colormap[i].green == GetPixelGreen(image,q) &&
8527 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008528 {
cristy16ea1392012-03-21 20:38:41 +00008529 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008530 break;
glennrp6185c532011-01-14 17:58:40 +00008531 }
glennrp6185c532011-01-14 17:58:40 +00008532 }
cristy16ea1392012-03-21 20:38:41 +00008533 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008534 }
glennrp6185c532011-01-14 17:58:40 +00008535
glennrpd71e86a2011-02-24 01:28:37 +00008536 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8537 break;
8538 }
8539 }
8540 }
8541
8542 if (logging != MagickFalse)
8543 {
8544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8545 " image->colors=%d", (int) image->colors);
8546
8547 if (image->colormap != NULL)
8548 {
8549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008550 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008551
8552 for (i=0; i < (ssize_t) image->colors; i++)
8553 {
cristy72988482011-03-29 16:34:38 +00008554 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008555 {
8556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8557 " %d (%d,%d,%d,%d)",
8558 (int) i,
8559 (int) image->colormap[i].red,
8560 (int) image->colormap[i].green,
8561 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008562 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008563 }
glennrp6185c532011-01-14 17:58:40 +00008564 }
8565 }
glennrp03812ae2010-12-24 01:31:34 +00008566
glennrpd71e86a2011-02-24 01:28:37 +00008567 if (number_transparent < 257)
8568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8569 " number_transparent = %d",
8570 number_transparent);
8571 else
glennrp03812ae2010-12-24 01:31:34 +00008572
glennrpd71e86a2011-02-24 01:28:37 +00008573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8574 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008575
glennrpd71e86a2011-02-24 01:28:37 +00008576 if (number_opaque < 257)
8577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8578 " number_opaque = %d",
8579 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008580
glennrpd71e86a2011-02-24 01:28:37 +00008581 else
8582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8583 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008584
glennrpd71e86a2011-02-24 01:28:37 +00008585 if (number_semitransparent < 257)
8586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8587 " number_semitransparent = %d",
8588 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008589
glennrpd71e86a2011-02-24 01:28:37 +00008590 else
8591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8592 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008593
glennrpd71e86a2011-02-24 01:28:37 +00008594 if (ping_have_non_bw == MagickFalse)
8595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8596 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008597
glennrpd71e86a2011-02-24 01:28:37 +00008598 else if (ping_have_color == MagickFalse)
8599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8600 " All pixels and the background are gray");
8601
8602 else
8603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8604 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008605
glennrp03812ae2010-12-24 01:31:34 +00008606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8607 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008608 }
glennrpfd05d622011-02-25 04:10:33 +00008609
glennrpc8c2f062011-02-25 19:00:33 +00008610 if (mng_info->write_png8 == MagickFalse)
8611 break;
glennrpfd05d622011-02-25 04:10:33 +00008612
glennrpc8c2f062011-02-25 19:00:33 +00008613 /* Make any reductions necessary for the PNG8 format */
8614 if (image_colors <= 256 &&
8615 image_colors != 0 && image->colormap != NULL &&
8616 number_semitransparent == 0 &&
8617 number_transparent <= 1)
8618 break;
8619
8620 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008621 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8622 * transparent color so if more than one is transparent we merge
8623 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008624 */
glennrp130fc452011-08-20 03:43:18 +00008625 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008626 {
8627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8628 " Thresholding the alpha channel to binary");
8629
8630 for (y=0; y < (ssize_t) image->rows; y++)
8631 {
cristy16ea1392012-03-21 20:38:41 +00008632 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008633
cristy16ea1392012-03-21 20:38:41 +00008634 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008635 break;
8636
8637 for (x=0; x < (ssize_t) image->columns; x++)
8638 {
cristy16ea1392012-03-21 20:38:41 +00008639 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008640 {
cristy16ea1392012-03-21 20:38:41 +00008641 SetPixelInfoPixel(image,&image->background_color,r);
8642 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008643 }
8644 else
cristy16ea1392012-03-21 20:38:41 +00008645 SetPixelAlpha(image,OpaqueAlpha,r);
8646 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008647 }
glennrpbb4f99d2011-05-22 11:13:17 +00008648
glennrpc8c2f062011-02-25 19:00:33 +00008649 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8650 break;
8651
8652 if (image_colors != 0 && image_colors <= 256 &&
8653 image->colormap != NULL)
8654 for (i=0; i<image_colors; i++)
cristy16ea1392012-03-21 20:38:41 +00008655 image->colormap[i].alpha =
8656 (image->colormap[i].alpha > TransparentAlpha/2 ?
8657 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008658 }
8659 continue;
8660 }
8661
8662 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008663 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8664 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8665 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008666 */
glennrpd3371642011-03-22 19:42:23 +00008667 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8668 {
8669 if (logging != MagickFalse)
8670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8671 " Quantizing the background color to 4-4-4");
8672
8673 tried_444 = MagickTrue;
8674
glennrp91d99252011-06-25 14:30:13 +00008675 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008676
8677 if (logging != MagickFalse)
8678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8679 " Quantizing the pixel colors to 4-4-4");
8680
8681 if (image->colormap == NULL)
8682 {
8683 for (y=0; y < (ssize_t) image->rows; y++)
8684 {
cristy16ea1392012-03-21 20:38:41 +00008685 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00008686
cristy16ea1392012-03-21 20:38:41 +00008687 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008688 break;
8689
8690 for (x=0; x < (ssize_t) image->columns; x++)
8691 {
cristy16ea1392012-03-21 20:38:41 +00008692 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008693 LBR04PixelRGB(r);
cristy16ea1392012-03-21 20:38:41 +00008694 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00008695 }
glennrpbb4f99d2011-05-22 11:13:17 +00008696
glennrpd3371642011-03-22 19:42:23 +00008697 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8698 break;
8699 }
8700 }
8701
8702 else /* Should not reach this; colormap already exists and
8703 must be <= 256 */
8704 {
8705 if (logging != MagickFalse)
8706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8707 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008708
glennrpd3371642011-03-22 19:42:23 +00008709 for (i=0; i<image_colors; i++)
8710 {
glennrp91d99252011-06-25 14:30:13 +00008711 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008712 }
8713 }
8714 continue;
8715 }
8716
glennrp82b3c532011-03-22 19:20:54 +00008717 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8718 {
8719 if (logging != MagickFalse)
8720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8721 " Quantizing the background color to 3-3-3");
8722
8723 tried_333 = MagickTrue;
8724
glennrp91d99252011-06-25 14:30:13 +00008725 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008726
8727 if (logging != MagickFalse)
8728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008729 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008730
8731 if (image->colormap == NULL)
8732 {
8733 for (y=0; y < (ssize_t) image->rows; y++)
8734 {
cristy16ea1392012-03-21 20:38:41 +00008735 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00008736
cristy16ea1392012-03-21 20:38:41 +00008737 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008738 break;
8739
8740 for (x=0; x < (ssize_t) image->columns; x++)
8741 {
cristy16ea1392012-03-21 20:38:41 +00008742 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8743 LBR03RGB(r);
8744 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00008745 }
glennrpbb4f99d2011-05-22 11:13:17 +00008746
glennrp82b3c532011-03-22 19:20:54 +00008747 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8748 break;
8749 }
8750 }
8751
8752 else /* Should not reach this; colormap already exists and
8753 must be <= 256 */
8754 {
8755 if (logging != MagickFalse)
8756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008757 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008758 for (i=0; i<image_colors; i++)
8759 {
glennrp91d99252011-06-25 14:30:13 +00008760 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008761 }
glennrpd3371642011-03-22 19:42:23 +00008762 }
8763 continue;
glennrp82b3c532011-03-22 19:20:54 +00008764 }
glennrpc8c2f062011-02-25 19:00:33 +00008765
glennrp8ca51ad2011-05-12 21:22:32 +00008766 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008767 {
8768 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008770 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008771
glennrp8ca51ad2011-05-12 21:22:32 +00008772 tried_332 = MagickTrue;
8773
glennrp3faa9a32011-04-23 14:00:25 +00008774 /* Red and green were already done so we only quantize the blue
8775 * channel
8776 */
8777
glennrp91d99252011-06-25 14:30:13 +00008778 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008779
glennrpc8c2f062011-02-25 19:00:33 +00008780 if (logging != MagickFalse)
8781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008782 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008783
glennrpc8c2f062011-02-25 19:00:33 +00008784 if (image->colormap == NULL)
8785 {
8786 for (y=0; y < (ssize_t) image->rows; y++)
8787 {
cristy16ea1392012-03-21 20:38:41 +00008788 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008789
cristy16ea1392012-03-21 20:38:41 +00008790 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008791 break;
8792
8793 for (x=0; x < (ssize_t) image->columns; x++)
8794 {
cristy16ea1392012-03-21 20:38:41 +00008795 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008796 LBR02PixelBlue(r);
cristy16ea1392012-03-21 20:38:41 +00008797 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008798 }
glennrpbb4f99d2011-05-22 11:13:17 +00008799
glennrpc8c2f062011-02-25 19:00:33 +00008800 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8801 break;
8802 }
8803 }
glennrpfd05d622011-02-25 04:10:33 +00008804
glennrpc8c2f062011-02-25 19:00:33 +00008805 else /* Should not reach this; colormap already exists and
8806 must be <= 256 */
8807 {
8808 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008810 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008811 for (i=0; i<image_colors; i++)
8812 {
glennrp91d99252011-06-25 14:30:13 +00008813 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008814 }
8815 }
8816 continue;
8817 }
8818 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008819
8820 if (image_colors == 0 || image_colors > 256)
8821 {
8822 /* Take care of special case with 256 colors + 1 transparent
8823 * color. We don't need to quantize to 2-3-2-1; we only need to
8824 * eliminate one color, so we'll merge the two darkest red
8825 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8826 */
8827 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8828 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8829 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8830 {
8831 image->background_color.red=ScaleCharToQuantum(0x24);
8832 }
glennrpbb4f99d2011-05-22 11:13:17 +00008833
glennrp8ca51ad2011-05-12 21:22:32 +00008834 if (image->colormap == NULL)
8835 {
8836 for (y=0; y < (ssize_t) image->rows; y++)
8837 {
cristy16ea1392012-03-21 20:38:41 +00008838 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008839
cristy16ea1392012-03-21 20:38:41 +00008840 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008841 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008842
glennrp8ca51ad2011-05-12 21:22:32 +00008843 for (x=0; x < (ssize_t) image->columns; x++)
8844 {
cristy16ea1392012-03-21 20:38:41 +00008845 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8846 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8847 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8848 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008849 {
cristy16ea1392012-03-21 20:38:41 +00008850 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008851 }
cristy16ea1392012-03-21 20:38:41 +00008852 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008853 }
glennrpbb4f99d2011-05-22 11:13:17 +00008854
glennrp8ca51ad2011-05-12 21:22:32 +00008855 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8856 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008857
glennrp8ca51ad2011-05-12 21:22:32 +00008858 }
8859 }
8860
8861 else
8862 {
8863 for (i=0; i<image_colors; i++)
8864 {
8865 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8866 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8867 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8868 {
8869 image->colormap[i].red=ScaleCharToQuantum(0x24);
8870 }
8871 }
8872 }
8873 }
glennrpd71e86a2011-02-24 01:28:37 +00008874 }
glennrpa8036d62012-11-04 01:46:06 +00008875 }
glennrpfd05d622011-02-25 04:10:33 +00008876 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008877
glennrpfd05d622011-02-25 04:10:33 +00008878 /* If we are excluding the tRNS chunk and there is transparency,
8879 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8880 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008881 */
glennrp0e8ea192010-12-24 18:00:33 +00008882 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8883 (number_transparent != 0 || number_semitransparent != 0))
8884 {
glennrpd17915c2011-04-29 14:24:22 +00008885 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008886
8887 if (ping_have_color == MagickFalse)
8888 mng_info->write_png_colortype = 5;
8889
8890 else
8891 mng_info->write_png_colortype = 7;
8892
glennrp8d579662011-02-23 02:05:02 +00008893 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008894 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008895 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008896
glennrp0e8ea192010-12-24 18:00:33 +00008897 }
8898
glennrpfd05d622011-02-25 04:10:33 +00008899 /* See if cheap transparency is possible. It is only possible
8900 * when there is a single transparent color, no semitransparent
8901 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008902 * as the transparent color. We only need this information if
8903 * we are writing a PNG with colortype 0 or 2, and we have not
8904 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008905 */
glennrp5a39f372011-02-25 04:52:16 +00008906 if (number_transparent == 1 &&
8907 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008908 {
8909 ping_have_cheap_transparency = MagickTrue;
8910
8911 if (number_semitransparent != 0)
8912 ping_have_cheap_transparency = MagickFalse;
8913
8914 else if (image_colors == 0 || image_colors > 256 ||
8915 image->colormap == NULL)
8916 {
cristy16ea1392012-03-21 20:38:41 +00008917 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008918 *q;
8919
glennrpfd05d622011-02-25 04:10:33 +00008920 for (y=0; y < (ssize_t) image->rows; y++)
8921 {
8922 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8923
cristy16ea1392012-03-21 20:38:41 +00008924 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008925 break;
8926
8927 for (x=0; x < (ssize_t) image->columns; x++)
8928 {
cristy16ea1392012-03-21 20:38:41 +00008929 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8930 (unsigned short) GetPixelRed(image,q) ==
8931 ping_trans_color.red &&
8932 (unsigned short) GetPixelGreen(image,q) ==
8933 ping_trans_color.green &&
8934 (unsigned short) GetPixelBlue(image,q) ==
8935 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008936 {
8937 ping_have_cheap_transparency = MagickFalse;
8938 break;
8939 }
8940
cristy16ea1392012-03-21 20:38:41 +00008941 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008942 }
glennrpbb4f99d2011-05-22 11:13:17 +00008943
glennrpfd05d622011-02-25 04:10:33 +00008944 if (ping_have_cheap_transparency == MagickFalse)
8945 break;
8946 }
8947 }
8948 else
8949 {
glennrp67b9c1a2011-04-22 18:47:36 +00008950 /* Assuming that image->colormap[0] is the one transparent color
8951 * and that all others are opaque.
8952 */
glennrpfd05d622011-02-25 04:10:33 +00008953 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008954 for (i=1; i<image_colors; i++)
8955 if (image->colormap[i].red == image->colormap[0].red &&
8956 image->colormap[i].green == image->colormap[0].green &&
8957 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008958 {
glennrp67b9c1a2011-04-22 18:47:36 +00008959 ping_have_cheap_transparency = MagickFalse;
8960 break;
glennrpfd05d622011-02-25 04:10:33 +00008961 }
8962 }
glennrpbb4f99d2011-05-22 11:13:17 +00008963
glennrpfd05d622011-02-25 04:10:33 +00008964 if (logging != MagickFalse)
8965 {
8966 if (ping_have_cheap_transparency == MagickFalse)
8967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8968 " Cheap transparency is not possible.");
8969
8970 else
8971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8972 " Cheap transparency is possible.");
8973 }
8974 }
8975 else
8976 ping_have_cheap_transparency = MagickFalse;
8977
glennrp8640fb52010-11-23 15:48:26 +00008978 image_depth=image->depth;
8979
glennrp26c990a2010-11-23 02:23:20 +00008980 quantum_info = (QuantumInfo *) NULL;
8981 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008982 image_colors=(int) image->colors;
cristyb0a657e2012-08-29 00:45:37 +00008983 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
glennrp26c990a2010-11-23 02:23:20 +00008984
glennrp0fe50b42010-11-16 03:52:51 +00008985 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008986 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008987
glennrp52a479c2011-02-26 21:14:38 +00008988 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8989 (image->colors == 0 || image->colormap == NULL))
8990 {
cristy16ea1392012-03-21 20:38:41 +00008991 image_info=DestroyImageInfo(image_info);
8992 image=DestroyImage(image);
8993 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00008994 "Cannot write PNG8 or color-type 3; colormap is NULL",
cristy16ea1392012-03-21 20:38:41 +00008995 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008996 return(MagickFalse);
8997 }
8998
cristy3ed852e2009-09-05 21:47:34 +00008999 /*
9000 Allocate the PNG structures
9001 */
9002#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00009003 error_info.image=image;
9004 error_info.exception=exception;
9005 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009006 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9007 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00009008
cristy3ed852e2009-09-05 21:47:34 +00009009#else
cristy16ea1392012-03-21 20:38:41 +00009010 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009011 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00009012
cristy3ed852e2009-09-05 21:47:34 +00009013#endif
9014 if (ping == (png_struct *) NULL)
9015 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009016
cristy3ed852e2009-09-05 21:47:34 +00009017 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00009018
cristy3ed852e2009-09-05 21:47:34 +00009019 if (ping_info == (png_info *) NULL)
9020 {
9021 png_destroy_write_struct(&ping,(png_info **) NULL);
9022 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9023 }
glennrp0fe50b42010-11-16 03:52:51 +00009024
cristy3ed852e2009-09-05 21:47:34 +00009025 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00009026 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00009027
glennrp5af765f2010-03-30 11:12:18 +00009028 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009029 {
9030 /*
9031 PNG write failed.
9032 */
9033#ifdef PNG_DEBUG
9034 if (image_info->verbose)
9035 (void) printf("PNG write has failed.\n");
9036#endif
9037 png_destroy_write_struct(&ping,&ping_info);
glennrpedaa0382012-04-12 14:16:21 +00009038#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00009039 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009040#endif
glennrpedaa0382012-04-12 14:16:21 +00009041
9042 if (ping_pixels != (unsigned char *) NULL)
9043 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9044
9045 if (quantum_info != (QuantumInfo *) NULL)
9046 quantum_info=DestroyQuantumInfo(quantum_info);
9047
cristy16ea1392012-03-21 20:38:41 +00009048 if (ping_have_blob != MagickFalse)
9049 (void) CloseBlob(image);
9050 image_info=DestroyImageInfo(image_info);
9051 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009052 return(MagickFalse);
9053 }
glennrpedaa0382012-04-12 14:16:21 +00009054
9055 /* { For navigation to end of SETJMP-protected block. Within this
9056 * block, use png_error() instead of Throwing an Exception, to ensure
9057 * that libpng is able to clean up, and that the semaphore is unlocked.
9058 */
9059
9060#ifdef PNG_SETJMP_NOT_THREAD_SAFE
9061 LockSemaphoreInfo(ping_semaphore);
9062#endif
9063
cristy3ed852e2009-09-05 21:47:34 +00009064 /*
9065 Prepare PNG for writing.
9066 */
glennrp9bf97b62012-06-06 21:03:14 +00009067
cristy3ed852e2009-09-05 21:47:34 +00009068#if defined(PNG_MNG_FEATURES_SUPPORTED)
9069 if (mng_info->write_mng)
glennrp25024a62012-06-07 11:38:34 +00009070 {
cristy3ed852e2009-09-05 21:47:34 +00009071 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp25024a62012-06-07 11:38:34 +00009072# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9073 /* Disable new libpng-1.5.10 feature when writing a MNG because
9074 * zero-length PLTE is OK
9075 */
9076 png_set_check_for_invalid_index (ping, 0);
9077# endif
9078 }
glennrp2b013e42010-11-24 16:55:50 +00009079
cristy3ed852e2009-09-05 21:47:34 +00009080#else
9081# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9082 if (mng_info->write_mng)
9083 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00009084
cristy3ed852e2009-09-05 21:47:34 +00009085# endif
9086#endif
glennrp2b013e42010-11-24 16:55:50 +00009087
cristy3ed852e2009-09-05 21:47:34 +00009088 x=0;
glennrp2b013e42010-11-24 16:55:50 +00009089
cristy4e5bc842010-06-09 13:56:01 +00009090 ping_width=(png_uint_32) image->columns;
9091 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00009092
cristy3ed852e2009-09-05 21:47:34 +00009093 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9094 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009095
cristy3ed852e2009-09-05 21:47:34 +00009096 if (mng_info->write_png_depth != 0)
9097 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009098
cristy3ed852e2009-09-05 21:47:34 +00009099 /* Adjust requested depth to next higher valid depth if necessary */
9100 if (image_depth > 8)
9101 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009102
cristy3ed852e2009-09-05 21:47:34 +00009103 if ((image_depth > 4) && (image_depth < 8))
9104 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009105
cristy3ed852e2009-09-05 21:47:34 +00009106 if (image_depth == 3)
9107 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00009108
cristy3ed852e2009-09-05 21:47:34 +00009109 if (logging != MagickFalse)
9110 {
9111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009112 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00009113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009114 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00009115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00009116 " image_matte=%.20g",(double) image->alpha_trait);
cristy3ed852e2009-09-05 21:47:34 +00009117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009118 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00009119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009120 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00009121 }
glennrp8640fb52010-11-23 15:48:26 +00009122
cristy3ed852e2009-09-05 21:47:34 +00009123 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00009124 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00009125
glennrp26f37912010-12-23 16:22:42 +00009126
cristy3ed852e2009-09-05 21:47:34 +00009127#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00009128 if (ping_exclude_pHYs == MagickFalse)
9129 {
cristy16ea1392012-03-21 20:38:41 +00009130 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00009131 (!mng_info->write_mng || !mng_info->equal_physs))
9132 {
glennrp0fe50b42010-11-16 03:52:51 +00009133 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9135 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00009136
9137 if (image->units == PixelsPerInchResolution)
9138 {
glennrpdfd70802010-11-14 01:23:35 +00009139 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009140 ping_pHYs_x_resolution=
cristy16ea1392012-03-21 20:38:41 +00009141 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00009142 ping_pHYs_y_resolution=
cristy16ea1392012-03-21 20:38:41 +00009143 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009144 }
glennrpdfd70802010-11-14 01:23:35 +00009145
cristy3ed852e2009-09-05 21:47:34 +00009146 else if (image->units == PixelsPerCentimeterResolution)
9147 {
glennrpdfd70802010-11-14 01:23:35 +00009148 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy16ea1392012-03-21 20:38:41 +00009149 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9150 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009151 }
glennrp991d11d2010-11-12 21:55:28 +00009152
cristy3ed852e2009-09-05 21:47:34 +00009153 else
9154 {
glennrpdfd70802010-11-14 01:23:35 +00009155 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy16ea1392012-03-21 20:38:41 +00009156 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9157 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009158 }
glennrp991d11d2010-11-12 21:55:28 +00009159
glennrp823b55c2011-03-14 18:46:46 +00009160 if (logging != MagickFalse)
9161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9162 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9163 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9164 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009165 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009166 }
glennrp26f37912010-12-23 16:22:42 +00009167 }
cristy3ed852e2009-09-05 21:47:34 +00009168#endif
glennrpa521b2f2010-10-29 04:11:03 +00009169
glennrp26f37912010-12-23 16:22:42 +00009170 if (ping_exclude_bKGD == MagickFalse)
9171 {
glennrpa521b2f2010-10-29 04:11:03 +00009172 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009173 {
glennrpa521b2f2010-10-29 04:11:03 +00009174 unsigned int
9175 mask;
cristy3ed852e2009-09-05 21:47:34 +00009176
glennrpa521b2f2010-10-29 04:11:03 +00009177 mask=0xffff;
9178 if (ping_bit_depth == 8)
9179 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009180
glennrpa521b2f2010-10-29 04:11:03 +00009181 if (ping_bit_depth == 4)
9182 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009183
glennrpa521b2f2010-10-29 04:11:03 +00009184 if (ping_bit_depth == 2)
9185 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009186
glennrpa521b2f2010-10-29 04:11:03 +00009187 if (ping_bit_depth == 1)
9188 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009189
glennrpa521b2f2010-10-29 04:11:03 +00009190 ping_background.red=(png_uint_16)
9191 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009192
glennrpa521b2f2010-10-29 04:11:03 +00009193 ping_background.green=(png_uint_16)
9194 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009195
glennrpa521b2f2010-10-29 04:11:03 +00009196 ping_background.blue=(png_uint_16)
9197 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009198
9199 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009200 }
cristy3ed852e2009-09-05 21:47:34 +00009201
glennrp0fe50b42010-11-16 03:52:51 +00009202 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009203 {
9204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9205 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9207 " background_color index is %d",
9208 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009209
9210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9211 " ping_bit_depth=%d",ping_bit_depth);
9212 }
glennrp0fe50b42010-11-16 03:52:51 +00009213
9214 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009215 }
glennrp0fe50b42010-11-16 03:52:51 +00009216
cristy3ed852e2009-09-05 21:47:34 +00009217 /*
9218 Select the color type.
9219 */
9220 matte=image_matte;
9221 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009222
glennrp1273f7b2011-02-24 03:20:30 +00009223 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009224 {
glennrp0fe50b42010-11-16 03:52:51 +00009225
glennrpfd05d622011-02-25 04:10:33 +00009226 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009227 for reducing the sample depth from 8. */
9228
glennrp0fe50b42010-11-16 03:52:51 +00009229 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009230
glennrp8bb3a022010-12-13 20:40:04 +00009231 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009232
9233 /*
9234 Set image palette.
9235 */
9236 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9237
glennrp0fe50b42010-11-16 03:52:51 +00009238 if (logging != MagickFalse)
9239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9240 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009241 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009242
9243 for (i=0; i < (ssize_t) number_colors; i++)
9244 {
9245 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9246 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9247 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9248 if (logging != MagickFalse)
9249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009250#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009251 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009252#else
9253 " %5ld (%5d,%5d,%5d)",
9254#endif
glennrp0fe50b42010-11-16 03:52:51 +00009255 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9256
9257 }
glennrp2b013e42010-11-24 16:55:50 +00009258
glennrp8bb3a022010-12-13 20:40:04 +00009259 ping_have_PLTE=MagickTrue;
9260 image_depth=ping_bit_depth;
9261 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009262
glennrp58e01762011-01-07 15:28:54 +00009263 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009264 {
glennrp0fe50b42010-11-16 03:52:51 +00009265 /*
9266 Identify which colormap entry is transparent.
9267 */
9268 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009269 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009270
glennrp8bb3a022010-12-13 20:40:04 +00009271 for (i=0; i < (ssize_t) number_transparent; i++)
9272 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009273
glennrp0fe50b42010-11-16 03:52:51 +00009274
glennrp2cc891a2010-12-24 13:44:32 +00009275 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009276 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009277
9278 if (ping_num_trans == 0)
9279 ping_have_tRNS=MagickFalse;
9280
glennrp8bb3a022010-12-13 20:40:04 +00009281 else
9282 ping_have_tRNS=MagickTrue;
9283 }
glennrp0fe50b42010-11-16 03:52:51 +00009284
glennrp1273f7b2011-02-24 03:20:30 +00009285 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009286 {
glennrp1273f7b2011-02-24 03:20:30 +00009287 /*
9288 * Identify which colormap entry is the background color.
9289 */
9290
glennrp4f25bd02011-01-01 18:51:28 +00009291 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9292 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9293 break;
glennrp0fe50b42010-11-16 03:52:51 +00009294
glennrp4f25bd02011-01-01 18:51:28 +00009295 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009296
9297 if (logging != MagickFalse)
9298 {
9299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9300 " background_color index is %d",
9301 (int) ping_background.index);
9302 }
glennrp4f25bd02011-01-01 18:51:28 +00009303 }
cristy3ed852e2009-09-05 21:47:34 +00009304 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009305
glennrp7e65e932011-08-19 02:31:16 +00009306 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009307 {
9308 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009309 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009310 }
glennrp0fe50b42010-11-16 03:52:51 +00009311
glennrp7e65e932011-08-19 02:31:16 +00009312 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009313 {
9314 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009315 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009316 }
glennrp0fe50b42010-11-16 03:52:51 +00009317
glennrp8bb3a022010-12-13 20:40:04 +00009318 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009319 {
glennrp5af765f2010-03-30 11:12:18 +00009320 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009321
glennrp8bb3a022010-12-13 20:40:04 +00009322 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009323 {
glennrp5af765f2010-03-30 11:12:18 +00009324 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009325
glennrp5af765f2010-03-30 11:12:18 +00009326 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9327 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009328 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009329
glennrp8bb3a022010-12-13 20:40:04 +00009330 else
9331 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009332
9333 if (logging != MagickFalse)
9334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9335 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009336 }
glennrp0fe50b42010-11-16 03:52:51 +00009337
glennrp7c4c9e62011-03-21 20:23:32 +00009338 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009339 {
9340 if (logging != MagickFalse)
9341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009342 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009343
glennrpd6bf1612010-12-17 17:28:54 +00009344 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009345 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009346
glennrpd6bf1612010-12-17 17:28:54 +00009347 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009348 {
glennrp5af765f2010-03-30 11:12:18 +00009349 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009350 image_matte=MagickFalse;
9351 }
glennrp0fe50b42010-11-16 03:52:51 +00009352
glennrpd6bf1612010-12-17 17:28:54 +00009353 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009354 {
glennrp5af765f2010-03-30 11:12:18 +00009355 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009356 image_matte=MagickTrue;
9357 }
glennrp0fe50b42010-11-16 03:52:51 +00009358
glennrp5aa37f62011-01-02 03:07:57 +00009359 if (image_info->type == PaletteType ||
9360 image_info->type == PaletteMatteType)
9361 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9362
glennrp7c4c9e62011-03-21 20:23:32 +00009363 if (mng_info->write_png_colortype == 0 &&
9364 (image_info->type == UndefinedType ||
9365 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009366 {
glennrp5aa37f62011-01-02 03:07:57 +00009367 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009368 {
glennrp5aa37f62011-01-02 03:07:57 +00009369 if (image_matte == MagickFalse)
9370 {
9371 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9372 image_matte=MagickFalse;
9373 }
glennrp0fe50b42010-11-16 03:52:51 +00009374
glennrp0b206f52011-01-07 04:55:32 +00009375 else
glennrp5aa37f62011-01-02 03:07:57 +00009376 {
9377 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9378 image_matte=MagickTrue;
9379 }
9380 }
9381 else
glennrp8bb3a022010-12-13 20:40:04 +00009382 {
glennrp5aa37f62011-01-02 03:07:57 +00009383 if (image_matte == MagickFalse)
9384 {
9385 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9386 image_matte=MagickFalse;
9387 }
glennrp8bb3a022010-12-13 20:40:04 +00009388
glennrp0b206f52011-01-07 04:55:32 +00009389 else
glennrp5aa37f62011-01-02 03:07:57 +00009390 {
9391 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9392 image_matte=MagickTrue;
9393 }
9394 }
glennrp0fe50b42010-11-16 03:52:51 +00009395 }
glennrp5aa37f62011-01-02 03:07:57 +00009396
cristy3ed852e2009-09-05 21:47:34 +00009397 }
glennrp0fe50b42010-11-16 03:52:51 +00009398
cristy3ed852e2009-09-05 21:47:34 +00009399 if (logging != MagickFalse)
9400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009401 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009402
glennrp5af765f2010-03-30 11:12:18 +00009403 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009404 {
9405 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9406 ping_color_type == PNG_COLOR_TYPE_RGB ||
9407 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9408 ping_bit_depth=8;
9409 }
cristy3ed852e2009-09-05 21:47:34 +00009410
glennrpd6bf1612010-12-17 17:28:54 +00009411 old_bit_depth=ping_bit_depth;
9412
glennrp5af765f2010-03-30 11:12:18 +00009413 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009414 {
cristy8a46d822012-08-28 23:32:39 +00009415 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00009416 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009417 }
glennrp8640fb52010-11-23 15:48:26 +00009418
glennrp5af765f2010-03-30 11:12:18 +00009419 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009420 {
cristy35ef8242010-06-03 16:24:13 +00009421 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009422 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009423
9424 if (image->colors == 0)
9425 {
glennrp0fe50b42010-11-16 03:52:51 +00009426 /* DO SOMETHING */
glennrpedaa0382012-04-12 14:16:21 +00009427 png_error(ping,"image has 0 colors");
glennrp0f111982010-07-07 20:18:33 +00009428 }
9429
cristy35ef8242010-06-03 16:24:13 +00009430 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009431 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009432 }
glennrp2b013e42010-11-24 16:55:50 +00009433
glennrpd6bf1612010-12-17 17:28:54 +00009434 if (logging != MagickFalse)
9435 {
9436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9437 " Number of colors: %.20g",(double) image_colors);
9438
9439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9440 " Tentative PNG bit depth: %d",ping_bit_depth);
9441 }
9442
9443 if (ping_bit_depth < (int) mng_info->write_png_depth)
9444 ping_bit_depth = mng_info->write_png_depth;
9445 }
glennrp2cc891a2010-12-24 13:44:32 +00009446
glennrp5af765f2010-03-30 11:12:18 +00009447 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009448
cristy3ed852e2009-09-05 21:47:34 +00009449 if (logging != MagickFalse)
9450 {
9451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +00009452 " Tentative PNG color type: %s (%.20g)",
9453 PngColorTypeToString(ping_color_type),
9454 (double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009455
cristy3ed852e2009-09-05 21:47:34 +00009456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009457 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009458
cristy3ed852e2009-09-05 21:47:34 +00009459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009460 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009461
cristy3ed852e2009-09-05 21:47:34 +00009462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009463
glennrp8640fb52010-11-23 15:48:26 +00009464 " image->depth: %.20g",(double) image->depth);
9465
9466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009467 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009468 }
9469
glennrp58e01762011-01-07 15:28:54 +00009470 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009471 {
glennrp4f25bd02011-01-01 18:51:28 +00009472 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009473 {
glennrp7c4c9e62011-03-21 20:23:32 +00009474 if (mng_info->write_png_colortype == 0)
9475 {
9476 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009477
glennrp7c4c9e62011-03-21 20:23:32 +00009478 if (ping_have_color != MagickFalse)
9479 ping_color_type=PNG_COLOR_TYPE_RGBA;
9480 }
glennrp4f25bd02011-01-01 18:51:28 +00009481
9482 /*
9483 * Determine if there is any transparent color.
9484 */
9485 if (number_transparent + number_semitransparent == 0)
9486 {
9487 /*
9488 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9489 */
glennrpa6a06632011-01-19 15:15:34 +00009490
glennrp4f25bd02011-01-01 18:51:28 +00009491 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009492
9493 if (mng_info->write_png_colortype == 0)
9494 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009495 }
9496
9497 else
9498 {
9499 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009500 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009501
9502 mask=0xffff;
9503
9504 if (ping_bit_depth == 8)
9505 mask=0x00ff;
9506
9507 if (ping_bit_depth == 4)
9508 mask=0x000f;
9509
9510 if (ping_bit_depth == 2)
9511 mask=0x0003;
9512
9513 if (ping_bit_depth == 1)
9514 mask=0x0001;
9515
9516 ping_trans_color.red=(png_uint_16)
9517 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9518
9519 ping_trans_color.green=(png_uint_16)
9520 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9521
9522 ping_trans_color.blue=(png_uint_16)
9523 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9524
9525 ping_trans_color.gray=(png_uint_16)
cristy16ea1392012-03-21 20:38:41 +00009526 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009527 image->colormap)) & mask);
9528
9529 ping_trans_color.index=(png_byte) 0;
9530
9531 ping_have_tRNS=MagickTrue;
9532 }
9533
9534 if (ping_have_tRNS != MagickFalse)
9535 {
9536 /*
glennrpfd05d622011-02-25 04:10:33 +00009537 * Determine if there is one and only one transparent color
9538 * and if so if it is fully transparent.
9539 */
9540 if (ping_have_cheap_transparency == MagickFalse)
9541 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009542 }
9543
9544 if (ping_have_tRNS != MagickFalse)
9545 {
glennrp7c4c9e62011-03-21 20:23:32 +00009546 if (mng_info->write_png_colortype == 0)
9547 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009548
9549 if (image_depth == 8)
9550 {
9551 ping_trans_color.red&=0xff;
9552 ping_trans_color.green&=0xff;
9553 ping_trans_color.blue&=0xff;
9554 ping_trans_color.gray&=0xff;
9555 }
9556 }
9557 }
cristy3ed852e2009-09-05 21:47:34 +00009558 else
9559 {
cristy3ed852e2009-09-05 21:47:34 +00009560 if (image_depth == 8)
9561 {
glennrp5af765f2010-03-30 11:12:18 +00009562 ping_trans_color.red&=0xff;
9563 ping_trans_color.green&=0xff;
9564 ping_trans_color.blue&=0xff;
9565 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009566 }
9567 }
9568 }
glennrp8640fb52010-11-23 15:48:26 +00009569
cristy3ed852e2009-09-05 21:47:34 +00009570 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009571
glennrp2e09f552010-11-14 00:38:48 +00009572 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009573 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009574
glennrp39992b42010-11-14 00:03:43 +00009575 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009576 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009577 ping_have_color == MagickFalse &&
9578 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009579 {
cristy35ef8242010-06-03 16:24:13 +00009580 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009581
cristy3ed852e2009-09-05 21:47:34 +00009582 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009583 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009584
glennrp7c4c9e62011-03-21 20:23:32 +00009585 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009586 {
glennrp5af765f2010-03-30 11:12:18 +00009587 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009588
cristy3ed852e2009-09-05 21:47:34 +00009589 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009590 {
9591 if (logging != MagickFalse)
9592 {
9593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9594 " Scaling ping_trans_color (0)");
9595 }
9596 ping_trans_color.gray*=0x0101;
9597 }
cristy3ed852e2009-09-05 21:47:34 +00009598 }
glennrp0fe50b42010-11-16 03:52:51 +00009599
cristy3ed852e2009-09-05 21:47:34 +00009600 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9601 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009602
glennrp136ee3a2011-04-27 15:47:45 +00009603 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009604 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009605 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009606
cristy3ed852e2009-09-05 21:47:34 +00009607 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009608 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009609
cristy3ed852e2009-09-05 21:47:34 +00009610 else
9611 {
glennrp5af765f2010-03-30 11:12:18 +00009612 ping_bit_depth=8;
9613 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009614 {
9615 if(!mng_info->write_png_depth)
9616 {
glennrp5af765f2010-03-30 11:12:18 +00009617 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009618
cristy35ef8242010-06-03 16:24:13 +00009619 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009620 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009621 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009622 }
9623 }
glennrp2b013e42010-11-24 16:55:50 +00009624
glennrp0fe50b42010-11-16 03:52:51 +00009625 else if (ping_color_type ==
9626 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009627 mng_info->IsPalette)
9628 {
cristy3ed852e2009-09-05 21:47:34 +00009629 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009630
cristy3ed852e2009-09-05 21:47:34 +00009631 int
9632 depth_4_ok=MagickTrue,
9633 depth_2_ok=MagickTrue,
9634 depth_1_ok=MagickTrue;
9635
cristybb503372010-05-27 20:51:26 +00009636 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009637 {
9638 unsigned char
9639 intensity;
9640
9641 intensity=ScaleQuantumToChar(image->colormap[i].red);
9642
9643 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9644 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9645 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9646 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009647 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009648 depth_1_ok=MagickFalse;
9649 }
glennrp2b013e42010-11-24 16:55:50 +00009650
cristy3ed852e2009-09-05 21:47:34 +00009651 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009652 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009653
cristy3ed852e2009-09-05 21:47:34 +00009654 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009655 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009656
cristy3ed852e2009-09-05 21:47:34 +00009657 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009658 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009659 }
9660 }
glennrp2b013e42010-11-24 16:55:50 +00009661
glennrp5af765f2010-03-30 11:12:18 +00009662 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009663 }
glennrp0fe50b42010-11-16 03:52:51 +00009664
cristy3ed852e2009-09-05 21:47:34 +00009665 else
glennrp0fe50b42010-11-16 03:52:51 +00009666
cristy3ed852e2009-09-05 21:47:34 +00009667 if (mng_info->IsPalette)
9668 {
glennrp17a14852010-05-10 03:01:59 +00009669 number_colors=image_colors;
9670
cristy3ed852e2009-09-05 21:47:34 +00009671 if (image_depth <= 8)
9672 {
cristy3ed852e2009-09-05 21:47:34 +00009673 /*
9674 Set image palette.
9675 */
glennrp5af765f2010-03-30 11:12:18 +00009676 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009677
glennrp58e01762011-01-07 15:28:54 +00009678 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009679 {
glennrp9c1eb072010-06-06 22:19:15 +00009680 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009681
glennrp3b51f0e2010-11-27 18:14:08 +00009682 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9684 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009685 }
glennrp0fe50b42010-11-16 03:52:51 +00009686
cristy3ed852e2009-09-05 21:47:34 +00009687 else
9688 {
cristybb503372010-05-27 20:51:26 +00009689 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009690 {
9691 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9692 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9693 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9694 }
glennrp0fe50b42010-11-16 03:52:51 +00009695
glennrp3b51f0e2010-11-27 18:14:08 +00009696 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009698 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009699 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009700
glennrp39992b42010-11-14 00:03:43 +00009701 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009702 }
glennrp0fe50b42010-11-16 03:52:51 +00009703
cristy3ed852e2009-09-05 21:47:34 +00009704 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009705 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009706 {
cristybefe4d22010-06-07 01:18:58 +00009707 size_t
9708 one;
9709
glennrp5af765f2010-03-30 11:12:18 +00009710 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009711 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009712
cristy16ea1392012-03-21 20:38:41 +00009713 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009714 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009715 }
glennrp0fe50b42010-11-16 03:52:51 +00009716
glennrp5af765f2010-03-30 11:12:18 +00009717 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009718
glennrp58e01762011-01-07 15:28:54 +00009719 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009720 {
glennrp0fe50b42010-11-16 03:52:51 +00009721 /*
glennrpd6bf1612010-12-17 17:28:54 +00009722 * Set up trans_colors array.
9723 */
glennrp0fe50b42010-11-16 03:52:51 +00009724 assert(number_colors <= 256);
9725
glennrpd6bf1612010-12-17 17:28:54 +00009726 ping_num_trans=(unsigned short) (number_transparent +
9727 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009728
9729 if (ping_num_trans == 0)
9730 ping_have_tRNS=MagickFalse;
9731
glennrpd6bf1612010-12-17 17:28:54 +00009732 else
glennrp0fe50b42010-11-16 03:52:51 +00009733 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009734 if (logging != MagickFalse)
9735 {
9736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9737 " Scaling ping_trans_color (1)");
9738 }
glennrpd6bf1612010-12-17 17:28:54 +00009739 ping_have_tRNS=MagickTrue;
9740
9741 for (i=0; i < ping_num_trans; i++)
9742 {
glennrp750105b2012-04-25 16:20:45 +00009743 ping_trans_alpha[i]= (png_byte)
cristy16ea1392012-03-21 20:38:41 +00009744 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009745 }
glennrp0fe50b42010-11-16 03:52:51 +00009746 }
9747 }
cristy3ed852e2009-09-05 21:47:34 +00009748 }
9749 }
glennrp0fe50b42010-11-16 03:52:51 +00009750
cristy3ed852e2009-09-05 21:47:34 +00009751 else
9752 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009753
cristy3ed852e2009-09-05 21:47:34 +00009754 if (image_depth < 8)
9755 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009756
cristy3ed852e2009-09-05 21:47:34 +00009757 if ((save_image_depth == 16) && (image_depth == 8))
9758 {
glennrp4f25bd02011-01-01 18:51:28 +00009759 if (logging != MagickFalse)
9760 {
9761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9762 " Scaling ping_trans_color from (%d,%d,%d)",
9763 (int) ping_trans_color.red,
9764 (int) ping_trans_color.green,
9765 (int) ping_trans_color.blue);
9766 }
9767
glennrp5af765f2010-03-30 11:12:18 +00009768 ping_trans_color.red*=0x0101;
9769 ping_trans_color.green*=0x0101;
9770 ping_trans_color.blue*=0x0101;
9771 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009772
9773 if (logging != MagickFalse)
9774 {
9775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9776 " to (%d,%d,%d)",
9777 (int) ping_trans_color.red,
9778 (int) ping_trans_color.green,
9779 (int) ping_trans_color.blue);
9780 }
cristy3ed852e2009-09-05 21:47:34 +00009781 }
9782 }
9783
cristy4383ec82011-01-05 15:42:32 +00009784 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9785 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009786
cristy3ed852e2009-09-05 21:47:34 +00009787 /*
9788 Adjust background and transparency samples in sub-8-bit grayscale files.
9789 */
glennrp5af765f2010-03-30 11:12:18 +00009790 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009791 PNG_COLOR_TYPE_GRAY)
9792 {
9793 png_uint_16
9794 maxval;
9795
cristy35ef8242010-06-03 16:24:13 +00009796 size_t
9797 one=1;
9798
cristy22ffd972010-06-03 16:51:47 +00009799 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009800
glennrp4f25bd02011-01-01 18:51:28 +00009801 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009802 {
cristy3ed852e2009-09-05 21:47:34 +00009803
cristy16ea1392012-03-21 20:38:41 +00009804 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9805 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9806 &image->background_color))) +.5)));
9807
cristy3ed852e2009-09-05 21:47:34 +00009808 if (logging != MagickFalse)
9809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00009810 " Setting up bKGD chunk (2)");
9811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9812 " background_color index is %d",
9813 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009814
glennrp991d11d2010-11-12 21:55:28 +00009815 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009816 }
cristy3ed852e2009-09-05 21:47:34 +00009817
glennrp3e3e20f2011-06-09 04:21:43 +00009818 if (logging != MagickFalse)
9819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9820 " Scaling ping_trans_color.gray from %d",
9821 (int)ping_trans_color.gray);
9822
glennrp9be9b1c2011-06-09 12:21:45 +00009823 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009824 ping_trans_color.gray)+.5);
9825
9826 if (logging != MagickFalse)
9827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009829 }
glennrp17a14852010-05-10 03:01:59 +00009830
glennrp26f37912010-12-23 16:22:42 +00009831 if (ping_exclude_bKGD == MagickFalse)
9832 {
glennrp1273f7b2011-02-24 03:20:30 +00009833 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009834 {
9835 /*
9836 Identify which colormap entry is the background color.
9837 */
9838
glennrp17a14852010-05-10 03:01:59 +00009839 number_colors=image_colors;
9840
glennrpa521b2f2010-10-29 04:11:03 +00009841 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9842 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009843 break;
9844
9845 ping_background.index=(png_byte) i;
9846
glennrp3b51f0e2010-11-27 18:14:08 +00009847 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009848 {
9849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009850 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009851 }
glennrp0fe50b42010-11-16 03:52:51 +00009852
cristy13d07042010-11-21 20:56:18 +00009853 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009854 {
9855 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009856
9857 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009858 {
9859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9860 " background =(%d,%d,%d)",
9861 (int) ping_background.red,
9862 (int) ping_background.green,
9863 (int) ping_background.blue);
9864 }
9865 }
glennrpa521b2f2010-10-29 04:11:03 +00009866
glennrpd6bf1612010-12-17 17:28:54 +00009867 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009868 {
glennrp3b51f0e2010-11-27 18:14:08 +00009869 if (logging != MagickFalse)
9870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9871 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009872 ping_have_bKGD = MagickFalse;
9873 }
glennrp17a14852010-05-10 03:01:59 +00009874 }
glennrp26f37912010-12-23 16:22:42 +00009875 }
glennrp17a14852010-05-10 03:01:59 +00009876
cristy3ed852e2009-09-05 21:47:34 +00009877 if (logging != MagickFalse)
9878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +00009879 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
9880 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009881 /*
9882 Initialize compression level and filtering.
9883 */
9884 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009885 {
9886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9887 " Setting up deflate compression");
9888
9889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9890 " Compression buffer size: 32768");
9891 }
9892
cristy3ed852e2009-09-05 21:47:34 +00009893 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009894
cristy3ed852e2009-09-05 21:47:34 +00009895 if (logging != MagickFalse)
9896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9897 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009898
cristy4054bfb2011-08-29 23:41:39 +00009899 png_set_compression_mem_level(ping, 9);
9900
glennrp10d739e2011-06-29 18:00:52 +00009901 /* Untangle the "-quality" setting:
9902
9903 Undefined is 0; the default is used.
9904 Default is 75
9905
9906 10's digit:
9907
9908 0: Use Z_HUFFMAN_ONLY strategy with the
9909 zlib default compression level
9910
9911 1-9: the zlib compression level
9912
9913 1's digit:
9914
9915 0-4: the PNG filter method
9916
9917 5: libpng adaptive filtering if compression level > 5
9918 libpng filter type "none" if compression level <= 5
9919 or if image is grayscale or palette
glennrp750105b2012-04-25 16:20:45 +00009920
glennrp10d739e2011-06-29 18:00:52 +00009921 6: libpng adaptive filtering
9922
9923 7: "LOCO" filtering (intrapixel differing) if writing
9924 a MNG, othewise "none". Did not work in IM-6.7.0-9
9925 and earlier because of a missing "else".
9926
9927 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009928 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009929
9930 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009931 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009932
9933 Note that using the -quality option, not all combinations of
9934 PNG filter type, zlib compression level, and zlib compression
cristy16ea1392012-03-21 20:38:41 +00009935 strategy are possible. This will be addressed soon in a
9936 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009937
9938 */
9939
cristy3ed852e2009-09-05 21:47:34 +00009940 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9941 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009942
glennrp18682582011-06-30 18:11:47 +00009943 if (quality <= 9)
9944 {
9945 if (mng_info->write_png_compression_strategy == 0)
9946 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9947 }
glennrp750105b2012-04-25 16:20:45 +00009948
glennrp18682582011-06-30 18:11:47 +00009949 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009950 {
9951 int
9952 level;
9953
cristybb503372010-05-27 20:51:26 +00009954 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009955
glennrp18682582011-06-30 18:11:47 +00009956 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009957 }
glennrp0fe50b42010-11-16 03:52:51 +00009958
glennrp18682582011-06-30 18:11:47 +00009959 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009960 {
glennrp18682582011-06-30 18:11:47 +00009961 if ((quality %10) == 8 || (quality %10) == 9)
glennrpa24b2452012-06-27 11:38:38 +00009962#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
9963 mng_info->write_png_compression_strategy=Z_RLE+1;
9964#else
9965 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
9966#endif
cristy3ed852e2009-09-05 21:47:34 +00009967 }
glennrp0fe50b42010-11-16 03:52:51 +00009968
glennrp18682582011-06-30 18:11:47 +00009969 if (mng_info->write_png_compression_filter == 0)
9970 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9971
cristy3ed852e2009-09-05 21:47:34 +00009972 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009973 {
glennrp18682582011-06-30 18:11:47 +00009974 if (mng_info->write_png_compression_level)
9975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9976 " Compression level: %d",
9977 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009978
glennrp18682582011-06-30 18:11:47 +00009979 if (mng_info->write_png_compression_strategy)
9980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9981 " Compression strategy: %d",
9982 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009983
glennrp18682582011-06-30 18:11:47 +00009984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9985 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009986
cristy4054bfb2011-08-29 23:41:39 +00009987 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9989 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009990 else if (mng_info->write_png_compression_filter == 0 ||
9991 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9993 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009994 else
9995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9996 " Base filter method: %d",
9997 (int) mng_info->write_png_compression_filter-1);
9998 }
glennrp2b013e42010-11-24 16:55:50 +00009999
glennrp18682582011-06-30 18:11:47 +000010000 if (mng_info->write_png_compression_level != 0)
10001 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10002
10003 if (mng_info->write_png_compression_filter == 6)
10004 {
10005 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10006 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10007 (quality < 50))
10008 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10009 else
10010 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10011 }
cristy4054bfb2011-08-29 23:41:39 +000010012 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +000010013 mng_info->write_png_compression_filter == 10)
10014 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10015
10016 else if (mng_info->write_png_compression_filter == 8)
10017 {
10018#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10019 if (mng_info->write_mng)
10020 {
10021 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10022 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10023 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10024 }
10025#endif
cristy4054bfb2011-08-29 23:41:39 +000010026 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +000010027 }
10028
10029 else if (mng_info->write_png_compression_filter == 9)
10030 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10031
10032 else if (mng_info->write_png_compression_filter != 0)
10033 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10034 mng_info->write_png_compression_filter-1);
10035
10036 if (mng_info->write_png_compression_strategy != 0)
10037 png_set_compression_strategy(ping,
10038 mng_info->write_png_compression_strategy-1);
10039
cristy0d57eec2011-09-04 22:13:56 +000010040 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10041 if (ping_exclude_sRGB != MagickFalse ||
10042 (image->rendering_intent == UndefinedIntent))
10043 {
10044 if ((ping_exclude_tEXt == MagickFalse ||
10045 ping_exclude_zTXt == MagickFalse) &&
10046 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +000010047 {
10048 ResetImageProfileIterator(image);
10049 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +000010050 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010051 profile=GetImageProfile(image,name);
10052
10053 if (profile != (StringInfo *) NULL)
10054 {
glennrp5af765f2010-03-30 11:12:18 +000010055#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +000010056 if ((LocaleCompare(name,"ICC") == 0) ||
10057 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +000010058 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010059
10060 if (ping_exclude_iCCP == MagickFalse)
10061 {
cristy16ea1392012-03-21 20:38:41 +000010062 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +000010063#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +000010064 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +000010065#else
10066 (png_const_bytep) GetStringInfoDatum(profile),
10067#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010068 (png_uint_32) GetStringInfoLength(profile));
10069 }
glennrp26f37912010-12-23 16:22:42 +000010070 }
glennrp0fe50b42010-11-16 03:52:51 +000010071
glennrpc8cbc5d2011-01-01 00:12:34 +000010072 else
cristy3ed852e2009-09-05 21:47:34 +000010073#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010074 if (ping_exclude_zCCP == MagickFalse)
10075 {
glennrpcf002022011-01-30 02:38:15 +000010076 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +000010077 (unsigned char *) name,(unsigned char *) name,
10078 GetStringInfoDatum(profile),
10079 (png_uint_32) GetStringInfoLength(profile));
10080 }
10081 }
glennrp0b206f52011-01-07 04:55:32 +000010082
glennrpc8cbc5d2011-01-01 00:12:34 +000010083 if (logging != MagickFalse)
10084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10085 " Setting up text chunk with %s profile",name);
10086
10087 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +000010088 }
cristy0d57eec2011-09-04 22:13:56 +000010089 }
cristy3ed852e2009-09-05 21:47:34 +000010090 }
10091
10092#if defined(PNG_WRITE_sRGB_SUPPORTED)
10093 if ((mng_info->have_write_global_srgb == 0) &&
glennrp5f11bf92012-05-05 03:21:03 +000010094 (image->rendering_intent != UndefinedIntent))
cristy3ed852e2009-09-05 21:47:34 +000010095 {
glennrp26f37912010-12-23 16:22:42 +000010096 if (ping_exclude_sRGB == MagickFalse)
10097 {
10098 /*
10099 Note image rendering intent.
10100 */
10101 if (logging != MagickFalse)
10102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10103 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +000010104
glennrp26f37912010-12-23 16:22:42 +000010105 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +000010106 Magick_RenderingIntent_to_PNG_RenderingIntent(
10107 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +000010108 }
cristy3ed852e2009-09-05 21:47:34 +000010109 }
glennrp26f37912010-12-23 16:22:42 +000010110
glennrp5af765f2010-03-30 11:12:18 +000010111 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +000010112#endif
10113 {
glennrp2cc891a2010-12-24 13:44:32 +000010114 if (ping_exclude_gAMA == MagickFalse &&
10115 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +000010116 (image->gamma < .45 || image->gamma > .46)))
10117 {
cristy3ed852e2009-09-05 21:47:34 +000010118 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10119 {
10120 /*
10121 Note image gamma.
10122 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10123 */
10124 if (logging != MagickFalse)
10125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10126 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010127
cristy3ed852e2009-09-05 21:47:34 +000010128 png_set_gAMA(ping,ping_info,image->gamma);
10129 }
glennrp26f37912010-12-23 16:22:42 +000010130 }
glennrp2b013e42010-11-24 16:55:50 +000010131
glennrp26f37912010-12-23 16:22:42 +000010132 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010133 {
glennrp26f37912010-12-23 16:22:42 +000010134 if ((mng_info->have_write_global_chrm == 0) &&
10135 (image->chromaticity.red_primary.x != 0.0))
10136 {
10137 /*
10138 Note image chromaticity.
10139 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10140 */
10141 PrimaryInfo
10142 bp,
10143 gp,
10144 rp,
10145 wp;
cristy3ed852e2009-09-05 21:47:34 +000010146
glennrp26f37912010-12-23 16:22:42 +000010147 wp=image->chromaticity.white_point;
10148 rp=image->chromaticity.red_primary;
10149 gp=image->chromaticity.green_primary;
10150 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010151
glennrp26f37912010-12-23 16:22:42 +000010152 if (logging != MagickFalse)
10153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10154 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010155
glennrp26f37912010-12-23 16:22:42 +000010156 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10157 bp.x,bp.y);
10158 }
10159 }
cristy3ed852e2009-09-05 21:47:34 +000010160 }
glennrpdfd70802010-11-14 01:23:35 +000010161
glennrp5af765f2010-03-30 11:12:18 +000010162 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010163
10164 if (mng_info->write_mng)
10165 png_set_sig_bytes(ping,8);
10166
cristy5d6fc9c2011-12-27 03:10:42 +000010167 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
cristy3ed852e2009-09-05 21:47:34 +000010168
glennrpd6bf1612010-12-17 17:28:54 +000010169 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010170 {
10171 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010172 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010173 {
glennrp5af765f2010-03-30 11:12:18 +000010174 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010175
glennrp5af765f2010-03-30 11:12:18 +000010176 if (ping_bit_depth < 8)
10177 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010178 }
glennrp0fe50b42010-11-16 03:52:51 +000010179
cristy3ed852e2009-09-05 21:47:34 +000010180 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010181 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010182 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010183 }
10184
glennrp0e8ea192010-12-24 18:00:33 +000010185 if (ping_need_colortype_warning != MagickFalse ||
10186 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010187 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010188 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010189 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010190 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010191 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010192 {
10193 if (logging != MagickFalse)
10194 {
glennrp0e8ea192010-12-24 18:00:33 +000010195 if (ping_need_colortype_warning != MagickFalse)
10196 {
10197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10198 " Image has transparency but tRNS chunk was excluded");
10199 }
10200
cristy3ed852e2009-09-05 21:47:34 +000010201 if (mng_info->write_png_depth)
10202 {
10203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010204 " Defined png:bit-depth=%u, Computed depth=%u",
cristy3ed852e2009-09-05 21:47:34 +000010205 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010206 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010207 }
glennrp0e8ea192010-12-24 18:00:33 +000010208
cristy3ed852e2009-09-05 21:47:34 +000010209 if (mng_info->write_png_colortype)
10210 {
10211 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010212 " Defined png:color-type=%u, Computed color type=%u",
cristy3ed852e2009-09-05 21:47:34 +000010213 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010214 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010215 }
10216 }
glennrp0e8ea192010-12-24 18:00:33 +000010217
glennrp3bd2e412010-08-10 13:34:52 +000010218 png_warning(ping,
cristy5d6fc9c2011-12-27 03:10:42 +000010219 "Cannot write image with defined png:bit-depth or png:color-type.");
cristy3ed852e2009-09-05 21:47:34 +000010220 }
10221
cristy8a46d822012-08-28 23:32:39 +000010222 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000010223 {
10224 /* Add an opaque matte channel */
cristyb0a657e2012-08-29 00:45:37 +000010225 image->alpha_trait = BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +000010226 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010227
glennrpb4a13412010-05-05 12:47:19 +000010228 if (logging != MagickFalse)
10229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10230 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010231 }
10232
glennrp0e319732011-01-25 21:53:13 +000010233 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010234 {
glennrp991d11d2010-11-12 21:55:28 +000010235 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010236 {
glennrp991d11d2010-11-12 21:55:28 +000010237 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010238 if (logging != MagickFalse)
10239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10240 " Setting ping_have_tRNS=MagickTrue.");
10241 }
glennrpe9c26dc2010-05-30 01:56:35 +000010242 }
10243
cristy3ed852e2009-09-05 21:47:34 +000010244 if (logging != MagickFalse)
10245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10246 " Writing PNG header chunks");
10247
glennrp5af765f2010-03-30 11:12:18 +000010248 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10249 ping_bit_depth,ping_color_type,
10250 ping_interlace_method,ping_compression_method,
10251 ping_filter_method);
10252
glennrp39992b42010-11-14 00:03:43 +000010253 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10254 {
glennrpf09bded2011-01-08 01:15:59 +000010255 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010256
glennrp3b51f0e2010-11-27 18:14:08 +000010257 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010258 {
glennrp8640fb52010-11-23 15:48:26 +000010259 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010260 {
glennrpd6bf1612010-12-17 17:28:54 +000010261 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010263 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10264 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010265 (int) palette[i].red,
10266 (int) palette[i].green,
10267 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010268 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010269 (int) ping_trans_alpha[i]);
10270 else
10271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010272 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010273 (int) i,
10274 (int) palette[i].red,
10275 (int) palette[i].green,
10276 (int) palette[i].blue);
10277 }
glennrp39992b42010-11-14 00:03:43 +000010278 }
glennrp39992b42010-11-14 00:03:43 +000010279 }
10280
glennrp26f37912010-12-23 16:22:42 +000010281 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010282 {
glennrp26f37912010-12-23 16:22:42 +000010283 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010284 {
glennrp26f37912010-12-23 16:22:42 +000010285 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010286 if (logging)
10287 {
10288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10289 " Setting up bKGD chunk");
10290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10291 " background color = (%d,%d,%d)",
10292 (int) ping_background.red,
10293 (int) ping_background.green,
10294 (int) ping_background.blue);
10295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10296 " index = %d, gray=%d",
10297 (int) ping_background.index,
10298 (int) ping_background.gray);
10299 }
10300 }
glennrp26f37912010-12-23 16:22:42 +000010301 }
10302
10303 if (ping_exclude_pHYs == MagickFalse)
10304 {
10305 if (ping_have_pHYs != MagickFalse)
10306 {
10307 png_set_pHYs(ping,ping_info,
10308 ping_pHYs_x_resolution,
10309 ping_pHYs_y_resolution,
10310 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010311
10312 if (logging)
10313 {
10314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10315 " Setting up pHYs chunk");
10316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10317 " x_resolution=%lu",
10318 (unsigned long) ping_pHYs_x_resolution);
10319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10320 " y_resolution=%lu",
10321 (unsigned long) ping_pHYs_y_resolution);
10322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10323 " unit_type=%lu",
10324 (unsigned long) ping_pHYs_unit_type);
10325 }
glennrp26f37912010-12-23 16:22:42 +000010326 }
glennrpdfd70802010-11-14 01:23:35 +000010327 }
10328
10329#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010330 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010331 {
glennrp26f37912010-12-23 16:22:42 +000010332 if (image->page.x || image->page.y)
10333 {
10334 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10335 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010336
glennrp26f37912010-12-23 16:22:42 +000010337 if (logging != MagickFalse)
10338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10339 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10340 (int) image->page.x, (int) image->page.y);
10341 }
glennrpdfd70802010-11-14 01:23:35 +000010342 }
10343#endif
10344
glennrpda8f3a72011-02-27 23:54:12 +000010345 if (mng_info->need_blob != MagickFalse)
10346 {
cristy16ea1392012-03-21 20:38:41 +000010347 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010348 MagickFalse)
10349 png_error(ping,"WriteBlob Failed");
10350
10351 ping_have_blob=MagickTrue;
10352 }
10353
cristy3ed852e2009-09-05 21:47:34 +000010354 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010355
glennrp39992b42010-11-14 00:03:43 +000010356 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010357 {
glennrp3b51f0e2010-11-27 18:14:08 +000010358 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010359 {
10360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10361 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10362 }
10363
10364 if (ping_color_type == 3)
10365 (void) png_set_tRNS(ping, ping_info,
10366 ping_trans_alpha,
10367 ping_num_trans,
10368 NULL);
10369
10370 else
10371 {
10372 (void) png_set_tRNS(ping, ping_info,
10373 NULL,
10374 0,
10375 &ping_trans_color);
10376
glennrp3b51f0e2010-11-27 18:14:08 +000010377 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010378 {
10379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010380 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010381 (int) ping_trans_color.red,
10382 (int) ping_trans_color.green,
10383 (int) ping_trans_color.blue);
10384 }
10385 }
glennrp991d11d2010-11-12 21:55:28 +000010386 }
10387
cristy3ed852e2009-09-05 21:47:34 +000010388 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010389 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010390
cristy3ed852e2009-09-05 21:47:34 +000010391 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010392
cristy3ed852e2009-09-05 21:47:34 +000010393 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010394 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010395
glennrp26f37912010-12-23 16:22:42 +000010396 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010397 {
glennrp4f25bd02011-01-01 18:51:28 +000010398 if ((image->page.width != 0 && image->page.width != image->columns) ||
10399 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010400 {
10401 unsigned char
10402 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010403
glennrp26f37912010-12-23 16:22:42 +000010404 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10405 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010406 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010407 PNGLong(chunk+4,(png_uint_32) image->page.width);
10408 PNGLong(chunk+8,(png_uint_32) image->page.height);
10409 chunk[12]=0; /* unit = pixels */
10410 (void) WriteBlob(image,13,chunk);
10411 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10412 }
cristy3ed852e2009-09-05 21:47:34 +000010413 }
10414
10415#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010416 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010417#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010418 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010419#undef PNG_HAVE_IDAT
10420#endif
10421
10422 png_set_packing(ping);
10423 /*
10424 Allocate memory.
10425 */
10426 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010427 if (image_depth > 8)
10428 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010429 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010430 {
glennrpb4a13412010-05-05 12:47:19 +000010431 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010432 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010433 break;
glennrp0fe50b42010-11-16 03:52:51 +000010434
glennrpb4a13412010-05-05 12:47:19 +000010435 case PNG_COLOR_TYPE_GRAY_ALPHA:
10436 rowbytes*=2;
10437 break;
glennrp0fe50b42010-11-16 03:52:51 +000010438
glennrpb4a13412010-05-05 12:47:19 +000010439 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010440 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010441 break;
glennrp0fe50b42010-11-16 03:52:51 +000010442
glennrpb4a13412010-05-05 12:47:19 +000010443 default:
10444 break;
cristy3ed852e2009-09-05 21:47:34 +000010445 }
glennrp3b51f0e2010-11-27 18:14:08 +000010446
10447 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010448 {
10449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10450 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010451
glennrpb4a13412010-05-05 12:47:19 +000010452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010453 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010454 }
glennrpcf002022011-01-30 02:38:15 +000010455 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10456 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010457
glennrpcf002022011-01-30 02:38:15 +000010458 if (ping_pixels == (unsigned char *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010459 png_error(ping,"Allocation of memory for pixels failed");
glennrp0fe50b42010-11-16 03:52:51 +000010460
cristy3ed852e2009-09-05 21:47:34 +000010461 /*
10462 Initialize image scanlines.
10463 */
cristyed552522009-10-16 14:04:35 +000010464 quantum_info=AcquireQuantumInfo(image_info,image);
10465 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010466 png_error(ping,"Memory allocation for quantum_info failed");
cristy3ed852e2009-09-05 21:47:34 +000010467 quantum_info->format=UndefinedQuantumFormat;
10468 quantum_info->depth=image_depth;
glennrp4b840d72012-11-22 16:01:16 +000010469 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
cristy3ed852e2009-09-05 21:47:34 +000010470 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010471
cristy3ed852e2009-09-05 21:47:34 +000010472 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010473 !mng_info->write_png32) &&
10474 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010475 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010476 image_matte == MagickFalse &&
10477 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010478 {
glennrp8bb3a022010-12-13 20:40:04 +000010479 /* Palette, Bilevel, or Opaque Monochrome */
cristy16ea1392012-03-21 20:38:41 +000010480 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010481 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010482
cristy3ed852e2009-09-05 21:47:34 +000010483 quantum_info->depth=8;
10484 for (pass=0; pass < num_passes; pass++)
10485 {
10486 /*
10487 Convert PseudoClass image to a PNG monochrome image.
10488 */
cristybb503372010-05-27 20:51:26 +000010489 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010490 {
glennrpd71e86a2011-02-24 01:28:37 +000010491 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10493 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010494
cristy16ea1392012-03-21 20:38:41 +000010495 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010496
cristy16ea1392012-03-21 20:38:41 +000010497 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010498 break;
glennrp0fe50b42010-11-16 03:52:51 +000010499
cristy3ed852e2009-09-05 21:47:34 +000010500 if (mng_info->IsPalette)
10501 {
cristy16ea1392012-03-21 20:38:41 +000010502 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10503 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010504 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10505 mng_info->write_png_depth &&
10506 mng_info->write_png_depth != old_bit_depth)
10507 {
10508 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010509 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010510 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010511 >> (8-old_bit_depth));
10512 }
10513 }
glennrp0fe50b42010-11-16 03:52:51 +000010514
cristy3ed852e2009-09-05 21:47:34 +000010515 else
10516 {
cristy16ea1392012-03-21 20:38:41 +000010517 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10518 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010519 }
glennrp0fe50b42010-11-16 03:52:51 +000010520
cristy3ed852e2009-09-05 21:47:34 +000010521 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010522 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010523 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010524 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010525
glennrp3b51f0e2010-11-27 18:14:08 +000010526 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10528 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010529
glennrpcf002022011-01-30 02:38:15 +000010530 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010531 }
10532 if (image->previous == (Image *) NULL)
10533 {
10534 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10535 if (status == MagickFalse)
10536 break;
10537 }
10538 }
10539 }
glennrp0fe50b42010-11-16 03:52:51 +000010540
glennrp8bb3a022010-12-13 20:40:04 +000010541 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010542 {
glennrp0fe50b42010-11-16 03:52:51 +000010543 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010544 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010545 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010546 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010547 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010548 {
cristy16ea1392012-03-21 20:38:41 +000010549 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010550 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010551
glennrp8bb3a022010-12-13 20:40:04 +000010552 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010553 {
glennrp8bb3a022010-12-13 20:40:04 +000010554
cristybb503372010-05-27 20:51:26 +000010555 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010556 {
cristy16ea1392012-03-21 20:38:41 +000010557 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010558
cristy16ea1392012-03-21 20:38:41 +000010559 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010560 break;
glennrp2cc891a2010-12-24 13:44:32 +000010561
glennrp5af765f2010-03-30 11:12:18 +000010562 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010563 {
glennrp8bb3a022010-12-13 20:40:04 +000010564 if (mng_info->IsPalette)
cristy16ea1392012-03-21 20:38:41 +000010565 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10566 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010567
glennrp8bb3a022010-12-13 20:40:04 +000010568 else
cristy16ea1392012-03-21 20:38:41 +000010569 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10570 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010571
glennrp3b51f0e2010-11-27 18:14:08 +000010572 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010574 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010575 }
glennrp2cc891a2010-12-24 13:44:32 +000010576
glennrp8bb3a022010-12-13 20:40:04 +000010577 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10578 {
10579 if (logging != MagickFalse && y == 0)
10580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10581 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010582
cristy16ea1392012-03-21 20:38:41 +000010583 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10584 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010585 }
glennrp2cc891a2010-12-24 13:44:32 +000010586
glennrp3b51f0e2010-11-27 18:14:08 +000010587 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010589 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010590
glennrpcf002022011-01-30 02:38:15 +000010591 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010592 }
glennrp2cc891a2010-12-24 13:44:32 +000010593
glennrp8bb3a022010-12-13 20:40:04 +000010594 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010595 {
glennrp8bb3a022010-12-13 20:40:04 +000010596 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10597 if (status == MagickFalse)
10598 break;
cristy3ed852e2009-09-05 21:47:34 +000010599 }
cristy3ed852e2009-09-05 21:47:34 +000010600 }
10601 }
glennrp8bb3a022010-12-13 20:40:04 +000010602
10603 else
10604 {
cristy16ea1392012-03-21 20:38:41 +000010605 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010606 *p;
10607
10608 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010609 {
glennrp8bb3a022010-12-13 20:40:04 +000010610 if ((image_depth > 8) || (mng_info->write_png24 ||
10611 mng_info->write_png32 ||
10612 (!mng_info->write_png8 && !mng_info->IsPalette)))
10613 {
10614 for (y=0; y < (ssize_t) image->rows; y++)
10615 {
cristy862a33c2012-05-17 22:49:37 +000010616 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010617
cristy16ea1392012-03-21 20:38:41 +000010618 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010619 break;
glennrp2cc891a2010-12-24 13:44:32 +000010620
glennrp8bb3a022010-12-13 20:40:04 +000010621 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10622 {
10623 if (image->storage_class == DirectClass)
cristy16ea1392012-03-21 20:38:41 +000010624 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10625 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010626
glennrp8bb3a022010-12-13 20:40:04 +000010627 else
cristy16ea1392012-03-21 20:38:41 +000010628 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10629 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010630 }
glennrp2cc891a2010-12-24 13:44:32 +000010631
glennrp8bb3a022010-12-13 20:40:04 +000010632 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10633 {
cristy16ea1392012-03-21 20:38:41 +000010634 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010635 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000010636 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010637
glennrp8bb3a022010-12-13 20:40:04 +000010638 if (logging != MagickFalse && y == 0)
10639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10640 " Writing GRAY_ALPHA PNG pixels (3)");
10641 }
glennrp2cc891a2010-12-24 13:44:32 +000010642
glennrp8bb3a022010-12-13 20:40:04 +000010643 else if (image_matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +000010644 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10645 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010646
glennrp8bb3a022010-12-13 20:40:04 +000010647 else
cristy16ea1392012-03-21 20:38:41 +000010648 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10649 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010650
glennrp8bb3a022010-12-13 20:40:04 +000010651 if (logging != MagickFalse && y == 0)
10652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10653 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010654
glennrpcf002022011-01-30 02:38:15 +000010655 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010656 }
10657 }
glennrp2cc891a2010-12-24 13:44:32 +000010658
glennrp8bb3a022010-12-13 20:40:04 +000010659 else
10660 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10661 mng_info->write_png32 ||
10662 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10663 {
10664 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10665 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10666 {
10667 if (logging != MagickFalse)
10668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10669 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010670
glennrp8bb3a022010-12-13 20:40:04 +000010671 quantum_info->depth=8;
10672 image_depth=8;
10673 }
glennrp2cc891a2010-12-24 13:44:32 +000010674
glennrp8bb3a022010-12-13 20:40:04 +000010675 for (y=0; y < (ssize_t) image->rows; y++)
10676 {
10677 if (logging != MagickFalse && y == 0)
10678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10679 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010680
cristy16ea1392012-03-21 20:38:41 +000010681 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010682
cristy16ea1392012-03-21 20:38:41 +000010683 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010684 break;
glennrp2cc891a2010-12-24 13:44:32 +000010685
glennrp8bb3a022010-12-13 20:40:04 +000010686 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010687 {
glennrp4bf89732011-03-21 13:48:28 +000010688 quantum_info->depth=image->depth;
10689
cristy16ea1392012-03-21 20:38:41 +000010690 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10691 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010692 }
glennrp2cc891a2010-12-24 13:44:32 +000010693
glennrp8bb3a022010-12-13 20:40:04 +000010694 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10695 {
10696 if (logging != MagickFalse && y == 0)
10697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10698 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010699
cristy16ea1392012-03-21 20:38:41 +000010700 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010701 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000010702 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010703 }
glennrp2cc891a2010-12-24 13:44:32 +000010704
glennrp8bb3a022010-12-13 20:40:04 +000010705 else
glennrp8bb3a022010-12-13 20:40:04 +000010706 {
cristy16ea1392012-03-21 20:38:41 +000010707 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10708 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010709
10710 if (logging != MagickFalse && y <= 2)
10711 {
10712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010713 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010714
10715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10716 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10717 (int)ping_pixels[0],(int)ping_pixels[1]);
10718 }
glennrp8bb3a022010-12-13 20:40:04 +000010719 }
glennrpcf002022011-01-30 02:38:15 +000010720 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010721 }
10722 }
glennrp2cc891a2010-12-24 13:44:32 +000010723
glennrp8bb3a022010-12-13 20:40:04 +000010724 if (image->previous == (Image *) NULL)
10725 {
10726 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10727 if (status == MagickFalse)
10728 break;
10729 }
cristy3ed852e2009-09-05 21:47:34 +000010730 }
glennrp8bb3a022010-12-13 20:40:04 +000010731 }
10732 }
10733
cristyb32b90a2009-09-07 21:45:48 +000010734 if (quantum_info != (QuantumInfo *) NULL)
10735 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010736
10737 if (logging != MagickFalse)
10738 {
10739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010740 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010741
cristy3ed852e2009-09-05 21:47:34 +000010742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010743 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010744
cristy3ed852e2009-09-05 21:47:34 +000010745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010746 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010747
cristy3ed852e2009-09-05 21:47:34 +000010748 if (mng_info->write_png_depth)
10749 {
10750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010751 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010752 }
glennrp0fe50b42010-11-16 03:52:51 +000010753
cristy3ed852e2009-09-05 21:47:34 +000010754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010755 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010756
cristy3ed852e2009-09-05 21:47:34 +000010757 if (mng_info->write_png_colortype)
10758 {
10759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010760 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010761 }
glennrp0fe50b42010-11-16 03:52:51 +000010762
cristy3ed852e2009-09-05 21:47:34 +000010763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010764 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010765
cristy3ed852e2009-09-05 21:47:34 +000010766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010767 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010768 }
10769 /*
glennrpa0ed0092011-04-18 16:36:29 +000010770 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010771 */
glennrp823b55c2011-03-14 18:46:46 +000010772 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010773 {
glennrp26f37912010-12-23 16:22:42 +000010774 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010775 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010776 while (property != (const char *) NULL)
10777 {
10778 png_textp
10779 text;
glennrp2cc891a2010-12-24 13:44:32 +000010780
cristy16ea1392012-03-21 20:38:41 +000010781 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010782
10783 /* Don't write any "png:" properties; those are just for "identify" */
10784 if (LocaleNCompare(property,"png:",4) != 0 &&
10785
10786 /* Suppress density and units if we wrote a pHYs chunk */
10787 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010788 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010789 LocaleCompare(property,"units") != 0) &&
10790
10791 /* Suppress the IM-generated Date:create and Date:modify */
10792 (ping_exclude_date == MagickFalse ||
10793 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010794 {
glennrpc70af4a2011-03-07 00:08:23 +000010795 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010796 {
cristya865ccd2012-07-28 00:33:10 +000010797
10798#if PNG_LIBPNG_VER >= 14000
10799 text=(png_textp) png_malloc(ping,
10800 (png_alloc_size_t) sizeof(png_text));
10801#else
10802 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
10803#endif
glennrpc70af4a2011-03-07 00:08:23 +000010804 text[0].key=(char *) property;
10805 text[0].text=(char *) value;
10806 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010807
glennrpc70af4a2011-03-07 00:08:23 +000010808 if (ping_exclude_tEXt != MagickFalse)
10809 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10810
10811 else if (ping_exclude_zTXt != MagickFalse)
10812 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10813
10814 else
glennrp26f37912010-12-23 16:22:42 +000010815 {
glennrpc70af4a2011-03-07 00:08:23 +000010816 text[0].compression=image_info->compression == NoCompression ||
10817 (image_info->compression == UndefinedCompression &&
10818 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10819 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010820 }
glennrp2cc891a2010-12-24 13:44:32 +000010821
glennrpc70af4a2011-03-07 00:08:23 +000010822 if (logging != MagickFalse)
10823 {
10824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10825 " Setting up text chunk");
10826
10827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10828 " keyword: %s",text[0].key);
10829 }
10830
10831 png_set_text(ping,ping_info,text,1);
10832 png_free(ping,text);
10833 }
glennrp26f37912010-12-23 16:22:42 +000010834 }
10835 property=GetNextImageProperty(image);
10836 }
cristy3ed852e2009-09-05 21:47:34 +000010837 }
10838
10839 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010840 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010841
10842 if (logging != MagickFalse)
10843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10844 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010845
cristy3ed852e2009-09-05 21:47:34 +000010846 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010847
cristy3ed852e2009-09-05 21:47:34 +000010848 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10849 {
10850 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010851 (ping_width != mng_info->page.width) ||
10852 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010853 {
10854 unsigned char
10855 chunk[32];
10856
10857 /*
10858 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10859 */
10860 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10861 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010862 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010863 chunk[4]=4;
10864 chunk[5]=0; /* frame name separator (no name) */
10865 chunk[6]=1; /* flag for changing delay, for next frame only */
10866 chunk[7]=0; /* flag for changing frame timeout */
10867 chunk[8]=1; /* flag for changing frame clipping for next frame */
10868 chunk[9]=0; /* flag for changing frame sync_id */
10869 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10870 chunk[14]=0; /* clipping boundaries delta type */
10871 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10872 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010873 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010874 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10875 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010876 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010877 (void) WriteBlob(image,31,chunk);
10878 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10879 mng_info->old_framing_mode=4;
10880 mng_info->framing_mode=1;
10881 }
glennrp0fe50b42010-11-16 03:52:51 +000010882
cristy3ed852e2009-09-05 21:47:34 +000010883 else
10884 mng_info->framing_mode=3;
10885 }
10886 if (mng_info->write_mng && !mng_info->need_fram &&
10887 ((int) image->dispose == 3))
glennrpedaa0382012-04-12 14:16:21 +000010888 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
glennrp0fe50b42010-11-16 03:52:51 +000010889
cristy3ed852e2009-09-05 21:47:34 +000010890 /*
10891 Free PNG resources.
10892 */
glennrp5af765f2010-03-30 11:12:18 +000010893
cristy3ed852e2009-09-05 21:47:34 +000010894 png_destroy_write_struct(&ping,&ping_info);
10895
glennrpcf002022011-01-30 02:38:15 +000010896 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010897
cristy16ea1392012-03-21 20:38:41 +000010898 if (ping_have_blob != MagickFalse)
10899 (void) CloseBlob(image);
10900
10901 image_info=DestroyImageInfo(image_info);
10902 image=DestroyImage(image);
10903
glennrpb9cfe272010-12-21 15:08:06 +000010904 /* Store bit depth actually written */
10905 s[0]=(char) ping_bit_depth;
10906 s[1]='\0';
10907
cristy16ea1392012-03-21 20:38:41 +000010908 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010909
cristy3ed852e2009-09-05 21:47:34 +000010910 if (logging != MagickFalse)
10911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10912 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010913
glennrpedaa0382012-04-12 14:16:21 +000010914#ifdef PNG_SETJMP_NOT_THREAD_SAFE
10915 UnlockSemaphoreInfo(ping_semaphore);
10916#endif
10917
10918 /* } for navigation to beginning of SETJMP-protected block. Revert to
10919 * Throwing an Exception when an error occurs.
10920 */
10921
cristy3ed852e2009-09-05 21:47:34 +000010922 return(MagickTrue);
10923/* End write one PNG image */
glennrpedaa0382012-04-12 14:16:21 +000010924
cristy3ed852e2009-09-05 21:47:34 +000010925}
10926
10927/*
10928%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10929% %
10930% %
10931% %
10932% W r i t e P N G I m a g e %
10933% %
10934% %
10935% %
10936%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10937%
10938% WritePNGImage() writes a Portable Network Graphics (PNG) or
10939% Multiple-image Network Graphics (MNG) image file.
10940%
10941% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10942%
10943% The format of the WritePNGImage method is:
10944%
cristy16ea1392012-03-21 20:38:41 +000010945% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10946% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010947%
10948% A description of each parameter follows:
10949%
10950% o image_info: the image info.
10951%
10952% o image: The image.
10953%
cristy16ea1392012-03-21 20:38:41 +000010954% o exception: return any errors or warnings in this structure.
10955%
cristy3ed852e2009-09-05 21:47:34 +000010956% Returns MagickTrue on success, MagickFalse on failure.
10957%
10958% Communicating with the PNG encoder:
10959%
10960% While the datastream written is always in PNG format and normally would
10961% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000010962% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000010963%
glennrp5a39f372011-02-25 04:52:16 +000010964% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10965% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010966% is present, the tRNS chunk must only have values 0 and 255
10967% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010968% transparent). If other values are present they will be
10969% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010970% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010971% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10972% of any resulting fully-transparent pixels is changed to
10973% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010974%
10975% If you want better quantization or dithering of the colors
10976% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010977% PNG encoder. The pixels contain 8-bit indices even if
10978% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010979% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010980% PNG grayscale type might be slightly more efficient. Please
10981% note that writing to the PNG8 format may result in loss
10982% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010983%
10984% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10985% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010986% one of the colors as transparent. The only loss incurred
10987% is reduction of sample depth to 8. If the image has more
10988% than one transparent color, has semitransparent pixels, or
10989% has an opaque pixel with the same RGB components as the
10990% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010991%
10992% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10993% transparency is permitted, i.e., the alpha sample for
10994% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010995% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010996% The only loss in data is the reduction of the sample depth
10997% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010998%
10999% o -define: For more precise control of the PNG output, you can use the
11000% Image options "png:bit-depth" and "png:color-type". These
11001% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000011002% from the application programming interfaces. The options
11003% are case-independent and are converted to lowercase before
11004% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000011005%
11006% png:color-type can be 0, 2, 3, 4, or 6.
11007%
11008% When png:color-type is 0 (Grayscale), png:bit-depth can
11009% be 1, 2, 4, 8, or 16.
11010%
11011% When png:color-type is 2 (RGB), png:bit-depth can
11012% be 8 or 16.
11013%
11014% When png:color-type is 3 (Indexed), png:bit-depth can
11015% be 1, 2, 4, or 8. This refers to the number of bits
11016% used to store the index. The color samples always have
11017% bit-depth 8 in indexed PNG files.
11018%
11019% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11020% png:bit-depth can be 8 or 16.
11021%
glennrp5a39f372011-02-25 04:52:16 +000011022% If the image cannot be written without loss with the requested bit-depth
11023% and color-type, a PNG file will not be written, and the encoder will
11024% return MagickFalse.
11025%
cristy3ed852e2009-09-05 21:47:34 +000011026% Since image encoders should not be responsible for the "heavy lifting",
11027% the user should make sure that ImageMagick has already reduced the
11028% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000011029% transparency prior to attempting to write the image with depth, color,
cristy16ea1392012-03-21 20:38:41 +000011030% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000011031%
cristy3ed852e2009-09-05 21:47:34 +000011032% Note that another definition, "png:bit-depth-written" exists, but it
11033% is not intended for external use. It is only used internally by the
11034% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11035%
11036% It is possible to request that the PNG encoder write previously-formatted
11037% ancillary chunks in the output PNG file, using the "-profile" commandline
11038% option as shown below or by setting the profile via a programming
11039% interface:
11040%
11041% -profile PNG-chunk-x:<file>
11042%
11043% where x is a location flag and <file> is a file containing the chunk
11044% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000011045% This encoder will compute the chunk length and CRC, so those must not
11046% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000011047%
11048% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11049% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11050% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000011051% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000011052%
glennrpbb8a7332010-11-13 15:17:35 +000011053% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000011054%
glennrp3241bd02010-12-12 04:36:28 +000011055% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000011056%
glennrpd6afd542010-11-19 01:53:05 +000011057% o 32-bit depth is reduced to 16.
11058% o 16-bit depth is reduced to 8 if all pixels contain samples whose
11059% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000011060% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000011061% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000011062% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000011063% o Grayscale images are reduced to 1, 2, or 4 bit depth if
11064% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000011065% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000011066% o If matte channel is present but only one transparent color is
11067% present, RGB+tRNS is written instead of RGBA
11068% o Opaque matte channel is removed (or added, if color-type 4 or 6
11069% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000011070%
cristy3ed852e2009-09-05 21:47:34 +000011071%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11072*/
11073static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy16ea1392012-03-21 20:38:41 +000011074 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011075{
11076 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011077 excluding,
11078 logging,
11079 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011080 status;
11081
11082 MngInfo
11083 *mng_info;
11084
11085 const char
11086 *value;
11087
11088 int
glennrp21f0e622011-01-07 16:20:57 +000011089 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000011090 source;
11091
cristy3ed852e2009-09-05 21:47:34 +000011092 /*
11093 Open image file.
11094 */
11095 assert(image_info != (const ImageInfo *) NULL);
11096 assert(image_info->signature == MagickSignature);
11097 assert(image != (Image *) NULL);
11098 assert(image->signature == MagickSignature);
11099 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011100 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011101 /*
11102 Allocate a MngInfo structure.
11103 */
11104 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011105 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000011106
cristy3ed852e2009-09-05 21:47:34 +000011107 if (mng_info == (MngInfo *) NULL)
11108 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011109
cristy3ed852e2009-09-05 21:47:34 +000011110 /*
11111 Initialize members of the MngInfo structure.
11112 */
11113 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11114 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000011115 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011116 have_mng_structure=MagickTrue;
11117
11118 /* See if user has requested a specific PNG subformat */
11119
11120 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11121 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11122 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11123
glennrpb381a262012-02-11 17:49:49 +000011124 value=GetImageOption(image_info,"png:format");
11125
11126 if (value != (char *) NULL)
11127 {
11128 if (LocaleCompare(value,"png8") == 0)
11129 {
11130 mng_info->write_png8 = MagickTrue;
11131 mng_info->write_png24 = MagickFalse;
11132 mng_info->write_png32 = MagickFalse;
11133 }
11134
11135 else if (LocaleCompare(value,"png24") == 0)
11136 {
11137 mng_info->write_png8 = MagickFalse;
11138 mng_info->write_png24 = MagickTrue;
11139 mng_info->write_png32 = MagickFalse;
11140 }
11141
11142 else if (LocaleCompare(value,"png32") == 0)
11143 {
11144 mng_info->write_png8 = MagickFalse;
11145 mng_info->write_png24 = MagickFalse;
11146 mng_info->write_png32 = MagickTrue;
11147 }
11148 }
cristy3ed852e2009-09-05 21:47:34 +000011149 if (mng_info->write_png8)
11150 {
glennrp9c1eb072010-06-06 22:19:15 +000011151 mng_info->write_png_colortype = /* 3 */ 4;
11152 mng_info->write_png_depth = 8;
11153 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011154 }
11155
11156 if (mng_info->write_png24)
11157 {
glennrp9c1eb072010-06-06 22:19:15 +000011158 mng_info->write_png_colortype = /* 2 */ 3;
11159 mng_info->write_png_depth = 8;
11160 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011161
cristy8a46d822012-08-28 23:32:39 +000011162 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +000011163 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011164
glennrp9c1eb072010-06-06 22:19:15 +000011165 else
cristy16ea1392012-03-21 20:38:41 +000011166 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011167
cristy16ea1392012-03-21 20:38:41 +000011168 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011169 }
11170
11171 if (mng_info->write_png32)
11172 {
glennrp9c1eb072010-06-06 22:19:15 +000011173 mng_info->write_png_colortype = /* 6 */ 7;
11174 mng_info->write_png_depth = 8;
11175 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011176
cristy8a46d822012-08-28 23:32:39 +000011177 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +000011178 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011179
glennrp9c1eb072010-06-06 22:19:15 +000011180 else
cristy16ea1392012-03-21 20:38:41 +000011181 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011182
cristy16ea1392012-03-21 20:38:41 +000011183 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011184 }
11185
11186 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011187
cristy3ed852e2009-09-05 21:47:34 +000011188 if (value != (char *) NULL)
11189 {
11190 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011191 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011192
cristy3ed852e2009-09-05 21:47:34 +000011193 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011194 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011195
cristy3ed852e2009-09-05 21:47:34 +000011196 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011197 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011198
cristy3ed852e2009-09-05 21:47:34 +000011199 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011200 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011201
cristy3ed852e2009-09-05 21:47:34 +000011202 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011203 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011204
glennrpbb8a7332010-11-13 15:17:35 +000011205 else
cristy16ea1392012-03-21 20:38:41 +000011206 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011207 GetMagickModule(),CoderWarning,
11208 "ignoring invalid defined png:bit-depth",
11209 "=%s",value);
11210
cristy3ed852e2009-09-05 21:47:34 +000011211 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011213 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011214 }
glennrp0fe50b42010-11-16 03:52:51 +000011215
cristy3ed852e2009-09-05 21:47:34 +000011216 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011217
cristy3ed852e2009-09-05 21:47:34 +000011218 if (value != (char *) NULL)
11219 {
11220 /* We must store colortype+1 because 0 is a valid colortype */
11221 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011222 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011223
cristy16ea1392012-03-21 20:38:41 +000011224 else if (LocaleCompare(value,"1") == 0)
11225 mng_info->write_png_colortype = 2;
11226
cristy3ed852e2009-09-05 21:47:34 +000011227 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011228 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011229
cristy3ed852e2009-09-05 21:47:34 +000011230 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011231 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011232
cristy3ed852e2009-09-05 21:47:34 +000011233 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011234 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011235
cristy3ed852e2009-09-05 21:47:34 +000011236 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011237 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011238
glennrpbb8a7332010-11-13 15:17:35 +000011239 else
cristy16ea1392012-03-21 20:38:41 +000011240 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011241 GetMagickModule(),CoderWarning,
11242 "ignoring invalid defined png:color-type",
11243 "=%s",value);
11244
cristy3ed852e2009-09-05 21:47:34 +000011245 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011247 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011248 }
11249
glennrp0e8ea192010-12-24 18:00:33 +000011250 /* Check for chunks to be excluded:
11251 *
glennrp0dff56c2011-01-29 19:10:02 +000011252 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011253 * listed in the "unused_chunks" array, above.
11254 *
cristy5d6fc9c2011-12-27 03:10:42 +000011255 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011256 * define (in the image properties or in the image artifacts)
11257 * or via a mng_info member. For convenience, in addition
11258 * to or instead of a comma-separated list of chunks, the
11259 * "exclude-chunk" string can be simply "all" or "none".
11260 *
11261 * The exclude-chunk define takes priority over the mng_info.
11262 *
cristy5d6fc9c2011-12-27 03:10:42 +000011263 * A "png:include-chunk" define takes priority over both the
11264 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011265 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011266 * well as a comma-separated list. Chunks that are unknown to
11267 * ImageMagick are always excluded, regardless of their "copy-safe"
11268 * status according to the PNG specification, and even if they
glennrpaa192b12012-01-17 21:35:21 +000011269 * appear in the "include-chunk" list. Such defines appearing among
11270 * the image options take priority over those found among the image
11271 * artifacts.
glennrp0e8ea192010-12-24 18:00:33 +000011272 *
11273 * Finally, all chunks listed in the "unused_chunks" array are
11274 * automatically excluded, regardless of the other instructions
11275 * or lack thereof.
11276 *
11277 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11278 * will not be written and the gAMA chunk will only be written if it
11279 * is not between .45 and .46, or approximately (1.0/2.2).
11280 *
11281 * If you exclude tRNS and the image has transparency, the colortype
11282 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11283 *
11284 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011285 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011286 */
11287
glennrp26f37912010-12-23 16:22:42 +000011288 mng_info->ping_exclude_bKGD=MagickFalse;
11289 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011290 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011291 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11292 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011293 mng_info->ping_exclude_iCCP=MagickFalse;
11294 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11295 mng_info->ping_exclude_oFFs=MagickFalse;
11296 mng_info->ping_exclude_pHYs=MagickFalse;
11297 mng_info->ping_exclude_sRGB=MagickFalse;
11298 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011299 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011300 mng_info->ping_exclude_vpAg=MagickFalse;
11301 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11302 mng_info->ping_exclude_zTXt=MagickFalse;
11303
glennrp8d3d6e52011-04-19 04:39:51 +000011304 mng_info->ping_preserve_colormap=MagickFalse;
11305
11306 value=GetImageArtifact(image,"png:preserve-colormap");
11307 if (value == NULL)
11308 value=GetImageOption(image_info,"png:preserve-colormap");
11309 if (value != NULL)
11310 mng_info->ping_preserve_colormap=MagickTrue;
11311
glennrp18682582011-06-30 18:11:47 +000011312 /* Thes compression-level, compression-strategy, and compression-filter
11313 * defines take precedence over values from the -quality option.
11314 */
11315 value=GetImageArtifact(image,"png:compression-level");
11316 if (value == NULL)
11317 value=GetImageOption(image_info,"png:compression-level");
11318 if (value != NULL)
11319 {
glennrp18682582011-06-30 18:11:47 +000011320 /* We have to add 1 to everything because 0 is a valid input,
11321 * and we want to use 0 (the default) to mean undefined.
11322 */
11323 if (LocaleCompare(value,"0") == 0)
11324 mng_info->write_png_compression_level = 1;
11325
glennrp0ffb95c2012-01-30 21:16:22 +000011326 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011327 mng_info->write_png_compression_level = 2;
11328
11329 else if (LocaleCompare(value,"2") == 0)
11330 mng_info->write_png_compression_level = 3;
11331
11332 else if (LocaleCompare(value,"3") == 0)
11333 mng_info->write_png_compression_level = 4;
11334
11335 else if (LocaleCompare(value,"4") == 0)
11336 mng_info->write_png_compression_level = 5;
11337
11338 else if (LocaleCompare(value,"5") == 0)
11339 mng_info->write_png_compression_level = 6;
11340
11341 else if (LocaleCompare(value,"6") == 0)
11342 mng_info->write_png_compression_level = 7;
11343
11344 else if (LocaleCompare(value,"7") == 0)
11345 mng_info->write_png_compression_level = 8;
11346
11347 else if (LocaleCompare(value,"8") == 0)
11348 mng_info->write_png_compression_level = 9;
11349
11350 else if (LocaleCompare(value,"9") == 0)
11351 mng_info->write_png_compression_level = 10;
11352
11353 else
cristy16ea1392012-03-21 20:38:41 +000011354 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011355 GetMagickModule(),CoderWarning,
11356 "ignoring invalid defined png:compression-level",
11357 "=%s",value);
11358 }
11359
11360 value=GetImageArtifact(image,"png:compression-strategy");
11361 if (value == NULL)
11362 value=GetImageOption(image_info,"png:compression-strategy");
11363 if (value != NULL)
11364 {
11365
11366 if (LocaleCompare(value,"0") == 0)
11367 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11368
11369 else if (LocaleCompare(value,"1") == 0)
11370 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11371
11372 else if (LocaleCompare(value,"2") == 0)
11373 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11374
11375 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011376#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011377 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011378#else
11379 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11380#endif
glennrp18682582011-06-30 18:11:47 +000011381
11382 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011383#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011384 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011385#else
11386 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11387#endif
glennrp18682582011-06-30 18:11:47 +000011388
11389 else
cristy16ea1392012-03-21 20:38:41 +000011390 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011391 GetMagickModule(),CoderWarning,
11392 "ignoring invalid defined png:compression-strategy",
11393 "=%s",value);
11394 }
11395
11396 value=GetImageArtifact(image,"png:compression-filter");
11397 if (value == NULL)
11398 value=GetImageOption(image_info,"png:compression-filter");
11399 if (value != NULL)
11400 {
11401
11402 /* To do: combinations of filters allowed by libpng
11403 * masks 0x08 through 0xf8
11404 *
11405 * Implement this as a comma-separated list of 0,1,2,3,4,5
11406 * where 5 is a special case meaning PNG_ALL_FILTERS.
11407 */
11408
11409 if (LocaleCompare(value,"0") == 0)
11410 mng_info->write_png_compression_filter = 1;
11411
cristyb19b8122012-10-22 11:03:30 +000011412 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011413 mng_info->write_png_compression_filter = 2;
11414
11415 else if (LocaleCompare(value,"2") == 0)
11416 mng_info->write_png_compression_filter = 3;
11417
11418 else if (LocaleCompare(value,"3") == 0)
11419 mng_info->write_png_compression_filter = 4;
11420
11421 else if (LocaleCompare(value,"4") == 0)
11422 mng_info->write_png_compression_filter = 5;
11423
11424 else if (LocaleCompare(value,"5") == 0)
11425 mng_info->write_png_compression_filter = 6;
11426
glennrp18682582011-06-30 18:11:47 +000011427 else
cristy16ea1392012-03-21 20:38:41 +000011428 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011429 GetMagickModule(),CoderWarning,
11430 "ignoring invalid defined png:compression-filter",
11431 "=%s",value);
11432 }
11433
glennrp03812ae2010-12-24 01:31:34 +000011434 excluding=MagickFalse;
11435
glennrp5c7cf4e2010-12-24 00:30:00 +000011436 for (source=0; source<1; source++)
11437 {
11438 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011439 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011440 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011441
11442 if (value == NULL)
11443 value=GetImageArtifact(image,"png:exclude-chunks");
11444 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011445 else
glennrpacba0042010-12-24 14:27:26 +000011446 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011447 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011448
glennrpacba0042010-12-24 14:27:26 +000011449 if (value == NULL)
11450 value=GetImageOption(image_info,"png:exclude-chunks");
11451 }
11452
glennrp03812ae2010-12-24 01:31:34 +000011453 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011454 {
glennrp03812ae2010-12-24 01:31:34 +000011455
11456 size_t
11457 last;
11458
11459 excluding=MagickTrue;
11460
11461 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011462 {
11463 if (source == 0)
11464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11465 " png:exclude-chunk=%s found in image artifacts.\n", value);
11466 else
11467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11468 " png:exclude-chunk=%s found in image properties.\n", value);
11469 }
glennrp03812ae2010-12-24 01:31:34 +000011470
11471 last=strlen(value);
11472
11473 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011474 {
glennrp03812ae2010-12-24 01:31:34 +000011475
11476 if (LocaleNCompare(value+i,"all",3) == 0)
11477 {
11478 mng_info->ping_exclude_bKGD=MagickTrue;
11479 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011480 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011481 mng_info->ping_exclude_EXIF=MagickTrue;
11482 mng_info->ping_exclude_gAMA=MagickTrue;
11483 mng_info->ping_exclude_iCCP=MagickTrue;
11484 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11485 mng_info->ping_exclude_oFFs=MagickTrue;
11486 mng_info->ping_exclude_pHYs=MagickTrue;
11487 mng_info->ping_exclude_sRGB=MagickTrue;
11488 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011489 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011490 mng_info->ping_exclude_vpAg=MagickTrue;
11491 mng_info->ping_exclude_zCCP=MagickTrue;
11492 mng_info->ping_exclude_zTXt=MagickTrue;
11493 i--;
11494 }
glennrp2cc891a2010-12-24 13:44:32 +000011495
glennrp03812ae2010-12-24 01:31:34 +000011496 if (LocaleNCompare(value+i,"none",4) == 0)
11497 {
11498 mng_info->ping_exclude_bKGD=MagickFalse;
11499 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011500 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011501 mng_info->ping_exclude_EXIF=MagickFalse;
11502 mng_info->ping_exclude_gAMA=MagickFalse;
11503 mng_info->ping_exclude_iCCP=MagickFalse;
11504 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11505 mng_info->ping_exclude_oFFs=MagickFalse;
11506 mng_info->ping_exclude_pHYs=MagickFalse;
11507 mng_info->ping_exclude_sRGB=MagickFalse;
11508 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011509 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011510 mng_info->ping_exclude_vpAg=MagickFalse;
11511 mng_info->ping_exclude_zCCP=MagickFalse;
11512 mng_info->ping_exclude_zTXt=MagickFalse;
11513 }
glennrp2cc891a2010-12-24 13:44:32 +000011514
glennrp03812ae2010-12-24 01:31:34 +000011515 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11516 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011517
glennrp03812ae2010-12-24 01:31:34 +000011518 if (LocaleNCompare(value+i,"chrm",4) == 0)
11519 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011520
glennrpa0ed0092011-04-18 16:36:29 +000011521 if (LocaleNCompare(value+i,"date",4) == 0)
11522 mng_info->ping_exclude_date=MagickTrue;
11523
glennrp03812ae2010-12-24 01:31:34 +000011524 if (LocaleNCompare(value+i,"exif",4) == 0)
11525 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011526
glennrp03812ae2010-12-24 01:31:34 +000011527 if (LocaleNCompare(value+i,"gama",4) == 0)
11528 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011529
glennrp03812ae2010-12-24 01:31:34 +000011530 if (LocaleNCompare(value+i,"iccp",4) == 0)
11531 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011532
glennrp03812ae2010-12-24 01:31:34 +000011533 /*
11534 if (LocaleNCompare(value+i,"itxt",4) == 0)
11535 mng_info->ping_exclude_iTXt=MagickTrue;
11536 */
glennrp2cc891a2010-12-24 13:44:32 +000011537
glennrp03812ae2010-12-24 01:31:34 +000011538 if (LocaleNCompare(value+i,"gama",4) == 0)
11539 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011540
glennrp03812ae2010-12-24 01:31:34 +000011541 if (LocaleNCompare(value+i,"offs",4) == 0)
11542 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011543
glennrp03812ae2010-12-24 01:31:34 +000011544 if (LocaleNCompare(value+i,"phys",4) == 0)
11545 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011546
glennrpa1e3b7b2010-12-24 16:37:33 +000011547 if (LocaleNCompare(value+i,"srgb",4) == 0)
11548 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011549
glennrp03812ae2010-12-24 01:31:34 +000011550 if (LocaleNCompare(value+i,"text",4) == 0)
11551 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011552
glennrpa1e3b7b2010-12-24 16:37:33 +000011553 if (LocaleNCompare(value+i,"trns",4) == 0)
11554 mng_info->ping_exclude_tRNS=MagickTrue;
11555
glennrp03812ae2010-12-24 01:31:34 +000011556 if (LocaleNCompare(value+i,"vpag",4) == 0)
11557 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011558
glennrp03812ae2010-12-24 01:31:34 +000011559 if (LocaleNCompare(value+i,"zccp",4) == 0)
11560 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011561
glennrp03812ae2010-12-24 01:31:34 +000011562 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11563 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011564
glennrp03812ae2010-12-24 01:31:34 +000011565 }
glennrpce91ed52010-12-23 22:37:49 +000011566 }
glennrp26f37912010-12-23 16:22:42 +000011567 }
11568
glennrp5c7cf4e2010-12-24 00:30:00 +000011569 for (source=0; source<1; source++)
11570 {
11571 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011572 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011573 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011574
11575 if (value == NULL)
11576 value=GetImageArtifact(image,"png:include-chunks");
11577 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011578 else
glennrpacba0042010-12-24 14:27:26 +000011579 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011580 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011581
glennrpacba0042010-12-24 14:27:26 +000011582 if (value == NULL)
11583 value=GetImageOption(image_info,"png:include-chunks");
11584 }
11585
glennrp03812ae2010-12-24 01:31:34 +000011586 if (value != NULL)
11587 {
11588 size_t
11589 last;
glennrp26f37912010-12-23 16:22:42 +000011590
glennrp03812ae2010-12-24 01:31:34 +000011591 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011592
glennrp03812ae2010-12-24 01:31:34 +000011593 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011594 {
11595 if (source == 0)
11596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11597 " png:include-chunk=%s found in image artifacts.\n", value);
11598 else
11599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11600 " png:include-chunk=%s found in image properties.\n", value);
11601 }
glennrp03812ae2010-12-24 01:31:34 +000011602
11603 last=strlen(value);
11604
11605 for (i=0; i<(int) last; i+=5)
11606 {
11607 if (LocaleNCompare(value+i,"all",3) == 0)
11608 {
11609 mng_info->ping_exclude_bKGD=MagickFalse;
11610 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011611 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011612 mng_info->ping_exclude_EXIF=MagickFalse;
11613 mng_info->ping_exclude_gAMA=MagickFalse;
11614 mng_info->ping_exclude_iCCP=MagickFalse;
11615 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11616 mng_info->ping_exclude_oFFs=MagickFalse;
11617 mng_info->ping_exclude_pHYs=MagickFalse;
11618 mng_info->ping_exclude_sRGB=MagickFalse;
11619 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011620 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011621 mng_info->ping_exclude_vpAg=MagickFalse;
11622 mng_info->ping_exclude_zCCP=MagickFalse;
11623 mng_info->ping_exclude_zTXt=MagickFalse;
11624 i--;
11625 }
glennrp2cc891a2010-12-24 13:44:32 +000011626
glennrp03812ae2010-12-24 01:31:34 +000011627 if (LocaleNCompare(value+i,"none",4) == 0)
11628 {
11629 mng_info->ping_exclude_bKGD=MagickTrue;
11630 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011631 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011632 mng_info->ping_exclude_EXIF=MagickTrue;
11633 mng_info->ping_exclude_gAMA=MagickTrue;
11634 mng_info->ping_exclude_iCCP=MagickTrue;
11635 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11636 mng_info->ping_exclude_oFFs=MagickTrue;
11637 mng_info->ping_exclude_pHYs=MagickTrue;
11638 mng_info->ping_exclude_sRGB=MagickTrue;
11639 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011640 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011641 mng_info->ping_exclude_vpAg=MagickTrue;
11642 mng_info->ping_exclude_zCCP=MagickTrue;
11643 mng_info->ping_exclude_zTXt=MagickTrue;
11644 }
glennrp2cc891a2010-12-24 13:44:32 +000011645
glennrp03812ae2010-12-24 01:31:34 +000011646 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11647 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011648
glennrp03812ae2010-12-24 01:31:34 +000011649 if (LocaleNCompare(value+i,"chrm",4) == 0)
11650 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011651
glennrpa0ed0092011-04-18 16:36:29 +000011652 if (LocaleNCompare(value+i,"date",4) == 0)
11653 mng_info->ping_exclude_date=MagickFalse;
11654
glennrp03812ae2010-12-24 01:31:34 +000011655 if (LocaleNCompare(value+i,"exif",4) == 0)
11656 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011657
glennrp03812ae2010-12-24 01:31:34 +000011658 if (LocaleNCompare(value+i,"gama",4) == 0)
11659 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011660
glennrp03812ae2010-12-24 01:31:34 +000011661 if (LocaleNCompare(value+i,"iccp",4) == 0)
11662 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011663
glennrp03812ae2010-12-24 01:31:34 +000011664 /*
11665 if (LocaleNCompare(value+i,"itxt",4) == 0)
11666 mng_info->ping_exclude_iTXt=MagickFalse;
11667 */
glennrp2cc891a2010-12-24 13:44:32 +000011668
glennrp03812ae2010-12-24 01:31:34 +000011669 if (LocaleNCompare(value+i,"gama",4) == 0)
11670 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011671
glennrp03812ae2010-12-24 01:31:34 +000011672 if (LocaleNCompare(value+i,"offs",4) == 0)
11673 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011674
glennrp03812ae2010-12-24 01:31:34 +000011675 if (LocaleNCompare(value+i,"phys",4) == 0)
11676 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011677
glennrpa1e3b7b2010-12-24 16:37:33 +000011678 if (LocaleNCompare(value+i,"srgb",4) == 0)
11679 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011680
glennrp03812ae2010-12-24 01:31:34 +000011681 if (LocaleNCompare(value+i,"text",4) == 0)
11682 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011683
glennrpa1e3b7b2010-12-24 16:37:33 +000011684 if (LocaleNCompare(value+i,"trns",4) == 0)
11685 mng_info->ping_exclude_tRNS=MagickFalse;
11686
glennrp03812ae2010-12-24 01:31:34 +000011687 if (LocaleNCompare(value+i,"vpag",4) == 0)
11688 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011689
glennrp03812ae2010-12-24 01:31:34 +000011690 if (LocaleNCompare(value+i,"zccp",4) == 0)
11691 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011692
glennrp03812ae2010-12-24 01:31:34 +000011693 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11694 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011695
glennrp03812ae2010-12-24 01:31:34 +000011696 }
glennrpce91ed52010-12-23 22:37:49 +000011697 }
glennrp26f37912010-12-23 16:22:42 +000011698 }
11699
glennrp03812ae2010-12-24 01:31:34 +000011700 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011701 {
11702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011703 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011704 if (mng_info->ping_exclude_bKGD != MagickFalse)
11705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11706 " bKGD");
11707 if (mng_info->ping_exclude_cHRM != MagickFalse)
11708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11709 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011710 if (mng_info->ping_exclude_date != MagickFalse)
11711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11712 " date");
glennrp26f37912010-12-23 16:22:42 +000011713 if (mng_info->ping_exclude_EXIF != MagickFalse)
11714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11715 " EXIF");
11716 if (mng_info->ping_exclude_gAMA != MagickFalse)
11717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11718 " gAMA");
11719 if (mng_info->ping_exclude_iCCP != MagickFalse)
11720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11721 " iCCP");
11722/*
11723 if (mng_info->ping_exclude_iTXt != MagickFalse)
11724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11725 " iTXt");
11726*/
11727 if (mng_info->ping_exclude_oFFs != MagickFalse)
11728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11729 " oFFs");
11730 if (mng_info->ping_exclude_pHYs != MagickFalse)
11731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11732 " pHYs");
11733 if (mng_info->ping_exclude_sRGB != MagickFalse)
11734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11735 " sRGB");
11736 if (mng_info->ping_exclude_tEXt != MagickFalse)
11737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11738 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011739 if (mng_info->ping_exclude_tRNS != MagickFalse)
11740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11741 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011742 if (mng_info->ping_exclude_vpAg != MagickFalse)
11743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11744 " vpAg");
11745 if (mng_info->ping_exclude_zCCP != MagickFalse)
11746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11747 " zCCP");
11748 if (mng_info->ping_exclude_zTXt != MagickFalse)
11749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11750 " zTXt");
11751 }
11752
glennrpb9cfe272010-12-21 15:08:06 +000011753 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011754
cristy16ea1392012-03-21 20:38:41 +000011755 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011756
11757 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011758
cristy3ed852e2009-09-05 21:47:34 +000011759 if (logging != MagickFalse)
11760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011761
cristy3ed852e2009-09-05 21:47:34 +000011762 return(status);
11763}
11764
11765#if defined(JNG_SUPPORTED)
11766
11767/* Write one JNG image */
11768static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +000011769 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011770{
11771 Image
11772 *jpeg_image;
11773
11774 ImageInfo
11775 *jpeg_image_info;
11776
11777 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011778 logging,
cristy3ed852e2009-09-05 21:47:34 +000011779 status;
11780
11781 size_t
11782 length;
11783
11784 unsigned char
11785 *blob,
11786 chunk[80],
11787 *p;
11788
11789 unsigned int
11790 jng_alpha_compression_method,
11791 jng_alpha_sample_depth,
11792 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011793 transparent;
11794
cristybb503372010-05-27 20:51:26 +000011795 size_t
glennrp59575fa2011-12-31 21:31:39 +000011796 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000011797 jng_quality;
11798
11799 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011800 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011801
11802 blob=(unsigned char *) NULL;
11803 jpeg_image=(Image *) NULL;
11804 jpeg_image_info=(ImageInfo *) NULL;
11805
11806 status=MagickTrue;
11807 transparent=image_info->type==GrayscaleMatteType ||
cristy8a46d822012-08-28 23:32:39 +000011808 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +000011809
glennrp59575fa2011-12-31 21:31:39 +000011810 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11811
11812 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11813
glennrp750105b2012-04-25 16:20:45 +000011814 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
glennrp59575fa2011-12-31 21:31:39 +000011815 image_info->quality;
11816
11817 if (jng_alpha_quality >= 1000)
11818 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000011819
11820 if (transparent)
11821 {
11822 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011823
cristy3ed852e2009-09-05 21:47:34 +000011824 /* Create JPEG blob, image, and image_info */
11825 if (logging != MagickFalse)
11826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +000011827 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011828
cristy3ed852e2009-09-05 21:47:34 +000011829 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011830
cristy3ed852e2009-09-05 21:47:34 +000011831 if (jpeg_image_info == (ImageInfo *) NULL)
11832 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011833
cristy3ed852e2009-09-05 21:47:34 +000011834 if (logging != MagickFalse)
11835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11836 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011837
cristy16ea1392012-03-21 20:38:41 +000011838 jpeg_image=SeparateImage(image,AlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +000011839 if (jpeg_image == (Image *) NULL)
11840 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11841 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy8a46d822012-08-28 23:32:39 +000011842 jpeg_image->alpha_trait=UndefinedPixelTrait;
glennrp8f77fdc2012-03-21 15:16:37 +000011843 jpeg_image->quality=jng_alpha_quality;
cristy16ea1392012-03-21 20:38:41 +000011844 jpeg_image_info->type=GrayscaleType;
11845 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011846 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011847 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011848 "%s",jpeg_image->filename);
11849 }
glennrp59575fa2011-12-31 21:31:39 +000011850 else
11851 {
11852 jng_alpha_compression_method=0;
11853 jng_color_type=10;
11854 jng_alpha_sample_depth=0;
11855 }
cristy3ed852e2009-09-05 21:47:34 +000011856
11857 /* To do: check bit depth of PNG alpha channel */
11858
11859 /* Check if image is grayscale. */
11860 if (image_info->type != TrueColorMatteType && image_info->type !=
cristy7fb26522012-06-21 13:02:48 +000011861 TrueColorType && IsImageGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011862 jng_color_type-=2;
11863
glennrp59575fa2011-12-31 21:31:39 +000011864 if (logging != MagickFalse)
11865 {
11866 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11867 " JNG Quality = %d",(int) jng_quality);
11868 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11869 " JNG Color Type = %d",jng_color_type);
11870 if (transparent)
11871 {
11872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11873 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11875 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11877 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11878 }
11879 }
11880
cristy3ed852e2009-09-05 21:47:34 +000011881 if (transparent)
11882 {
11883 if (jng_alpha_compression_method==0)
11884 {
11885 const char
11886 *value;
11887
cristy16ea1392012-03-21 20:38:41 +000011888 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011889 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000011890 exception);
cristy3ed852e2009-09-05 21:47:34 +000011891 if (logging != MagickFalse)
11892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11893 " Creating PNG blob.");
11894 length=0;
11895
11896 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11897 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11898 jpeg_image_info->interlace=NoInterlace;
11899
glennrpcc5d45b2012-01-06 04:06:10 +000011900 /* Exclude all ancillary chunks */
11901 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11902
cristy3ed852e2009-09-05 21:47:34 +000011903 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000011904 exception);
cristy3ed852e2009-09-05 21:47:34 +000011905
11906 /* Retrieve sample depth used */
cristy16ea1392012-03-21 20:38:41 +000011907 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011908 if (value != (char *) NULL)
11909 jng_alpha_sample_depth= (unsigned int) value[0];
11910 }
11911 else
11912 {
cristy16ea1392012-03-21 20:38:41 +000011913 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011914
11915 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000011916 exception);
cristy3ed852e2009-09-05 21:47:34 +000011917
11918 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11919 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11920 jpeg_image_info->interlace=NoInterlace;
11921 if (logging != MagickFalse)
11922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11923 " Creating blob.");
11924 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000011925 exception);
cristy3ed852e2009-09-05 21:47:34 +000011926 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011927
cristy3ed852e2009-09-05 21:47:34 +000011928 if (logging != MagickFalse)
11929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011930 " Successfully read jpeg_image into a blob, length=%.20g.",
11931 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011932
11933 }
11934 /* Destroy JPEG image and image_info */
11935 jpeg_image=DestroyImage(jpeg_image);
11936 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11937 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11938 }
11939
11940 /* Write JHDR chunk */
11941 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11942 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011943 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011944 PNGLong(chunk+4,(png_uint_32) image->columns);
11945 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011946 chunk[12]=jng_color_type;
11947 chunk[13]=8; /* sample depth */
11948 chunk[14]=8; /*jng_image_compression_method */
11949 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11950 chunk[16]=jng_alpha_sample_depth;
11951 chunk[17]=jng_alpha_compression_method;
11952 chunk[18]=0; /*jng_alpha_filter_method */
11953 chunk[19]=0; /*jng_alpha_interlace_method */
11954 (void) WriteBlob(image,20,chunk);
11955 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11956 if (logging != MagickFalse)
11957 {
11958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011959 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011960
cristy3ed852e2009-09-05 21:47:34 +000011961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011962 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011963
cristy3ed852e2009-09-05 21:47:34 +000011964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11965 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011966
cristy3ed852e2009-09-05 21:47:34 +000011967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11968 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011969
cristy3ed852e2009-09-05 21:47:34 +000011970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11971 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011972
cristy3ed852e2009-09-05 21:47:34 +000011973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11974 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011975
cristy3ed852e2009-09-05 21:47:34 +000011976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11977 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011978
cristy3ed852e2009-09-05 21:47:34 +000011979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11980 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011981
cristy3ed852e2009-09-05 21:47:34 +000011982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11983 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011984
cristy3ed852e2009-09-05 21:47:34 +000011985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11986 " JNG alpha interlace:%5d",0);
11987 }
11988
glennrp0fe50b42010-11-16 03:52:51 +000011989 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011990 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011991
11992 /*
11993 Write leading ancillary chunks
11994 */
11995
11996 if (transparent)
11997 {
11998 /*
11999 Write JNG bKGD chunk
12000 */
12001
12002 unsigned char
12003 blue,
12004 green,
12005 red;
12006
cristybb503372010-05-27 20:51:26 +000012007 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012008 num_bytes;
12009
12010 if (jng_color_type == 8 || jng_color_type == 12)
12011 num_bytes=6L;
12012 else
12013 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000012014 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012015 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012016 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012017 red=ScaleQuantumToChar(image->background_color.red);
12018 green=ScaleQuantumToChar(image->background_color.green);
12019 blue=ScaleQuantumToChar(image->background_color.blue);
12020 *(chunk+4)=0;
12021 *(chunk+5)=red;
12022 *(chunk+6)=0;
12023 *(chunk+7)=green;
12024 *(chunk+8)=0;
12025 *(chunk+9)=blue;
12026 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12027 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12028 }
12029
12030 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12031 {
12032 /*
12033 Write JNG sRGB chunk
12034 */
12035 (void) WriteBlobMSBULong(image,1L);
12036 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012037 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012038
cristy3ed852e2009-09-05 21:47:34 +000012039 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012040 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012041 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012042 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012043
cristy3ed852e2009-09-05 21:47:34 +000012044 else
glennrpe610a072010-08-05 17:08:46 +000012045 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012046 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012047 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012048
cristy3ed852e2009-09-05 21:47:34 +000012049 (void) WriteBlob(image,5,chunk);
12050 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12051 }
12052 else
12053 {
12054 if (image->gamma != 0.0)
12055 {
12056 /*
12057 Write JNG gAMA chunk
12058 */
12059 (void) WriteBlobMSBULong(image,4L);
12060 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012061 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012062 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012063 (void) WriteBlob(image,8,chunk);
12064 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12065 }
glennrp0fe50b42010-11-16 03:52:51 +000012066
cristy3ed852e2009-09-05 21:47:34 +000012067 if ((mng_info->equal_chrms == MagickFalse) &&
12068 (image->chromaticity.red_primary.x != 0.0))
12069 {
12070 PrimaryInfo
12071 primary;
12072
12073 /*
12074 Write JNG cHRM chunk
12075 */
12076 (void) WriteBlobMSBULong(image,32L);
12077 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012078 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012079 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012080 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12081 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012082 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012083 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12084 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012085 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012086 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12087 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012088 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012089 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12090 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012091 (void) WriteBlob(image,36,chunk);
12092 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12093 }
12094 }
glennrp0fe50b42010-11-16 03:52:51 +000012095
cristy16ea1392012-03-21 20:38:41 +000012096 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012097 {
12098 /*
12099 Write JNG pHYs chunk
12100 */
12101 (void) WriteBlobMSBULong(image,9L);
12102 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012103 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000012104 if (image->units == PixelsPerInchResolution)
12105 {
cristy35ef8242010-06-03 16:24:13 +000012106 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012107 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012108
cristy35ef8242010-06-03 16:24:13 +000012109 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012110 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012111
cristy3ed852e2009-09-05 21:47:34 +000012112 chunk[12]=1;
12113 }
glennrp0fe50b42010-11-16 03:52:51 +000012114
cristy3ed852e2009-09-05 21:47:34 +000012115 else
12116 {
12117 if (image->units == PixelsPerCentimeterResolution)
12118 {
cristy35ef8242010-06-03 16:24:13 +000012119 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012120 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012121
cristy35ef8242010-06-03 16:24:13 +000012122 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012123 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012124
cristy3ed852e2009-09-05 21:47:34 +000012125 chunk[12]=1;
12126 }
glennrp0fe50b42010-11-16 03:52:51 +000012127
cristy3ed852e2009-09-05 21:47:34 +000012128 else
12129 {
cristy16ea1392012-03-21 20:38:41 +000012130 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12131 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012132 chunk[12]=0;
12133 }
12134 }
12135 (void) WriteBlob(image,13,chunk);
12136 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12137 }
12138
12139 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12140 {
12141 /*
12142 Write JNG oFFs chunk
12143 */
12144 (void) WriteBlobMSBULong(image,9L);
12145 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000012146 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000012147 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12148 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000012149 chunk[12]=0;
12150 (void) WriteBlob(image,13,chunk);
12151 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12152 }
12153 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12154 {
12155 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12156 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000012157 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012158 PNGLong(chunk+4,(png_uint_32) image->page.width);
12159 PNGLong(chunk+8,(png_uint_32) image->page.height);
12160 chunk[12]=0; /* unit = pixels */
12161 (void) WriteBlob(image,13,chunk);
12162 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12163 }
12164
12165
12166 if (transparent)
12167 {
12168 if (jng_alpha_compression_method==0)
12169 {
cristybb503372010-05-27 20:51:26 +000012170 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012171 i;
12172
cristybb503372010-05-27 20:51:26 +000012173 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012174 len;
12175
12176 /* Write IDAT chunk header */
12177 if (logging != MagickFalse)
12178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012179 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012180 length);
cristy3ed852e2009-09-05 21:47:34 +000012181
12182 /* Copy IDAT chunks */
12183 len=0;
12184 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012185 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012186 {
12187 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12188 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012189
cristy3ed852e2009-09-05 21:47:34 +000012190 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12191 {
12192 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012193 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012194 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012195 (void) WriteBlob(image,(size_t) len+4,p);
12196 (void) WriteBlobMSBULong(image,
12197 crc32(0,p,(uInt) len+4));
12198 }
glennrp0fe50b42010-11-16 03:52:51 +000012199
cristy3ed852e2009-09-05 21:47:34 +000012200 else
12201 {
12202 if (logging != MagickFalse)
12203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012204 " Skipping %c%c%c%c chunk, length=%.20g.",
12205 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012206 }
12207 p+=(8+len);
12208 }
12209 }
12210 else
12211 {
12212 /* Write JDAA chunk header */
12213 if (logging != MagickFalse)
12214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012215 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012216 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012217 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012218 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012219 /* Write JDAT chunk(s) data */
12220 (void) WriteBlob(image,4,chunk);
12221 (void) WriteBlob(image,length,blob);
12222 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12223 (uInt) length));
12224 }
12225 blob=(unsigned char *) RelinquishMagickMemory(blob);
12226 }
12227
12228 /* Encode image as a JPEG blob */
12229 if (logging != MagickFalse)
12230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12231 " Creating jpeg_image_info.");
12232 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12233 if (jpeg_image_info == (ImageInfo *) NULL)
12234 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12235
12236 if (logging != MagickFalse)
12237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12238 " Creating jpeg_image.");
12239
cristy16ea1392012-03-21 20:38:41 +000012240 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012241 if (jpeg_image == (Image *) NULL)
12242 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12243 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12244
12245 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012246 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012247 jpeg_image->filename);
12248
12249 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012250 exception);
cristy3ed852e2009-09-05 21:47:34 +000012251
12252 if (logging != MagickFalse)
12253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012254 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12255 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012256
12257 if (jng_color_type == 8 || jng_color_type == 12)
12258 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012259
glennrp59575fa2011-12-31 21:31:39 +000012260 jpeg_image_info->quality=jng_quality;
12261 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012262 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12263 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012264
cristy3ed852e2009-09-05 21:47:34 +000012265 if (logging != MagickFalse)
12266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12267 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012268
cristy16ea1392012-03-21 20:38:41 +000012269 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012270
cristy3ed852e2009-09-05 21:47:34 +000012271 if (logging != MagickFalse)
12272 {
12273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012274 " Successfully read jpeg_image into a blob, length=%.20g.",
12275 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012276
12277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012278 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012279 }
glennrp0fe50b42010-11-16 03:52:51 +000012280
cristy3ed852e2009-09-05 21:47:34 +000012281 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012282 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012283 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012284 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012285 (void) WriteBlob(image,4,chunk);
12286 (void) WriteBlob(image,length,blob);
12287 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12288
12289 jpeg_image=DestroyImage(jpeg_image);
12290 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12291 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12292 blob=(unsigned char *) RelinquishMagickMemory(blob);
12293
12294 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012295 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012296
12297 /* Write IEND chunk */
12298 (void) WriteBlobMSBULong(image,0L);
12299 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012300 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012301 (void) WriteBlob(image,4,chunk);
12302 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12303
12304 if (logging != MagickFalse)
12305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12306 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012307
cristy3ed852e2009-09-05 21:47:34 +000012308 return(status);
12309}
12310
12311
12312/*
12313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12314% %
12315% %
12316% %
12317% W r i t e J N G I m a g e %
12318% %
12319% %
12320% %
12321%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12322%
12323% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12324%
12325% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12326%
12327% The format of the WriteJNGImage method is:
12328%
cristy16ea1392012-03-21 20:38:41 +000012329% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12330% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012331%
12332% A description of each parameter follows:
12333%
12334% o image_info: the image info.
12335%
12336% o image: The image.
12337%
cristy16ea1392012-03-21 20:38:41 +000012338% o exception: return any errors or warnings in this structure.
12339%
cristy3ed852e2009-09-05 21:47:34 +000012340%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12341*/
cristy16ea1392012-03-21 20:38:41 +000012342static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12343 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012344{
12345 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012346 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012347 logging,
cristy3ed852e2009-09-05 21:47:34 +000012348 status;
12349
12350 MngInfo
12351 *mng_info;
12352
cristy3ed852e2009-09-05 21:47:34 +000012353 /*
12354 Open image file.
12355 */
12356 assert(image_info != (const ImageInfo *) NULL);
12357 assert(image_info->signature == MagickSignature);
12358 assert(image != (Image *) NULL);
12359 assert(image->signature == MagickSignature);
12360 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012361 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012362 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012363 if (status == MagickFalse)
12364 return(status);
12365
12366 /*
12367 Allocate a MngInfo structure.
12368 */
12369 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012370 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012371 if (mng_info == (MngInfo *) NULL)
12372 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12373 /*
12374 Initialize members of the MngInfo structure.
12375 */
12376 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12377 mng_info->image=image;
12378 have_mng_structure=MagickTrue;
12379
12380 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12381
cristy16ea1392012-03-21 20:38:41 +000012382 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012383 (void) CloseBlob(image);
12384
12385 (void) CatchImageException(image);
12386 MngInfoFreeStruct(mng_info,&have_mng_structure);
12387 if (logging != MagickFalse)
12388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12389 return(status);
12390}
12391#endif
12392
cristy16ea1392012-03-21 20:38:41 +000012393static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12394 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012395{
12396 const char
12397 *option;
12398
12399 Image
12400 *next_image;
12401
12402 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012403 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012404 status;
12405
glennrp03812ae2010-12-24 01:31:34 +000012406 volatile MagickBooleanType
12407 logging;
12408
cristy3ed852e2009-09-05 21:47:34 +000012409 MngInfo
12410 *mng_info;
12411
12412 int
cristy3ed852e2009-09-05 21:47:34 +000012413 image_count,
12414 need_iterations,
12415 need_matte;
12416
12417 volatile int
12418#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12419 defined(PNG_MNG_FEATURES_SUPPORTED)
12420 need_local_plte,
12421#endif
12422 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012423 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012424 use_global_plte;
12425
cristybb503372010-05-27 20:51:26 +000012426 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012427 i;
12428
12429 unsigned char
12430 chunk[800];
12431
12432 volatile unsigned int
12433 write_jng,
12434 write_mng;
12435
cristybb503372010-05-27 20:51:26 +000012436 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012437 scene;
12438
cristybb503372010-05-27 20:51:26 +000012439 size_t
cristy3ed852e2009-09-05 21:47:34 +000012440 final_delay=0,
12441 initial_delay;
12442
glennrpd5045b42010-03-24 12:40:35 +000012443#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012444 if (image_info->verbose)
12445 printf("Your PNG library (libpng-%s) is rather old.\n",
12446 PNG_LIBPNG_VER_STRING);
12447#endif
12448
12449 /*
12450 Open image file.
12451 */
12452 assert(image_info != (const ImageInfo *) NULL);
12453 assert(image_info->signature == MagickSignature);
12454 assert(image != (Image *) NULL);
12455 assert(image->signature == MagickSignature);
12456 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012457 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012458 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012459 if (status == MagickFalse)
12460 return(status);
12461
12462 /*
12463 Allocate a MngInfo structure.
12464 */
12465 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012466 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012467 if (mng_info == (MngInfo *) NULL)
12468 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12469 /*
12470 Initialize members of the MngInfo structure.
12471 */
12472 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12473 mng_info->image=image;
12474 have_mng_structure=MagickTrue;
12475 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12476
12477 /*
12478 * See if user has requested a specific PNG subformat to be used
12479 * for all of the PNGs in the MNG being written, e.g.,
12480 *
12481 * convert *.png png8:animation.mng
12482 *
12483 * To do: check -define png:bit_depth and png:color_type as well,
12484 * or perhaps use mng:bit_depth and mng:color_type instead for
12485 * global settings.
12486 */
12487
12488 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12489 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12490 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12491
12492 write_jng=MagickFalse;
12493 if (image_info->compression == JPEGCompression)
12494 write_jng=MagickTrue;
12495
12496 mng_info->adjoin=image_info->adjoin &&
12497 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12498
cristy3ed852e2009-09-05 21:47:34 +000012499 if (logging != MagickFalse)
12500 {
12501 /* Log some info about the input */
12502 Image
12503 *p;
12504
12505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12506 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012507
cristy3ed852e2009-09-05 21:47:34 +000012508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012509 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012510
cristy3ed852e2009-09-05 21:47:34 +000012511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12512 " Type: %d",image_info->type);
12513
12514 scene=0;
12515 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12516 {
12517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012518 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012519
cristy3ed852e2009-09-05 21:47:34 +000012520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012521 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012522
cristy8a46d822012-08-28 23:32:39 +000012523 if (p->alpha_trait)
cristy3ed852e2009-09-05 21:47:34 +000012524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12525 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012526
cristy3ed852e2009-09-05 21:47:34 +000012527 else
12528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12529 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012530
cristy3ed852e2009-09-05 21:47:34 +000012531 if (p->storage_class == PseudoClass)
12532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12533 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012534
cristy3ed852e2009-09-05 21:47:34 +000012535 else
12536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12537 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012538
cristy3ed852e2009-09-05 21:47:34 +000012539 if (p->colors)
12540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012541 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012542
cristy3ed852e2009-09-05 21:47:34 +000012543 else
12544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12545 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012546
cristy3ed852e2009-09-05 21:47:34 +000012547 if (mng_info->adjoin == MagickFalse)
12548 break;
12549 }
12550 }
12551
cristy3ed852e2009-09-05 21:47:34 +000012552 use_global_plte=MagickFalse;
12553 all_images_are_gray=MagickFalse;
12554#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12555 need_local_plte=MagickTrue;
12556#endif
12557 need_defi=MagickFalse;
12558 need_matte=MagickFalse;
12559 mng_info->framing_mode=1;
12560 mng_info->old_framing_mode=1;
12561
12562 if (write_mng)
12563 if (image_info->page != (char *) NULL)
12564 {
12565 /*
12566 Determine image bounding box.
12567 */
12568 SetGeometry(image,&mng_info->page);
12569 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12570 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12571 }
12572 if (write_mng)
12573 {
12574 unsigned int
12575 need_geom;
12576
12577 unsigned short
12578 red,
12579 green,
12580 blue;
12581
12582 mng_info->page=image->page;
12583 need_geom=MagickTrue;
12584 if (mng_info->page.width || mng_info->page.height)
12585 need_geom=MagickFalse;
12586 /*
12587 Check all the scenes.
12588 */
12589 initial_delay=image->delay;
12590 need_iterations=MagickFalse;
12591 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12592 mng_info->equal_physs=MagickTrue,
12593 mng_info->equal_gammas=MagickTrue;
12594 mng_info->equal_srgbs=MagickTrue;
12595 mng_info->equal_backgrounds=MagickTrue;
12596 image_count=0;
12597#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12598 defined(PNG_MNG_FEATURES_SUPPORTED)
12599 all_images_are_gray=MagickTrue;
12600 mng_info->equal_palettes=MagickFalse;
12601 need_local_plte=MagickFalse;
12602#endif
12603 for (next_image=image; next_image != (Image *) NULL; )
12604 {
12605 if (need_geom)
12606 {
12607 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12608 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012609
cristy3ed852e2009-09-05 21:47:34 +000012610 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12611 mng_info->page.height=next_image->rows+next_image->page.y;
12612 }
glennrp0fe50b42010-11-16 03:52:51 +000012613
cristy3ed852e2009-09-05 21:47:34 +000012614 if (next_image->page.x || next_image->page.y)
12615 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012616
cristy8a46d822012-08-28 23:32:39 +000012617 if (next_image->alpha_trait)
cristy3ed852e2009-09-05 21:47:34 +000012618 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012619
cristy3ed852e2009-09-05 21:47:34 +000012620 if ((int) next_image->dispose >= BackgroundDispose)
cristy8a46d822012-08-28 23:32:39 +000012621 if (next_image->alpha_trait || next_image->page.x || next_image->page.y ||
cristy3ed852e2009-09-05 21:47:34 +000012622 ((next_image->columns < mng_info->page.width) &&
12623 (next_image->rows < mng_info->page.height)))
12624 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012625
cristy3ed852e2009-09-05 21:47:34 +000012626 if (next_image->iterations)
12627 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012628
cristy3ed852e2009-09-05 21:47:34 +000012629 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012630
cristy3ed852e2009-09-05 21:47:34 +000012631 if (final_delay != initial_delay || final_delay > 1UL*
12632 next_image->ticks_per_second)
12633 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012634
cristy3ed852e2009-09-05 21:47:34 +000012635#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12636 defined(PNG_MNG_FEATURES_SUPPORTED)
12637 /*
12638 check for global palette possibility.
12639 */
cristy8a46d822012-08-28 23:32:39 +000012640 if (image->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000012641 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012642
cristy3ed852e2009-09-05 21:47:34 +000012643 if (need_local_plte == 0)
12644 {
cristy7fb26522012-06-21 13:02:48 +000012645 if (IsImageGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012646 all_images_are_gray=MagickFalse;
12647 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12648 if (use_global_plte == 0)
12649 use_global_plte=mng_info->equal_palettes;
12650 need_local_plte=!mng_info->equal_palettes;
12651 }
12652#endif
12653 if (GetNextImageInList(next_image) != (Image *) NULL)
12654 {
12655 if (next_image->background_color.red !=
12656 next_image->next->background_color.red ||
12657 next_image->background_color.green !=
12658 next_image->next->background_color.green ||
12659 next_image->background_color.blue !=
12660 next_image->next->background_color.blue)
12661 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012662
cristy3ed852e2009-09-05 21:47:34 +000012663 if (next_image->gamma != next_image->next->gamma)
12664 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012665
cristy3ed852e2009-09-05 21:47:34 +000012666 if (next_image->rendering_intent !=
12667 next_image->next->rendering_intent)
12668 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012669
cristy3ed852e2009-09-05 21:47:34 +000012670 if ((next_image->units != next_image->next->units) ||
cristy16ea1392012-03-21 20:38:41 +000012671 (next_image->resolution.x != next_image->next->resolution.x) ||
12672 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012673 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012674
cristy3ed852e2009-09-05 21:47:34 +000012675 if (mng_info->equal_chrms)
12676 {
12677 if (next_image->chromaticity.red_primary.x !=
12678 next_image->next->chromaticity.red_primary.x ||
12679 next_image->chromaticity.red_primary.y !=
12680 next_image->next->chromaticity.red_primary.y ||
12681 next_image->chromaticity.green_primary.x !=
12682 next_image->next->chromaticity.green_primary.x ||
12683 next_image->chromaticity.green_primary.y !=
12684 next_image->next->chromaticity.green_primary.y ||
12685 next_image->chromaticity.blue_primary.x !=
12686 next_image->next->chromaticity.blue_primary.x ||
12687 next_image->chromaticity.blue_primary.y !=
12688 next_image->next->chromaticity.blue_primary.y ||
12689 next_image->chromaticity.white_point.x !=
12690 next_image->next->chromaticity.white_point.x ||
12691 next_image->chromaticity.white_point.y !=
12692 next_image->next->chromaticity.white_point.y)
12693 mng_info->equal_chrms=MagickFalse;
12694 }
12695 }
12696 image_count++;
12697 next_image=GetNextImageInList(next_image);
12698 }
12699 if (image_count < 2)
12700 {
12701 mng_info->equal_backgrounds=MagickFalse;
12702 mng_info->equal_chrms=MagickFalse;
12703 mng_info->equal_gammas=MagickFalse;
12704 mng_info->equal_srgbs=MagickFalse;
12705 mng_info->equal_physs=MagickFalse;
12706 use_global_plte=MagickFalse;
12707#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12708 need_local_plte=MagickTrue;
12709#endif
12710 need_iterations=MagickFalse;
12711 }
glennrp0fe50b42010-11-16 03:52:51 +000012712
cristy3ed852e2009-09-05 21:47:34 +000012713 if (mng_info->need_fram == MagickFalse)
12714 {
12715 /*
12716 Only certain framing rates 100/n are exactly representable without
12717 the FRAM chunk but we'll allow some slop in VLC files
12718 */
12719 if (final_delay == 0)
12720 {
12721 if (need_iterations != MagickFalse)
12722 {
12723 /*
12724 It's probably a GIF with loop; don't run it *too* fast.
12725 */
glennrp02617122010-07-28 13:07:35 +000012726 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012727 {
12728 final_delay=10;
cristy16ea1392012-03-21 20:38:41 +000012729 (void) ThrowMagickException(exception,GetMagickModule(),
12730 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012731 "input has zero delay between all frames; assuming",
12732 " 10 cs `%s'","");
12733 }
cristy3ed852e2009-09-05 21:47:34 +000012734 }
12735 else
12736 mng_info->ticks_per_second=0;
12737 }
12738 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012739 mng_info->ticks_per_second=(png_uint_32)
12740 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012741 if (final_delay > 50)
12742 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012743
cristy3ed852e2009-09-05 21:47:34 +000012744 if (final_delay > 75)
12745 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012746
cristy3ed852e2009-09-05 21:47:34 +000012747 if (final_delay > 125)
12748 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012749
cristy3ed852e2009-09-05 21:47:34 +000012750 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12751 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12752 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12753 1UL*image->ticks_per_second))
12754 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12755 }
glennrp0fe50b42010-11-16 03:52:51 +000012756
cristy3ed852e2009-09-05 21:47:34 +000012757 if (mng_info->need_fram != MagickFalse)
12758 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12759 /*
12760 If pseudocolor, we should also check to see if all the
12761 palettes are identical and write a global PLTE if they are.
12762 ../glennrp Feb 99.
12763 */
12764 /*
12765 Write the MNG version 1.0 signature and MHDR chunk.
12766 */
12767 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12768 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12769 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012770 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012771 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12772 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012773 PNGLong(chunk+12,mng_info->ticks_per_second);
12774 PNGLong(chunk+16,0L); /* layer count=unknown */
12775 PNGLong(chunk+20,0L); /* frame count=unknown */
12776 PNGLong(chunk+24,0L); /* play time=unknown */
12777 if (write_jng)
12778 {
12779 if (need_matte)
12780 {
12781 if (need_defi || mng_info->need_fram || use_global_plte)
12782 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012783
cristy3ed852e2009-09-05 21:47:34 +000012784 else
12785 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12786 }
glennrp0fe50b42010-11-16 03:52:51 +000012787
cristy3ed852e2009-09-05 21:47:34 +000012788 else
12789 {
12790 if (need_defi || mng_info->need_fram || use_global_plte)
12791 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012792
cristy3ed852e2009-09-05 21:47:34 +000012793 else
12794 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12795 }
12796 }
glennrp0fe50b42010-11-16 03:52:51 +000012797
cristy3ed852e2009-09-05 21:47:34 +000012798 else
12799 {
12800 if (need_matte)
12801 {
12802 if (need_defi || mng_info->need_fram || use_global_plte)
12803 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012804
cristy3ed852e2009-09-05 21:47:34 +000012805 else
12806 PNGLong(chunk+28,9L); /* simplicity=VLC */
12807 }
glennrp0fe50b42010-11-16 03:52:51 +000012808
cristy3ed852e2009-09-05 21:47:34 +000012809 else
12810 {
12811 if (need_defi || mng_info->need_fram || use_global_plte)
12812 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012813
cristy3ed852e2009-09-05 21:47:34 +000012814 else
12815 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12816 }
12817 }
12818 (void) WriteBlob(image,32,chunk);
12819 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12820 option=GetImageOption(image_info,"mng:need-cacheoff");
12821 if (option != (const char *) NULL)
12822 {
12823 size_t
12824 length;
12825
12826 /*
12827 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12828 */
12829 PNGType(chunk,mng_nEED);
12830 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012831 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012832 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012833 length+=4;
12834 (void) WriteBlob(image,length,chunk);
12835 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12836 }
12837 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12838 (GetNextImageInList(image) != (Image *) NULL) &&
12839 (image->iterations != 1))
12840 {
12841 /*
12842 Write MNG TERM chunk
12843 */
12844 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12845 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012846 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012847 chunk[4]=3; /* repeat animation */
12848 chunk[5]=0; /* show last frame when done */
12849 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12850 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012851
cristy3ed852e2009-09-05 21:47:34 +000012852 if (image->iterations == 0)
12853 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012854
cristy3ed852e2009-09-05 21:47:34 +000012855 else
12856 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012857
cristy3ed852e2009-09-05 21:47:34 +000012858 if (logging != MagickFalse)
12859 {
12860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012861 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12862 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012863
cristy3ed852e2009-09-05 21:47:34 +000012864 if (image->iterations == 0)
12865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012866 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012867
cristy3ed852e2009-09-05 21:47:34 +000012868 else
12869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012870 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012871 }
12872 (void) WriteBlob(image,14,chunk);
12873 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12874 }
12875 /*
12876 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12877 */
12878 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12879 mng_info->equal_srgbs)
12880 {
12881 /*
12882 Write MNG sRGB chunk
12883 */
12884 (void) WriteBlobMSBULong(image,1L);
12885 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012886 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012887
cristy3ed852e2009-09-05 21:47:34 +000012888 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012889 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012890 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012891 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012892
cristy3ed852e2009-09-05 21:47:34 +000012893 else
glennrpe610a072010-08-05 17:08:46 +000012894 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012895 Magick_RenderingIntent_to_PNG_RenderingIntent(
12896 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012897
cristy3ed852e2009-09-05 21:47:34 +000012898 (void) WriteBlob(image,5,chunk);
12899 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12900 mng_info->have_write_global_srgb=MagickTrue;
12901 }
glennrp0fe50b42010-11-16 03:52:51 +000012902
cristy3ed852e2009-09-05 21:47:34 +000012903 else
12904 {
12905 if (image->gamma && mng_info->equal_gammas)
12906 {
12907 /*
12908 Write MNG gAMA chunk
12909 */
12910 (void) WriteBlobMSBULong(image,4L);
12911 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012912 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012913 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012914 (void) WriteBlob(image,8,chunk);
12915 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12916 mng_info->have_write_global_gama=MagickTrue;
12917 }
12918 if (mng_info->equal_chrms)
12919 {
12920 PrimaryInfo
12921 primary;
12922
12923 /*
12924 Write MNG cHRM chunk
12925 */
12926 (void) WriteBlobMSBULong(image,32L);
12927 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012928 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012929 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012930 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12931 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012932 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012933 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12934 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012935 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012936 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12937 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012938 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012939 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12940 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012941 (void) WriteBlob(image,36,chunk);
12942 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12943 mng_info->have_write_global_chrm=MagickTrue;
12944 }
12945 }
cristy16ea1392012-03-21 20:38:41 +000012946 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012947 {
12948 /*
12949 Write MNG pHYs chunk
12950 */
12951 (void) WriteBlobMSBULong(image,9L);
12952 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012953 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012954
cristy3ed852e2009-09-05 21:47:34 +000012955 if (image->units == PixelsPerInchResolution)
12956 {
cristy35ef8242010-06-03 16:24:13 +000012957 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012958 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012959
cristy35ef8242010-06-03 16:24:13 +000012960 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012961 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012962
cristy3ed852e2009-09-05 21:47:34 +000012963 chunk[12]=1;
12964 }
glennrp0fe50b42010-11-16 03:52:51 +000012965
cristy3ed852e2009-09-05 21:47:34 +000012966 else
12967 {
12968 if (image->units == PixelsPerCentimeterResolution)
12969 {
cristy35ef8242010-06-03 16:24:13 +000012970 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012971 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012972
cristy35ef8242010-06-03 16:24:13 +000012973 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012974 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012975
cristy3ed852e2009-09-05 21:47:34 +000012976 chunk[12]=1;
12977 }
glennrp0fe50b42010-11-16 03:52:51 +000012978
cristy3ed852e2009-09-05 21:47:34 +000012979 else
12980 {
cristy16ea1392012-03-21 20:38:41 +000012981 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12982 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012983 chunk[12]=0;
12984 }
12985 }
12986 (void) WriteBlob(image,13,chunk);
12987 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12988 }
12989 /*
12990 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12991 or does not cover the entire frame.
12992 */
cristy8a46d822012-08-28 23:32:39 +000012993 if (write_mng && (image->alpha_trait || image->page.x > 0 ||
cristy3ed852e2009-09-05 21:47:34 +000012994 image->page.y > 0 || (image->page.width &&
12995 (image->page.width+image->page.x < mng_info->page.width))
12996 || (image->page.height && (image->page.height+image->page.y
12997 < mng_info->page.height))))
12998 {
12999 (void) WriteBlobMSBULong(image,6L);
13000 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000013001 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000013002 red=ScaleQuantumToShort(image->background_color.red);
13003 green=ScaleQuantumToShort(image->background_color.green);
13004 blue=ScaleQuantumToShort(image->background_color.blue);
13005 PNGShort(chunk+4,red);
13006 PNGShort(chunk+6,green);
13007 PNGShort(chunk+8,blue);
13008 (void) WriteBlob(image,10,chunk);
13009 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13010 if (mng_info->equal_backgrounds)
13011 {
13012 (void) WriteBlobMSBULong(image,6L);
13013 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000013014 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000013015 (void) WriteBlob(image,10,chunk);
13016 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13017 }
13018 }
13019
13020#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13021 if ((need_local_plte == MagickFalse) &&
13022 (image->storage_class == PseudoClass) &&
13023 (all_images_are_gray == MagickFalse))
13024 {
cristybb503372010-05-27 20:51:26 +000013025 size_t
cristy3ed852e2009-09-05 21:47:34 +000013026 data_length;
13027
13028 /*
13029 Write MNG PLTE chunk
13030 */
13031 data_length=3*image->colors;
13032 (void) WriteBlobMSBULong(image,data_length);
13033 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013034 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013035
cristybb503372010-05-27 20:51:26 +000013036 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013037 {
cristy16ea1392012-03-21 20:38:41 +000013038 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13039 image->colormap[i].red) & 0xff);
13040 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13041 image->colormap[i].green) & 0xff);
13042 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13043 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000013044 }
glennrp0fe50b42010-11-16 03:52:51 +000013045
cristy3ed852e2009-09-05 21:47:34 +000013046 (void) WriteBlob(image,data_length+4,chunk);
13047 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13048 mng_info->have_write_global_plte=MagickTrue;
13049 }
13050#endif
13051 }
13052 scene=0;
13053 mng_info->delay=0;
13054#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13055 defined(PNG_MNG_FEATURES_SUPPORTED)
13056 mng_info->equal_palettes=MagickFalse;
13057#endif
13058 do
13059 {
13060 if (mng_info->adjoin)
13061 {
13062#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13063 defined(PNG_MNG_FEATURES_SUPPORTED)
13064 /*
13065 If we aren't using a global palette for the entire MNG, check to
13066 see if we can use one for two or more consecutive images.
13067 */
13068 if (need_local_plte && use_global_plte && !all_images_are_gray)
13069 {
13070 if (mng_info->IsPalette)
13071 {
13072 /*
13073 When equal_palettes is true, this image has the same palette
13074 as the previous PseudoClass image
13075 */
13076 mng_info->have_write_global_plte=mng_info->equal_palettes;
13077 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13078 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13079 {
13080 /*
13081 Write MNG PLTE chunk
13082 */
cristybb503372010-05-27 20:51:26 +000013083 size_t
cristy3ed852e2009-09-05 21:47:34 +000013084 data_length;
13085
13086 data_length=3*image->colors;
13087 (void) WriteBlobMSBULong(image,data_length);
13088 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013089 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013090
cristybb503372010-05-27 20:51:26 +000013091 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013092 {
13093 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13094 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13095 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13096 }
glennrp0fe50b42010-11-16 03:52:51 +000013097
cristy3ed852e2009-09-05 21:47:34 +000013098 (void) WriteBlob(image,data_length+4,chunk);
13099 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13100 (uInt) (data_length+4)));
13101 mng_info->have_write_global_plte=MagickTrue;
13102 }
13103 }
13104 else
13105 mng_info->have_write_global_plte=MagickFalse;
13106 }
13107#endif
13108 if (need_defi)
13109 {
cristybb503372010-05-27 20:51:26 +000013110 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000013111 previous_x,
13112 previous_y;
13113
13114 if (scene)
13115 {
13116 previous_x=mng_info->page.x;
13117 previous_y=mng_info->page.y;
13118 }
13119 else
13120 {
13121 previous_x=0;
13122 previous_y=0;
13123 }
13124 mng_info->page=image->page;
13125 if ((mng_info->page.x != previous_x) ||
13126 (mng_info->page.y != previous_y))
13127 {
13128 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13129 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000013130 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000013131 chunk[4]=0; /* object 0 MSB */
13132 chunk[5]=0; /* object 0 LSB */
13133 chunk[6]=0; /* visible */
13134 chunk[7]=0; /* abstract */
13135 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13136 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13137 (void) WriteBlob(image,16,chunk);
13138 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13139 }
13140 }
13141 }
13142
13143 mng_info->write_mng=write_mng;
13144
13145 if ((int) image->dispose >= 3)
13146 mng_info->framing_mode=3;
13147
13148 if (mng_info->need_fram && mng_info->adjoin &&
13149 ((image->delay != mng_info->delay) ||
13150 (mng_info->framing_mode != mng_info->old_framing_mode)))
13151 {
13152 if (image->delay == mng_info->delay)
13153 {
13154 /*
13155 Write a MNG FRAM chunk with the new framing mode.
13156 */
13157 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13158 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013159 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013160 chunk[4]=(unsigned char) mng_info->framing_mode;
13161 (void) WriteBlob(image,5,chunk);
13162 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13163 }
13164 else
13165 {
13166 /*
13167 Write a MNG FRAM chunk with the delay.
13168 */
13169 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13170 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013171 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013172 chunk[4]=(unsigned char) mng_info->framing_mode;
13173 chunk[5]=0; /* frame name separator (no name) */
13174 chunk[6]=2; /* flag for changing default delay */
13175 chunk[7]=0; /* flag for changing frame timeout */
13176 chunk[8]=0; /* flag for changing frame clipping */
13177 chunk[9]=0; /* flag for changing frame sync_id */
13178 PNGLong(chunk+10,(png_uint_32)
13179 ((mng_info->ticks_per_second*
13180 image->delay)/MagickMax(image->ticks_per_second,1)));
13181 (void) WriteBlob(image,14,chunk);
13182 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013183 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013184 }
13185 mng_info->old_framing_mode=mng_info->framing_mode;
13186 }
13187
13188#if defined(JNG_SUPPORTED)
13189 if (image_info->compression == JPEGCompression)
13190 {
13191 ImageInfo
13192 *write_info;
13193
13194 if (logging != MagickFalse)
13195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13196 " Writing JNG object.");
13197 /* To do: specify the desired alpha compression method. */
13198 write_info=CloneImageInfo(image_info);
13199 write_info->compression=UndefinedCompression;
cristy16ea1392012-03-21 20:38:41 +000013200 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013201 write_info=DestroyImageInfo(write_info);
13202 }
13203 else
13204#endif
13205 {
13206 if (logging != MagickFalse)
13207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13208 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013209
glennrpb9cfe272010-12-21 15:08:06 +000013210 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013211 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013212
13213 /* We don't want any ancillary chunks written */
13214 mng_info->ping_exclude_bKGD=MagickTrue;
13215 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013216 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013217 mng_info->ping_exclude_EXIF=MagickTrue;
13218 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013219 mng_info->ping_exclude_iCCP=MagickTrue;
13220 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13221 mng_info->ping_exclude_oFFs=MagickTrue;
13222 mng_info->ping_exclude_pHYs=MagickTrue;
13223 mng_info->ping_exclude_sRGB=MagickTrue;
13224 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013225 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013226 mng_info->ping_exclude_vpAg=MagickTrue;
13227 mng_info->ping_exclude_zCCP=MagickTrue;
13228 mng_info->ping_exclude_zTXt=MagickTrue;
13229
cristy16ea1392012-03-21 20:38:41 +000013230 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013231 }
13232
13233 if (status == MagickFalse)
13234 {
13235 MngInfoFreeStruct(mng_info,&have_mng_structure);
13236 (void) CloseBlob(image);
13237 return(MagickFalse);
13238 }
13239 (void) CatchImageException(image);
13240 if (GetNextImageInList(image) == (Image *) NULL)
13241 break;
13242 image=SyncNextImageInList(image);
13243 status=SetImageProgress(image,SaveImagesTag,scene++,
13244 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013245
cristy3ed852e2009-09-05 21:47:34 +000013246 if (status == MagickFalse)
13247 break;
glennrp0fe50b42010-11-16 03:52:51 +000013248
cristy3ed852e2009-09-05 21:47:34 +000013249 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013250
cristy3ed852e2009-09-05 21:47:34 +000013251 if (write_mng)
13252 {
13253 while (GetPreviousImageInList(image) != (Image *) NULL)
13254 image=GetPreviousImageInList(image);
13255 /*
13256 Write the MEND chunk.
13257 */
13258 (void) WriteBlobMSBULong(image,0x00000000L);
13259 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013260 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013261 (void) WriteBlob(image,4,chunk);
13262 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13263 }
13264 /*
13265 Relinquish resources.
13266 */
13267 (void) CloseBlob(image);
13268 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013269
cristy3ed852e2009-09-05 21:47:34 +000013270 if (logging != MagickFalse)
13271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013272
cristy3ed852e2009-09-05 21:47:34 +000013273 return(MagickTrue);
13274}
glennrpd5045b42010-03-24 12:40:35 +000013275#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013276
cristy3ed852e2009-09-05 21:47:34 +000013277static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13278{
glennrp3bd393f2011-12-21 18:54:53 +000013279 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013280 printf("Your PNG library is too old: You have libpng-%s\n",
13281 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013282
cristy3ed852e2009-09-05 21:47:34 +000013283 ThrowBinaryException(CoderError,"PNG library is too old",
13284 image_info->filename);
13285}
glennrp39992b42010-11-14 00:03:43 +000013286
cristy3ed852e2009-09-05 21:47:34 +000013287static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13288{
13289 return(WritePNGImage(image_info,image));
13290}
glennrpd5045b42010-03-24 12:40:35 +000013291#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013292#endif