blob: 4b19548d34aeab37705df0367877f6aadc8aa4b6 [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,
glennrpfd164d22013-01-26 21:10:22 +0000883 write_png32,
884 write_png48,
885 write_png64;
cristy3ed852e2009-09-05 21:47:34 +0000886
887#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000888 size_t
cristy3ed852e2009-09-05 21:47:34 +0000889 basi_width,
890 basi_height;
891
892 unsigned int
893 basi_depth,
894 basi_color_type,
895 basi_compression_method,
896 basi_filter_type,
897 basi_interlace_method,
898 basi_red,
899 basi_green,
900 basi_blue,
901 basi_alpha,
902 basi_viewable;
903#endif
904
905 png_uint_16
906 magn_first,
907 magn_last,
908 magn_mb,
909 magn_ml,
910 magn_mr,
911 magn_mt,
912 magn_mx,
913 magn_my,
914 magn_methx,
915 magn_methy;
916
cristy16ea1392012-03-21 20:38:41 +0000917 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000918 mng_global_bkgd;
919
glennrp26f37912010-12-23 16:22:42 +0000920 /* Added at version 6.6.6-7 */
921 MagickBooleanType
922 ping_exclude_bKGD,
923 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000924 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000925 ping_exclude_EXIF,
926 ping_exclude_gAMA,
927 ping_exclude_iCCP,
928 /* ping_exclude_iTXt, */
929 ping_exclude_oFFs,
930 ping_exclude_pHYs,
931 ping_exclude_sRGB,
932 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000933 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000934 ping_exclude_vpAg,
935 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000936 ping_exclude_zTXt,
937 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000938
cristy3ed852e2009-09-05 21:47:34 +0000939} MngInfo;
940#endif /* VER */
941
942/*
943 Forward declarations.
944*/
945static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000946 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000947
cristy3ed852e2009-09-05 21:47:34 +0000948static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000949 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000950
cristy3ed852e2009-09-05 21:47:34 +0000951#if defined(JNG_SUPPORTED)
952static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000953 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000954#endif
955
glennrp0c3e06b2010-11-19 13:45:02 +0000956#if PNG_LIBPNG_VER > 10011
957
glennrpfd05d622011-02-25 04:10:33 +0000958
glennrp0c3e06b2010-11-19 13:45:02 +0000959#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
960static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000961LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
glennrp0c3e06b2010-11-19 13:45:02 +0000962{
glennrp67b9c1a2011-04-22 18:47:36 +0000963 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
964 *
965 * This is true if the high byte and the next highest byte of
966 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000967 * are equal to each other. We check this by seeing if the samples
968 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000969 *
970 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000971 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000972 */
973
glennrp3faa9a32011-04-23 14:00:25 +0000974#define QuantumToCharToQuantumEqQuantum(quantum) \
975 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
976
glennrp0c3e06b2010-11-19 13:45:02 +0000977 MagickBooleanType
978 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000979
glennrp03e11f62011-04-22 13:30:16 +0000980 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000981 {
982
cristy16ea1392012-03-21 20:38:41 +0000983 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000984 *p;
985
986 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000987 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
988 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
989 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
990 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000991
992 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
993 {
994 int indx;
995
996 for (indx=0; indx < (ssize_t) image->colors; indx++)
997 {
glennrp3faa9a32011-04-23 14:00:25 +0000998 ok_to_reduce=(
999 QuantumToCharToQuantumEqQuantum(
1000 image->colormap[indx].red) &&
1001 QuantumToCharToQuantumEqQuantum(
1002 image->colormap[indx].green) &&
1003 QuantumToCharToQuantumEqQuantum(
1004 image->colormap[indx].blue)) ?
1005 MagickTrue : MagickFalse;
1006
glennrp0c3e06b2010-11-19 13:45:02 +00001007 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00001008 break;
glennrp0c3e06b2010-11-19 13:45:02 +00001009 }
1010 }
1011
1012 if ((ok_to_reduce != MagickFalse) &&
1013 (image->storage_class != PseudoClass))
1014 {
1015 ssize_t
1016 y;
1017
1018 register ssize_t
1019 x;
1020
1021 for (y=0; y < (ssize_t) image->rows; y++)
1022 {
cristy16ea1392012-03-21 20:38:41 +00001023 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0c3e06b2010-11-19 13:45:02 +00001024
cristy16ea1392012-03-21 20:38:41 +00001025 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001026 {
1027 ok_to_reduce = MagickFalse;
1028 break;
1029 }
1030
1031 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1032 {
glennrp3faa9a32011-04-23 14:00:25 +00001033 ok_to_reduce=
cristy16ea1392012-03-21 20:38:41 +00001034 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1035 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1036 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001037 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001038
1039 if (ok_to_reduce == MagickFalse)
1040 break;
1041
cristy16ea1392012-03-21 20:38:41 +00001042 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001043 }
glennrp8640fb52010-11-23 15:48:26 +00001044 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001045 break;
1046 }
1047 }
1048
1049 if (ok_to_reduce != MagickFalse)
1050 {
glennrp0c3e06b2010-11-19 13:45:02 +00001051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001052 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001053 }
glennrpa6a06632011-01-19 15:15:34 +00001054 else
1055 {
1056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001057 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001058 }
glennrp0c3e06b2010-11-19 13:45:02 +00001059 }
1060
1061 return ok_to_reduce;
1062}
1063#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1064
glennrp1a56e9c2012-04-25 17:06:57 +00001065static const char* PngColorTypeToString(const unsigned int color_type)
1066{
1067 const char
1068 *result = "Unknown";
1069
1070 switch (color_type)
1071 {
1072 case PNG_COLOR_TYPE_GRAY:
1073 result = "Gray";
1074 break;
1075 case PNG_COLOR_TYPE_GRAY_ALPHA:
1076 result = "Gray+Alpha";
1077 break;
1078 case PNG_COLOR_TYPE_PALETTE:
1079 result = "Palette";
1080 break;
1081 case PNG_COLOR_TYPE_RGB:
1082 result = "RGB";
1083 break;
1084 case PNG_COLOR_TYPE_RGB_ALPHA:
1085 result = "RGB+Alpha";
1086 break;
1087 }
1088
1089 return result;
1090}
1091
glennrpe610a072010-08-05 17:08:46 +00001092static int
glennrpcf002022011-01-30 02:38:15 +00001093Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001094{
glennrpe610a072010-08-05 17:08:46 +00001095 switch (intent)
1096 {
1097 case PerceptualIntent:
1098 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001099
glennrpe610a072010-08-05 17:08:46 +00001100 case RelativeIntent:
1101 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001102
glennrpe610a072010-08-05 17:08:46 +00001103 case SaturationIntent:
1104 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001105
glennrpe610a072010-08-05 17:08:46 +00001106 case AbsoluteIntent:
1107 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001108
glennrpe610a072010-08-05 17:08:46 +00001109 default:
1110 return -1;
1111 }
1112}
1113
1114static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001115Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001116{
glennrpcf002022011-01-30 02:38:15 +00001117 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001118 {
1119 case 0:
1120 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001121
glennrpe610a072010-08-05 17:08:46 +00001122 case 1:
1123 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001124
glennrpe610a072010-08-05 17:08:46 +00001125 case 2:
1126 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001127
glennrpe610a072010-08-05 17:08:46 +00001128 case 3:
1129 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001130
glennrpe610a072010-08-05 17:08:46 +00001131 default:
1132 return UndefinedIntent;
1133 }
1134}
1135
cristy9d8c1222012-08-10 12:34:19 +00001136static const char *
glennrp98b83d42012-07-23 02:50:31 +00001137Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1138{
1139 switch (ping_intent)
1140 {
1141 case 0:
1142 return "Perceptual Intent";
1143
1144 case 1:
1145 return "Relative Intent";
1146
1147 case 2:
1148 return "Saturation Intent";
1149
1150 case 3:
1151 return "Absolute Intent";
1152
1153 default:
1154 return "Undefined Intent";
1155 }
1156}
1157
cristybb503372010-05-27 20:51:26 +00001158static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001159{
1160 if (x > y)
1161 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001162
cristy3ed852e2009-09-05 21:47:34 +00001163 return(y);
1164}
glennrp0c3e06b2010-11-19 13:45:02 +00001165
cristyd9ecd042012-06-17 18:26:12 +00001166static const char *
glennrp5dff4352012-06-06 22:12:04 +00001167Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1168{
1169 switch (ping_colortype)
1170 {
1171 case 0:
1172 return "Grayscale";
1173
1174 case 2:
1175 return "Truecolor";
1176
1177 case 3:
1178 return "Indexed";
1179
1180 case 4:
1181 return "GrayAlpha";
1182
1183 case 6:
1184 return "RGBA";
1185
1186 default:
1187 return "UndefinedColorType";
1188 }
1189}
1190
1191
cristybb503372010-05-27 20:51:26 +00001192static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001193{
1194 if (x < y)
1195 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001196
cristy3ed852e2009-09-05 21:47:34 +00001197 return(y);
1198}
glennrpd5045b42010-03-24 12:40:35 +00001199#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001200#endif /* MAGICKCORE_PNG_DELEGATE */
1201
1202/*
1203%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1204% %
1205% %
1206% %
1207% I s M N G %
1208% %
1209% %
1210% %
1211%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1212%
1213% IsMNG() returns MagickTrue if the image format type, identified by the
1214% magick string, is MNG.
1215%
1216% The format of the IsMNG method is:
1217%
1218% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1219%
1220% A description of each parameter follows:
1221%
1222% o magick: compare image format pattern against these bytes.
1223%
1224% o length: Specifies the length of the magick string.
1225%
1226%
1227*/
1228static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1229{
1230 if (length < 8)
1231 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001232
cristy3ed852e2009-09-05 21:47:34 +00001233 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1234 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001235
cristy3ed852e2009-09-05 21:47:34 +00001236 return(MagickFalse);
1237}
1238
1239/*
1240%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1241% %
1242% %
1243% %
1244% I s J N G %
1245% %
1246% %
1247% %
1248%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1249%
1250% IsJNG() returns MagickTrue if the image format type, identified by the
1251% magick string, is JNG.
1252%
1253% The format of the IsJNG method is:
1254%
1255% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1256%
1257% A description of each parameter follows:
1258%
1259% o magick: compare image format pattern against these bytes.
1260%
1261% o length: Specifies the length of the magick string.
1262%
1263%
1264*/
1265static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1266{
1267 if (length < 8)
1268 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001269
cristy3ed852e2009-09-05 21:47:34 +00001270 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1271 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001272
cristy3ed852e2009-09-05 21:47:34 +00001273 return(MagickFalse);
1274}
1275
1276/*
1277%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1278% %
1279% %
1280% %
1281% I s P N G %
1282% %
1283% %
1284% %
1285%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1286%
1287% IsPNG() returns MagickTrue if the image format type, identified by the
1288% magick string, is PNG.
1289%
1290% The format of the IsPNG method is:
1291%
1292% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1293%
1294% A description of each parameter follows:
1295%
1296% o magick: compare image format pattern against these bytes.
1297%
1298% o length: Specifies the length of the magick string.
1299%
1300*/
1301static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1302{
1303 if (length < 8)
1304 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001305
cristy3ed852e2009-09-05 21:47:34 +00001306 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1307 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001308
cristy3ed852e2009-09-05 21:47:34 +00001309 return(MagickFalse);
1310}
1311
1312#if defined(MAGICKCORE_PNG_DELEGATE)
1313#if defined(__cplusplus) || defined(c_plusplus)
1314extern "C" {
1315#endif
1316
glennrpd5045b42010-03-24 12:40:35 +00001317#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001318static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001319{
1320 unsigned char
1321 buffer[4];
1322
1323 assert(image != (Image *) NULL);
1324 assert(image->signature == MagickSignature);
1325 buffer[0]=(unsigned char) (value >> 24);
1326 buffer[1]=(unsigned char) (value >> 16);
1327 buffer[2]=(unsigned char) (value >> 8);
1328 buffer[3]=(unsigned char) value;
1329 return((size_t) WriteBlob(image,4,buffer));
1330}
1331
1332static void PNGLong(png_bytep p,png_uint_32 value)
1333{
1334 *p++=(png_byte) ((value >> 24) & 0xff);
1335 *p++=(png_byte) ((value >> 16) & 0xff);
1336 *p++=(png_byte) ((value >> 8) & 0xff);
1337 *p++=(png_byte) (value & 0xff);
1338}
1339
glennrpa521b2f2010-10-29 04:11:03 +00001340#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001341static void PNGsLong(png_bytep p,png_int_32 value)
1342{
1343 *p++=(png_byte) ((value >> 24) & 0xff);
1344 *p++=(png_byte) ((value >> 16) & 0xff);
1345 *p++=(png_byte) ((value >> 8) & 0xff);
1346 *p++=(png_byte) (value & 0xff);
1347}
glennrpa521b2f2010-10-29 04:11:03 +00001348#endif
cristy3ed852e2009-09-05 21:47:34 +00001349
1350static void PNGShort(png_bytep p,png_uint_16 value)
1351{
1352 *p++=(png_byte) ((value >> 8) & 0xff);
1353 *p++=(png_byte) (value & 0xff);
1354}
1355
1356static void PNGType(png_bytep p,png_bytep type)
1357{
1358 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1359}
1360
glennrp03812ae2010-12-24 01:31:34 +00001361static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1362 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001363{
1364 if (logging != MagickFalse)
1365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001366 " Writing %c%c%c%c chunk, length: %.20g",
1367 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001368}
glennrpd5045b42010-03-24 12:40:35 +00001369#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001370
1371#if defined(__cplusplus) || defined(c_plusplus)
1372}
1373#endif
1374
glennrpd5045b42010-03-24 12:40:35 +00001375#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001376/*
1377%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1378% %
1379% %
1380% %
1381% R e a d P N G I m a g e %
1382% %
1383% %
1384% %
1385%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1386%
1387% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1388% Multiple-image Network Graphics (MNG) image file and returns it. It
1389% allocates the memory necessary for the new Image structure and returns a
1390% pointer to the new image or set of images.
1391%
1392% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1393%
1394% The format of the ReadPNGImage method is:
1395%
1396% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1397%
1398% A description of each parameter follows:
1399%
1400% o image_info: the image info.
1401%
1402% o exception: return any errors or warnings in this structure.
1403%
1404% To do, more or less in chronological order (as of version 5.5.2,
1405% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1406%
1407% Get 16-bit cheap transparency working.
1408%
1409% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1410%
1411% Preserve all unknown and not-yet-handled known chunks found in input
1412% PNG file and copy them into output PNG files according to the PNG
1413% copying rules.
1414%
1415% (At this point, PNG encoding should be in full MNG compliance)
1416%
1417% Provide options for choice of background to use when the MNG BACK
1418% chunk is not present or is not mandatory (i.e., leave transparent,
1419% user specified, MNG BACK, PNG bKGD)
1420%
1421% Implement LOOP/ENDL [done, but could do discretionary loops more
1422% efficiently by linking in the duplicate frames.].
1423%
1424% Decode and act on the MHDR simplicity profile (offer option to reject
1425% files or attempt to process them anyway when the profile isn't LC or VLC).
1426%
1427% Upgrade to full MNG without Delta-PNG.
1428%
1429% o BACK [done a while ago except for background image ID]
1430% o MOVE [done 15 May 1999]
1431% o CLIP [done 15 May 1999]
1432% o DISC [done 19 May 1999]
1433% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1434% o SEEK [partially done 19 May 1999 (discard function only)]
1435% o SHOW
1436% o PAST
1437% o BASI
1438% o MNG-level tEXt/iTXt/zTXt
1439% o pHYg
1440% o pHYs
1441% o sBIT
1442% o bKGD
1443% o iTXt (wait for libpng implementation).
1444%
1445% Use the scene signature to discover when an identical scene is
1446% being reused, and just point to the original image->exception instead
1447% of storing another set of pixels. This not specific to MNG
1448% but could be applied generally.
1449%
1450% Upgrade to full MNG with Delta-PNG.
1451%
1452% JNG tEXt/iTXt/zTXt
1453%
1454% We will not attempt to read files containing the CgBI chunk.
1455% They are really Xcode files meant for display on the iPhone.
1456% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001457% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001458% since irretrievable loss of color data has occurred due to the
1459% use of premultiplied alpha.
1460*/
1461
1462#if defined(__cplusplus) || defined(c_plusplus)
1463extern "C" {
1464#endif
1465
1466/*
1467 This the function that does the actual reading of data. It is
1468 the same as the one supplied in libpng, except that it receives the
1469 datastream from the ReadBlob() function instead of standard input.
1470*/
1471static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1472{
1473 Image
1474 *image;
1475
1476 image=(Image *) png_get_io_ptr(png_ptr);
1477 if (length)
1478 {
1479 png_size_t
1480 check;
1481
1482 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1483 if (check != length)
1484 {
1485 char
1486 msg[MaxTextExtent];
1487
cristy3b6fd2e2011-05-20 12:53:50 +00001488 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001489 "Expected %.20g bytes; found %.20g bytes",(double) length,
1490 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001491 png_warning(png_ptr,msg);
1492 png_error(png_ptr,"Read Exception");
1493 }
1494 }
1495}
1496
1497#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1498 !defined(PNG_MNG_FEATURES_SUPPORTED)
1499/* We use mng_get_data() instead of png_get_data() if we have a libpng
1500 * older than libpng-1.0.3a, which was the first to allow the empty
1501 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1502 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1503 * encountered after an empty PLTE, so we have to look ahead for bKGD
1504 * chunks and remove them from the datastream that is passed to libpng,
1505 * and store their contents for later use.
1506 */
1507static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1508{
1509 MngInfo
1510 *mng_info;
1511
1512 Image
1513 *image;
1514
1515 png_size_t
1516 check;
1517
cristybb503372010-05-27 20:51:26 +00001518 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001519 i;
1520
1521 i=0;
1522 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1523 image=(Image *) mng_info->image;
1524 while (mng_info->bytes_in_read_buffer && length)
1525 {
1526 data[i]=mng_info->read_buffer[i];
1527 mng_info->bytes_in_read_buffer--;
1528 length--;
1529 i++;
1530 }
1531 if (length)
1532 {
1533 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001534
cristy3ed852e2009-09-05 21:47:34 +00001535 if (check != length)
1536 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001537
cristy3ed852e2009-09-05 21:47:34 +00001538 if (length == 4)
1539 {
1540 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1541 (data[3] == 0))
1542 {
1543 check=(png_size_t) ReadBlob(image,(size_t) length,
1544 (char *) mng_info->read_buffer);
1545 mng_info->read_buffer[4]=0;
1546 mng_info->bytes_in_read_buffer=4;
1547 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1548 mng_info->found_empty_plte=MagickTrue;
1549 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1550 {
1551 mng_info->found_empty_plte=MagickFalse;
1552 mng_info->have_saved_bkgd_index=MagickFalse;
1553 }
1554 }
glennrp0fe50b42010-11-16 03:52:51 +00001555
cristy3ed852e2009-09-05 21:47:34 +00001556 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1557 (data[3] == 1))
1558 {
1559 check=(png_size_t) ReadBlob(image,(size_t) length,
1560 (char *) mng_info->read_buffer);
1561 mng_info->read_buffer[4]=0;
1562 mng_info->bytes_in_read_buffer=4;
1563 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1564 if (mng_info->found_empty_plte)
1565 {
1566 /*
1567 Skip the bKGD data byte and CRC.
1568 */
1569 check=(png_size_t)
1570 ReadBlob(image,5,(char *) mng_info->read_buffer);
1571 check=(png_size_t) ReadBlob(image,(size_t) length,
1572 (char *) mng_info->read_buffer);
1573 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1574 mng_info->have_saved_bkgd_index=MagickTrue;
1575 mng_info->bytes_in_read_buffer=0;
1576 }
1577 }
1578 }
1579 }
1580}
1581#endif
1582
1583static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1584{
1585 Image
1586 *image;
1587
1588 image=(Image *) png_get_io_ptr(png_ptr);
1589 if (length)
1590 {
1591 png_size_t
1592 check;
1593
cristybb503372010-05-27 20:51:26 +00001594 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001595
cristy3ed852e2009-09-05 21:47:34 +00001596 if (check != length)
1597 png_error(png_ptr,"WriteBlob Failed");
1598 }
1599}
1600
1601static void png_flush_data(png_structp png_ptr)
1602{
1603 (void) png_ptr;
1604}
1605
1606#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1607static int PalettesAreEqual(Image *a,Image *b)
1608{
cristybb503372010-05-27 20:51:26 +00001609 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001610 i;
1611
1612 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1613 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001614
cristy3ed852e2009-09-05 21:47:34 +00001615 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1616 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001617
cristy3ed852e2009-09-05 21:47:34 +00001618 if (a->colors != b->colors)
1619 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001620
cristybb503372010-05-27 20:51:26 +00001621 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001622 {
1623 if ((a->colormap[i].red != b->colormap[i].red) ||
1624 (a->colormap[i].green != b->colormap[i].green) ||
1625 (a->colormap[i].blue != b->colormap[i].blue))
1626 return((int) MagickFalse);
1627 }
glennrp0fe50b42010-11-16 03:52:51 +00001628
cristy3ed852e2009-09-05 21:47:34 +00001629 return((int) MagickTrue);
1630}
1631#endif
1632
1633static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1634{
1635 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1636 mng_info->exists[i] && !mng_info->frozen[i])
1637 {
1638#ifdef MNG_OBJECT_BUFFERS
1639 if (mng_info->ob[i] != (MngBuffer *) NULL)
1640 {
1641 if (mng_info->ob[i]->reference_count > 0)
1642 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001643
cristy3ed852e2009-09-05 21:47:34 +00001644 if (mng_info->ob[i]->reference_count == 0)
1645 {
1646 if (mng_info->ob[i]->image != (Image *) NULL)
1647 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001648
cristy3ed852e2009-09-05 21:47:34 +00001649 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1650 }
1651 }
1652 mng_info->ob[i]=(MngBuffer *) NULL;
1653#endif
1654 mng_info->exists[i]=MagickFalse;
1655 mng_info->invisible[i]=MagickFalse;
1656 mng_info->viewable[i]=MagickFalse;
1657 mng_info->frozen[i]=MagickFalse;
1658 mng_info->x_off[i]=0;
1659 mng_info->y_off[i]=0;
1660 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001661 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001662 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001663 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001664 }
1665}
1666
glennrp21f0e622011-01-07 16:20:57 +00001667static void MngInfoFreeStruct(MngInfo *mng_info,
1668 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001669{
glennrp21f0e622011-01-07 16:20:57 +00001670 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001671 {
cristybb503372010-05-27 20:51:26 +00001672 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001673 i;
1674
1675 for (i=1; i < MNG_MAX_OBJECTS; i++)
1676 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001677
cristy3ed852e2009-09-05 21:47:34 +00001678 if (mng_info->global_plte != (png_colorp) NULL)
1679 mng_info->global_plte=(png_colorp)
1680 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001681
cristy3ed852e2009-09-05 21:47:34 +00001682 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1683 *have_mng_structure=MagickFalse;
1684 }
1685}
1686
1687static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1688{
1689 MngBox
1690 box;
1691
1692 box=box1;
1693 if (box.left < box2.left)
1694 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001695
cristy3ed852e2009-09-05 21:47:34 +00001696 if (box.top < box2.top)
1697 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001698
cristy3ed852e2009-09-05 21:47:34 +00001699 if (box.right > box2.right)
1700 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001701
cristy3ed852e2009-09-05 21:47:34 +00001702 if (box.bottom > box2.bottom)
1703 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001704
cristy3ed852e2009-09-05 21:47:34 +00001705 return box;
1706}
1707
1708static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1709{
1710 MngBox
1711 box;
1712
1713 /*
1714 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1715 */
cristybb503372010-05-27 20:51:26 +00001716 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1717 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1718 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1719 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001720 if (delta_type != 0)
1721 {
1722 box.left+=previous_box.left;
1723 box.right+=previous_box.right;
1724 box.top+=previous_box.top;
1725 box.bottom+=previous_box.bottom;
1726 }
glennrp0fe50b42010-11-16 03:52:51 +00001727
cristy3ed852e2009-09-05 21:47:34 +00001728 return(box);
1729}
1730
1731static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1732 unsigned char *p)
1733{
1734 MngPair
1735 pair;
1736 /*
cristybb503372010-05-27 20:51:26 +00001737 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001738 */
cristy8182b072010-05-30 20:10:53 +00001739 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1740 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001741
cristy3ed852e2009-09-05 21:47:34 +00001742 if (delta_type != 0)
1743 {
1744 pair.a+=previous_pair.a;
1745 pair.b+=previous_pair.b;
1746 }
glennrp0fe50b42010-11-16 03:52:51 +00001747
cristy3ed852e2009-09-05 21:47:34 +00001748 return(pair);
1749}
1750
cristy8182b072010-05-30 20:10:53 +00001751static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001752{
cristy8182b072010-05-30 20:10:53 +00001753 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001754}
1755
cristy16ea1392012-03-21 20:38:41 +00001756typedef struct _PNGErrorInfo
cristyc82a27b2011-10-21 01:07:16 +00001757{
cristyc82a27b2011-10-21 01:07:16 +00001758 Image
1759 *image;
1760
cristy16ea1392012-03-21 20:38:41 +00001761 ExceptionInfo
1762 *exception;
1763} PNGErrorInfo;
cristyc82a27b2011-10-21 01:07:16 +00001764
cristy16ea1392012-03-21 20:38:41 +00001765static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1766{
1767 ExceptionInfo
1768 *exception;
cristyc82a27b2011-10-21 01:07:16 +00001769
cristy16ea1392012-03-21 20:38:41 +00001770 Image
1771 *image;
1772
1773 PNGErrorInfo
1774 *error_info;
1775
1776 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1777 image=error_info->image;
1778 exception=error_info->exception;
1779
1780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1781 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1782
1783 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1784 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001785
glennrpe4017e32011-01-08 17:16:09 +00001786#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001787 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1788 * are building with libpng-1.4.x and can be ignored.
1789 */
cristy3ed852e2009-09-05 21:47:34 +00001790 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001791#else
1792 png_longjmp(ping,1);
1793#endif
cristy3ed852e2009-09-05 21:47:34 +00001794}
1795
glennrpcf002022011-01-30 02:38:15 +00001796static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001797{
cristy16ea1392012-03-21 20:38:41 +00001798 ExceptionInfo
1799 *exception;
1800
cristy3ed852e2009-09-05 21:47:34 +00001801 Image
1802 *image;
1803
cristy16ea1392012-03-21 20:38:41 +00001804 PNGErrorInfo
1805 *error_info;
1806
cristy3ed852e2009-09-05 21:47:34 +00001807 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1808 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001809
cristy16ea1392012-03-21 20:38:41 +00001810 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1811 image=error_info->image;
1812 exception=error_info->exception;
1813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1814 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001815
cristy16ea1392012-03-21 20:38:41 +00001816 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001817 message,"`%s'",image->filename);
1818}
1819
1820#ifdef PNG_USER_MEM_SUPPORTED
cristya865ccd2012-07-28 00:33:10 +00001821#if PNG_LIBPNG_VER >= 14000
1822static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1823#else
1824static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1825#endif
cristy3ed852e2009-09-05 21:47:34 +00001826{
cristydf0d90e2011-12-12 01:03:55 +00001827 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001828 return((png_voidp) AcquireMagickMemory((size_t) size));
cristy3ed852e2009-09-05 21:47:34 +00001829}
1830
1831/*
1832 Free a pointer. It is removed from the list at the same time.
1833*/
glennrpcf002022011-01-30 02:38:15 +00001834static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001835{
glennrp3bd393f2011-12-21 18:54:53 +00001836 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001837 ptr=RelinquishMagickMemory(ptr);
1838 return((png_free_ptr) NULL);
1839}
1840#endif
1841
1842#if defined(__cplusplus) || defined(c_plusplus)
1843}
1844#endif
1845
1846static int
glennrpedaa0382012-04-12 14:16:21 +00001847Magick_png_read_raw_profile(png_struct *ping,Image *image,
1848 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001849{
cristybb503372010-05-27 20:51:26 +00001850 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001851 i;
1852
1853 register unsigned char
1854 *dp;
1855
1856 register png_charp
1857 sp;
1858
1859 png_uint_32
1860 length,
1861 nibbles;
1862
1863 StringInfo
1864 *profile;
1865
glennrp0c3e06b2010-11-19 13:45:02 +00001866 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001867 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,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,1, 2,3,4,5,6,7,8,9,0,0,
1870 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1871 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1872 13,14,15};
1873
1874 sp=text[ii].text+1;
1875 /* look for newline */
1876 while (*sp != '\n')
1877 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001878
cristy3ed852e2009-09-05 21:47:34 +00001879 /* look for length */
1880 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1881 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001882
cristyf2f27272009-12-17 14:48:46 +00001883 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001884
glennrp97f90e22011-02-22 05:47:58 +00001885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1886 " length: %lu",(unsigned long) length);
1887
cristy3ed852e2009-09-05 21:47:34 +00001888 while (*sp != ' ' && *sp != '\n')
1889 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001890
cristy3ed852e2009-09-05 21:47:34 +00001891 /* allocate space */
1892 if (length == 0)
1893 {
glennrpedaa0382012-04-12 14:16:21 +00001894 png_warning(ping,"invalid profile length");
cristy3ed852e2009-09-05 21:47:34 +00001895 return(MagickFalse);
1896 }
glennrp0fe50b42010-11-16 03:52:51 +00001897
cristy8723e4b2011-09-01 13:11:19 +00001898 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001899
cristy3ed852e2009-09-05 21:47:34 +00001900 if (profile == (StringInfo *) NULL)
1901 {
glennrpedaa0382012-04-12 14:16:21 +00001902 png_warning(ping, "unable to copy profile");
cristy3ed852e2009-09-05 21:47:34 +00001903 return(MagickFalse);
1904 }
glennrp0fe50b42010-11-16 03:52:51 +00001905
cristy3ed852e2009-09-05 21:47:34 +00001906 /* copy profile, skipping white space and column 1 "=" signs */
1907 dp=GetStringInfoDatum(profile);
1908 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001909
cristybb503372010-05-27 20:51:26 +00001910 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001911 {
1912 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1913 {
1914 if (*sp == '\0')
1915 {
glennrpedaa0382012-04-12 14:16:21 +00001916 png_warning(ping, "ran out of profile data");
cristy3ed852e2009-09-05 21:47:34 +00001917 profile=DestroyStringInfo(profile);
1918 return(MagickFalse);
1919 }
1920 sp++;
1921 }
glennrp0fe50b42010-11-16 03:52:51 +00001922
cristy3ed852e2009-09-05 21:47:34 +00001923 if (i%2 == 0)
1924 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001925
cristy3ed852e2009-09-05 21:47:34 +00001926 else
1927 (*dp++)+=unhex[(int) *sp++];
1928 }
1929 /*
1930 We have already read "Raw profile type.
1931 */
cristy16ea1392012-03-21 20:38:41 +00001932 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001933 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001934
cristy3ed852e2009-09-05 21:47:34 +00001935 if (image_info->verbose)
1936 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001937
cristy3ed852e2009-09-05 21:47:34 +00001938 return MagickTrue;
1939}
1940
1941#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1942static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1943{
1944 Image
1945 *image;
1946
1947
1948 /* The unknown chunk structure contains the chunk data:
1949 png_byte name[5];
1950 png_byte *data;
1951 png_size_t size;
1952
1953 Note that libpng has already taken care of the CRC handling.
1954 */
1955
1956
1957 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1958 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1959 return(0); /* Did not recognize */
1960
1961 /* recognized vpAg */
1962
1963 if (chunk->size != 9)
1964 return(-1); /* Error return */
1965
1966 if (chunk->data[8] != 0)
1967 return(0); /* ImageMagick requires pixel units */
1968
1969 image=(Image *) png_get_user_chunk_ptr(ping);
1970
cristybb503372010-05-27 20:51:26 +00001971 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001972 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001973
cristybb503372010-05-27 20:51:26 +00001974 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001975 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1976
1977 /* Return one of the following: */
1978 /* return(-n); chunk had an error */
1979 /* return(0); did not recognize */
1980 /* return(n); success */
1981
1982 return(1);
1983
1984}
1985#endif
1986
1987/*
1988%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1989% %
1990% %
1991% %
1992% R e a d O n e P N G I m a g e %
1993% %
1994% %
1995% %
1996%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1997%
1998% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1999% (minus the 8-byte signature) and returns it. It allocates the memory
2000% necessary for the new Image structure and returns a pointer to the new
2001% image.
2002%
2003% The format of the ReadOnePNGImage method is:
2004%
2005% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2006% ExceptionInfo *exception)
2007%
2008% A description of each parameter follows:
2009%
2010% o mng_info: Specifies a pointer to a MngInfo structure.
2011%
2012% o image_info: the image info.
2013%
2014% o exception: return any errors or warnings in this structure.
2015%
2016*/
2017static Image *ReadOnePNGImage(MngInfo *mng_info,
2018 const ImageInfo *image_info, ExceptionInfo *exception)
2019{
2020 /* Read one PNG image */
2021
glennrpcc95c3f2011-04-18 16:46:48 +00002022 /* To do: Read the tIME chunk into the date:modify property */
2023 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2024
cristy3ed852e2009-09-05 21:47:34 +00002025 Image
2026 *image;
2027
2028 int
glennrp98b83d42012-07-23 02:50:31 +00002029 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
glennrpcb395ac2011-03-30 19:50:23 +00002030 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002031 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002032 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002033 num_passes,
glennrp913f9612012-06-27 17:50:00 +00002034 number_colors,
glennrpfaa852b2010-03-30 12:17:00 +00002035 pass,
2036 ping_bit_depth,
2037 ping_color_type,
glennrpfcf06162012-11-05 14:57:08 +00002038 ping_file_depth,
glennrpfaa852b2010-03-30 12:17:00 +00002039 ping_interlace_method,
2040 ping_compression_method,
2041 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002042 ping_num_trans,
2043 unit_type;
2044
2045 double
2046 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002047
2048 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002049 logging,
glennrp98b83d42012-07-23 02:50:31 +00002050 ping_found_cHRM,
2051 ping_found_gAMA,
2052 ping_found_iCCP,
2053 ping_found_sRGB,
cristy3ed852e2009-09-05 21:47:34 +00002054 status;
2055
cristy16ea1392012-03-21 20:38:41 +00002056 PixelInfo
2057 transparent_color;
2058
2059 PNGErrorInfo
2060 error_info;
2061
glennrpfaa852b2010-03-30 12:17:00 +00002062 png_bytep
2063 ping_trans_alpha;
2064
2065 png_color_16p
2066 ping_background,
2067 ping_trans_color;
2068
cristy3ed852e2009-09-05 21:47:34 +00002069 png_info
2070 *end_info,
2071 *ping_info;
2072
2073 png_struct
2074 *ping;
2075
2076 png_textp
2077 text;
2078
glennrpfaa852b2010-03-30 12:17:00 +00002079 png_uint_32
2080 ping_height,
2081 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002082 x_resolution,
2083 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002084
cristy16ea1392012-03-21 20:38:41 +00002085 QuantumInfo
2086 *quantum_info;
2087
cristybb503372010-05-27 20:51:26 +00002088 ssize_t
cristy756ae432011-11-19 02:18:25 +00002089 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002090 y;
2091
2092 register unsigned char
2093 *p;
2094
cristybb503372010-05-27 20:51:26 +00002095 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002096 i,
2097 x;
2098
cristy16ea1392012-03-21 20:38:41 +00002099 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002100 *q;
2101
2102 size_t
glennrp39992b42010-11-14 00:03:43 +00002103 length,
cristy3ed852e2009-09-05 21:47:34 +00002104 row_offset;
2105
cristyeb3b22a2011-03-31 20:16:11 +00002106 ssize_t
2107 j;
2108
cristy75fc68f2012-10-08 16:26:00 +00002109 unsigned char
2110 *volatile ping_pixels;
cristy55b78b52012-10-08 14:01:27 +00002111
glennrp629960f2012-05-29 19:13:52 +00002112#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002113 png_byte unused_chunks[]=
2114 {
2115 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2116 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2117 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2118 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2119 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2120 116, 73, 77, 69, (png_byte) '\0', /* tIME */
glennrp629960f2012-05-29 19:13:52 +00002121#ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2122 /* ignore the APNG chunks */
2123 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2124 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2125 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2126#endif
cristy3ed852e2009-09-05 21:47:34 +00002127 };
2128#endif
2129
2130 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002131 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002132
glennrp25c1e2b2010-03-25 01:39:56 +00002133#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002134 if (image_info->verbose)
2135 printf("Your PNG library (libpng-%s) is rather old.\n",
2136 PNG_LIBPNG_VER_STRING);
2137#endif
2138
glennrp61b4c952009-11-10 20:40:41 +00002139#if (PNG_LIBPNG_VER >= 10400)
2140# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2141 if (image_info->verbose)
2142 {
2143 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2144 PNG_LIBPNG_VER_STRING);
2145 printf("Please update it.\n");
2146 }
2147# endif
2148#endif
2149
cristy16ea1392012-03-21 20:38:41 +00002150
2151 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002152 image=mng_info->image;
2153
glennrpa6a06632011-01-19 15:15:34 +00002154 if (logging != MagickFalse)
glennrp98b83d42012-07-23 02:50:31 +00002155 {
glennrpa6a06632011-01-19 15:15:34 +00002156 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00002157 " image->alpha_trait=%d",(int) image->alpha_trait);
glennrpa6a06632011-01-19 15:15:34 +00002158
glennrp98b83d42012-07-23 02:50:31 +00002159 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2160 " image->rendering_intent=%d",(int) image->rendering_intent);
glennrpe88af772012-08-22 13:59:50 +00002161
2162 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2163 " image->colorspace=%d",(int) image->colorspace);
glennrp98b83d42012-07-23 02:50:31 +00002164 }
2165 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2166
glennrp0e319732011-01-25 21:53:13 +00002167 /* Set to an out-of-range color unless tRNS chunk is present */
2168 transparent_color.red=65537;
2169 transparent_color.green=65537;
2170 transparent_color.blue=65537;
cristy16ea1392012-03-21 20:38:41 +00002171 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002172
glennrp913f9612012-06-27 17:50:00 +00002173 number_colors=0;
glennrpcb395ac2011-03-30 19:50:23 +00002174 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002175 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002176 num_raw_profiles = 0;
2177
glennrp98b83d42012-07-23 02:50:31 +00002178 ping_found_cHRM = MagickFalse;
2179 ping_found_gAMA = MagickFalse;
2180 ping_found_iCCP = MagickFalse;
2181 ping_found_sRGB = MagickFalse;
2182
cristy3ed852e2009-09-05 21:47:34 +00002183 /*
2184 Allocate the PNG structures
2185 */
2186#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00002187 error_info.image=image;
2188 error_info.exception=exception;
2189 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002190 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2191 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002192#else
cristy16ea1392012-03-21 20:38:41 +00002193 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002194 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002195#endif
2196 if (ping == (png_struct *) NULL)
2197 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002198
cristy3ed852e2009-09-05 21:47:34 +00002199 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002200
cristy3ed852e2009-09-05 21:47:34 +00002201 if (ping_info == (png_info *) NULL)
2202 {
2203 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2204 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2205 }
glennrp0fe50b42010-11-16 03:52:51 +00002206
cristy3ed852e2009-09-05 21:47:34 +00002207 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002208
cristy3ed852e2009-09-05 21:47:34 +00002209 if (end_info == (png_info *) NULL)
2210 {
2211 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2212 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2213 }
glennrp0fe50b42010-11-16 03:52:51 +00002214
glennrpcf002022011-01-30 02:38:15 +00002215 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002216
glennrpfaa852b2010-03-30 12:17:00 +00002217 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002218 {
2219 /*
2220 PNG image is corrupt.
2221 */
2222 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002223
2224#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002225 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002226#endif
glennrpedaa0382012-04-12 14:16:21 +00002227
2228 if (ping_pixels != (unsigned char *) NULL)
2229 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2230
cristy3ed852e2009-09-05 21:47:34 +00002231 if (logging != MagickFalse)
2232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2233 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002234
cristy3ed852e2009-09-05 21:47:34 +00002235 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002236 {
cristy16ea1392012-03-21 20:38:41 +00002237 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002238 image->columns=0;
2239 }
glennrp0fe50b42010-11-16 03:52:51 +00002240
cristy3ed852e2009-09-05 21:47:34 +00002241 return(GetFirstImageInList(image));
2242 }
glennrpedaa0382012-04-12 14:16:21 +00002243
2244 /* { For navigation to end of SETJMP-protected block. Within this
2245 * block, use png_error() instead of Throwing an Exception, to ensure
2246 * that libpng is able to clean up, and that the semaphore is unlocked.
2247 */
2248
2249#ifdef PNG_SETJMP_NOT_THREAD_SAFE
2250 LockSemaphoreInfo(ping_semaphore);
2251#endif
2252
cristy3ed852e2009-09-05 21:47:34 +00002253 /*
2254 Prepare PNG for reading.
2255 */
glennrpfaa852b2010-03-30 12:17:00 +00002256
cristy3ed852e2009-09-05 21:47:34 +00002257 mng_info->image_found++;
2258 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002259
cristy3ed852e2009-09-05 21:47:34 +00002260 if (LocaleCompare(image_info->magick,"MNG") == 0)
2261 {
2262#if defined(PNG_MNG_FEATURES_SUPPORTED)
2263 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2264 png_set_read_fn(ping,image,png_get_data);
2265#else
2266#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2267 png_permit_empty_plte(ping,MagickTrue);
2268 png_set_read_fn(ping,image,png_get_data);
2269#else
2270 mng_info->image=image;
2271 mng_info->bytes_in_read_buffer=0;
2272 mng_info->found_empty_plte=MagickFalse;
2273 mng_info->have_saved_bkgd_index=MagickFalse;
2274 png_set_read_fn(ping,mng_info,mng_get_data);
2275#endif
2276#endif
2277 }
glennrp0fe50b42010-11-16 03:52:51 +00002278
cristy3ed852e2009-09-05 21:47:34 +00002279 else
2280 png_set_read_fn(ping,image,png_get_data);
2281
2282#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2283 /* Ignore unused chunks and all unknown chunks except for vpAg */
2284 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2285 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2286 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2287 (int)sizeof(unused_chunks)/5);
2288 /* Callback for other unknown chunks */
2289 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2290#endif
2291
glennrp2feb1412013-01-22 15:02:16 +00002292#ifdef PNG_SET_USER_LIMITS_SUPPORTED
glennrp09cd9622013-01-22 15:17:38 +00002293# if (PNG_LIBPNG_VER >= 10400)
glennrp2feb1412013-01-22 15:02:16 +00002294 /* Limit the size of the chunk storage cache used for sPLT, text,
glennrp68736192013-01-24 06:32:08 +00002295 * and unknown chunks.
glennrp2feb1412013-01-22 15:02:16 +00002296 */
glennrp68736192013-01-24 06:32:08 +00002297 png_set_chunk_cache_max(ping, 32767);
glennrp09cd9622013-01-22 15:17:38 +00002298# endif
glennrp2feb1412013-01-22 15:02:16 +00002299#endif
2300
glennrp9bf97b62012-06-06 21:03:14 +00002301#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2302 /* Disable new libpng-1.5.10 feature */
2303 png_set_check_for_invalid_index (ping, 0);
2304#endif
2305
glennrp991e92a2010-01-28 03:09:00 +00002306#if (PNG_LIBPNG_VER < 10400)
2307# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2308 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002309 /* Disable thread-unsafe features of pnggccrd */
2310 if (png_access_version_number() >= 10200)
2311 {
2312 png_uint_32 mmx_disable_mask=0;
2313 png_uint_32 asm_flags;
2314
2315 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2316 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2317 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2318 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2319 asm_flags=png_get_asm_flags(ping);
2320 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2321 }
glennrp991e92a2010-01-28 03:09:00 +00002322# endif
cristy3ed852e2009-09-05 21:47:34 +00002323#endif
2324
2325 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002326
2327 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2328 &ping_bit_depth,&ping_color_type,
2329 &ping_interlace_method,&ping_compression_method,
2330 &ping_filter_method);
2331
glennrpfcf06162012-11-05 14:57:08 +00002332 ping_file_depth = ping_bit_depth;
2333
glennrp5830fbc2013-01-27 06:11:45 +00002334 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2335 {
2336 char
2337 msg[MaxTextExtent];
2338
2339 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2340 (void) SetImageProperty(image,"png:IHDR.color-type-orig ",msg,exception);
2341
2342 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2343 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig ",msg,exception);
2344 }
2345
glennrpfaa852b2010-03-30 12:17:00 +00002346 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2347 &ping_trans_color);
2348
2349 (void) png_get_bKGD(ping, ping_info, &ping_background);
2350
2351 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002352 {
glennrpfcf06162012-11-05 14:57:08 +00002353 png_set_packing(ping);
2354 ping_bit_depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00002355 }
glennrpfaa852b2010-03-30 12:17:00 +00002356
2357 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002358 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002359 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
glennrp98b83d42012-07-23 02:50:31 +00002360
cristy176b29a2012-06-21 13:35:15 +00002361 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2362 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2363 {
2364 image->rendering_intent=UndefinedIntent;
glennrp98b83d42012-07-23 02:50:31 +00002365 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
cristy176b29a2012-06-21 13:35:15 +00002366 image->gamma=1.000;
2367 (void) ResetMagickMemory(&image->chromaticity,0,
2368 sizeof(image->chromaticity));
2369 }
glennrp98b83d42012-07-23 02:50:31 +00002370
cristy3ed852e2009-09-05 21:47:34 +00002371 if (logging != MagickFalse)
2372 {
2373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002374 " PNG width: %.20g, height: %.20g",
2375 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002376
cristy3ed852e2009-09-05 21:47:34 +00002377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2378 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002379 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002380
cristy3ed852e2009-09-05 21:47:34 +00002381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2382 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002383 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002384
cristy3ed852e2009-09-05 21:47:34 +00002385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2386 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002387 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002388 }
2389
glennrp98b83d42012-07-23 02:50:31 +00002390 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2391 {
2392 ping_found_gAMA=MagickTrue;
2393 if (logging != MagickFalse)
2394 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2395 " Found PNG gAMA chunk.");
2396 }
2397
2398 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2399 {
2400 ping_found_cHRM=MagickTrue;
2401 if (logging != MagickFalse)
2402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2403 " Found PNG cHRM chunk.");
2404 }
2405
2406 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2407 {
2408 ping_found_iCCP=MagickTrue;
2409 if (logging != MagickFalse)
2410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2411 " Found PNG iCCP chunk.");
2412 }
2413
2414 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2415 {
2416 ping_found_sRGB=MagickTrue;
2417 if (logging != MagickFalse)
2418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2419 " Found PNG sRGB chunk.");
2420 }
2421
glennrpfaa852b2010-03-30 12:17:00 +00002422#ifdef PNG_READ_iCCP_SUPPORTED
2423 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002424 {
2425 int
2426 compression;
2427
glennrpe4017e32011-01-08 17:16:09 +00002428#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002429 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002430 info;
2431#else
2432 png_bytep
2433 info;
2434#endif
2435
2436 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002437 name;
2438
2439 png_uint_32
2440 profile_length;
2441
2442 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2443 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002444
cristy3ed852e2009-09-05 21:47:34 +00002445 if (profile_length != 0)
2446 {
2447 StringInfo
2448 *profile;
2449
2450 if (logging != MagickFalse)
2451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2452 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002453 profile=BlobToStringInfo(info,profile_length);
2454 if (profile == (StringInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00002455 {
2456 png_warning(ping, "ICC profile is NULL");
2457 profile=DestroyStringInfo(profile);
2458 }
2459 else
2460 {
2461 (void) SetImageProfile(image,"icc",profile,exception);
2462 profile=DestroyStringInfo(profile);
2463 }
cristy3ed852e2009-09-05 21:47:34 +00002464 }
2465 }
2466#endif
2467#if defined(PNG_READ_sRGB_SUPPORTED)
2468 {
cristy3ed852e2009-09-05 21:47:34 +00002469 if (mng_info->have_global_srgb)
cristy2ea7a8e2012-02-08 01:04:50 +00002470 {
cristy2ea7a8e2012-02-08 01:04:50 +00002471 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2472 (mng_info->global_srgb_intent);
2473 }
glennrp0fe50b42010-11-16 03:52:51 +00002474
cristy3ed852e2009-09-05 21:47:34 +00002475 if (png_get_sRGB(ping,ping_info,&intent))
2476 {
glennrpcf002022011-01-30 02:38:15 +00002477 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2478 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002479
cristy3ed852e2009-09-05 21:47:34 +00002480 if (logging != MagickFalse)
2481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002482 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002483 }
2484 }
2485#endif
2486 {
glennrpfaa852b2010-03-30 12:17:00 +00002487 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2488 if (mng_info->have_global_gama)
2489 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002490
cristy3ed852e2009-09-05 21:47:34 +00002491 if (png_get_gAMA(ping,ping_info,&file_gamma))
2492 {
2493 image->gamma=(float) file_gamma;
2494 if (logging != MagickFalse)
2495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2496 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2497 }
2498 }
glennrp98b83d42012-07-23 02:50:31 +00002499
glennrpfaa852b2010-03-30 12:17:00 +00002500 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2501 {
2502 if (mng_info->have_global_chrm != MagickFalse)
2503 {
2504 (void) png_set_cHRM(ping,ping_info,
2505 mng_info->global_chrm.white_point.x,
2506 mng_info->global_chrm.white_point.y,
2507 mng_info->global_chrm.red_primary.x,
2508 mng_info->global_chrm.red_primary.y,
2509 mng_info->global_chrm.green_primary.x,
2510 mng_info->global_chrm.green_primary.y,
2511 mng_info->global_chrm.blue_primary.x,
2512 mng_info->global_chrm.blue_primary.y);
2513 }
2514 }
glennrp0fe50b42010-11-16 03:52:51 +00002515
glennrpfaa852b2010-03-30 12:17:00 +00002516 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002517 {
2518 (void) png_get_cHRM(ping,ping_info,
2519 &image->chromaticity.white_point.x,
2520 &image->chromaticity.white_point.y,
2521 &image->chromaticity.red_primary.x,
2522 &image->chromaticity.red_primary.y,
2523 &image->chromaticity.green_primary.x,
2524 &image->chromaticity.green_primary.y,
2525 &image->chromaticity.blue_primary.x,
2526 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002527
glennrp5cf1bff2013-01-16 02:44:03 +00002528 ping_found_cHRM=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00002529 }
glennrp0fe50b42010-11-16 03:52:51 +00002530
glennrpe610a072010-08-05 17:08:46 +00002531 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002532 {
glennrp5cf1bff2013-01-16 02:44:03 +00002533 if (ping_found_sRGB != MagickTrue)
2534 {
2535 png_set_sRGB(ping,ping_info,
2536 Magick_RenderingIntent_to_PNG_RenderingIntent
2537 (image->rendering_intent));
2538 png_set_gAMA(ping,ping_info,1.000f/2.200f);
2539 file_gamma=1.000f/2.200f;
2540 ping_found_sRGB=MagickTrue;
2541 ping_found_cHRM=MagickTrue;
2542 }
cristy3ed852e2009-09-05 21:47:34 +00002543 }
glennrp5cf1bff2013-01-16 02:44:03 +00002544
cristy3ed852e2009-09-05 21:47:34 +00002545#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002546 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002547 {
cristy905ef802011-02-23 00:29:18 +00002548 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2549 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002550
cristy3ed852e2009-09-05 21:47:34 +00002551 if (logging != MagickFalse)
2552 if (image->page.x || image->page.y)
2553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002554 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2555 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002556 }
2557#endif
2558#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002559 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2560 {
2561 if (mng_info->have_global_phys)
2562 {
2563 png_set_pHYs(ping,ping_info,
2564 mng_info->global_x_pixels_per_unit,
2565 mng_info->global_y_pixels_per_unit,
2566 mng_info->global_phys_unit_type);
2567 }
2568 }
2569
2570 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002571 {
cristy3ed852e2009-09-05 21:47:34 +00002572 /*
2573 Set image resolution.
2574 */
2575 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002576 &unit_type);
cristy16ea1392012-03-21 20:38:41 +00002577 image->resolution.x=(double) x_resolution;
2578 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002579
cristy3ed852e2009-09-05 21:47:34 +00002580 if (unit_type == PNG_RESOLUTION_METER)
2581 {
2582 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00002583 image->resolution.x=(double) x_resolution/100.0;
2584 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002585 }
glennrp0fe50b42010-11-16 03:52:51 +00002586
cristy3ed852e2009-09-05 21:47:34 +00002587 if (logging != MagickFalse)
2588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002589 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2590 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002591 }
cristy3ed852e2009-09-05 21:47:34 +00002592#endif
glennrp823b55c2011-03-14 18:46:46 +00002593
glennrpfaa852b2010-03-30 12:17:00 +00002594 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002595 {
cristy3ed852e2009-09-05 21:47:34 +00002596 png_colorp
2597 palette;
2598
2599 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002600
cristy3ed852e2009-09-05 21:47:34 +00002601 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002602 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002603 {
2604 if (mng_info->global_plte_length)
2605 {
2606 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2607 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002608
glennrpfaa852b2010-03-30 12:17:00 +00002609 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrpedaa0382012-04-12 14:16:21 +00002610 {
cristy3ed852e2009-09-05 21:47:34 +00002611 if (mng_info->global_trns_length)
2612 {
glennrpedaa0382012-04-12 14:16:21 +00002613 png_warning(ping,
2614 "global tRNS has more entries than global PLTE");
cristy3ed852e2009-09-05 21:47:34 +00002615 }
glennrpedaa0382012-04-12 14:16:21 +00002616 else
2617 {
2618 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2619 (int) mng_info->global_trns_length,NULL);
2620 }
2621 }
glennrpbfd9e612011-04-22 14:02:20 +00002622#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002623 if (
2624#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2625 mng_info->have_saved_bkgd_index ||
2626#endif
glennrpfaa852b2010-03-30 12:17:00 +00002627 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002628 {
2629 png_color_16
2630 background;
2631
2632#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2633 if (mng_info->have_saved_bkgd_index)
2634 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002635#endif
glennrpfaa852b2010-03-30 12:17:00 +00002636 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2637 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002638
cristy3ed852e2009-09-05 21:47:34 +00002639 background.red=(png_uint_16)
2640 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002641
cristy3ed852e2009-09-05 21:47:34 +00002642 background.green=(png_uint_16)
2643 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002644
cristy3ed852e2009-09-05 21:47:34 +00002645 background.blue=(png_uint_16)
2646 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002647
glennrpc6c391a2011-04-27 02:23:56 +00002648 background.gray=(png_uint_16)
2649 mng_info->global_plte[background.index].green;
2650
cristy3ed852e2009-09-05 21:47:34 +00002651 png_set_bKGD(ping,ping_info,&background);
2652 }
2653#endif
2654 }
2655 else
glennrpedaa0382012-04-12 14:16:21 +00002656 png_error(ping,"No global PLTE in file");
cristy3ed852e2009-09-05 21:47:34 +00002657 }
2658 }
2659
glennrpbfd9e612011-04-22 14:02:20 +00002660#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002661 if (mng_info->have_global_bkgd &&
2662 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002663 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002664
glennrpfaa852b2010-03-30 12:17:00 +00002665 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002666 {
glennrpbfd9e612011-04-22 14:02:20 +00002667 unsigned int
2668 bkgd_scale;
2669
cristy3ed852e2009-09-05 21:47:34 +00002670 /*
2671 Set image background color.
2672 */
2673 if (logging != MagickFalse)
2674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2675 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002676
glennrpbfd9e612011-04-22 14:02:20 +00002677 /* Scale background components to 16-bit, then scale
2678 * to quantum depth
2679 */
2680 if (logging != MagickFalse)
2681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2682 " raw ping_background=(%d,%d,%d).",ping_background->red,
2683 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002684
glennrpbfd9e612011-04-22 14:02:20 +00002685 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002686
glennrpfcf06162012-11-05 14:57:08 +00002687 if (ping_file_depth == 1)
glennrpbfd9e612011-04-22 14:02:20 +00002688 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002689
glennrpfcf06162012-11-05 14:57:08 +00002690 else if (ping_file_depth == 2)
glennrpbfd9e612011-04-22 14:02:20 +00002691 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002692
glennrpfcf06162012-11-05 14:57:08 +00002693 else if (ping_file_depth == 4)
glennrpbfd9e612011-04-22 14:02:20 +00002694 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002695
glennrpfcf06162012-11-05 14:57:08 +00002696 if (ping_file_depth <= 8)
glennrpbfd9e612011-04-22 14:02:20 +00002697 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002698
glennrpbfd9e612011-04-22 14:02:20 +00002699 ping_background->red *= bkgd_scale;
2700 ping_background->green *= bkgd_scale;
2701 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002702
glennrpbfd9e612011-04-22 14:02:20 +00002703 if (logging != MagickFalse)
2704 {
glennrp2cbb4482010-06-02 04:37:24 +00002705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2706 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002707
glennrp2cbb4482010-06-02 04:37:24 +00002708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2709 " ping_background=(%d,%d,%d).",ping_background->red,
2710 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002711 }
glennrp2cbb4482010-06-02 04:37:24 +00002712
glennrpbfd9e612011-04-22 14:02:20 +00002713 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002714 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002715
glennrpbfd9e612011-04-22 14:02:20 +00002716 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002717 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002718
glennrpbfd9e612011-04-22 14:02:20 +00002719 image->background_color.blue=
2720 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002721
cristy16ea1392012-03-21 20:38:41 +00002722 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002723
glennrpbfd9e612011-04-22 14:02:20 +00002724 if (logging != MagickFalse)
2725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2726 " image->background_color=(%.20g,%.20g,%.20g).",
2727 (double) image->background_color.red,
2728 (double) image->background_color.green,
2729 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002730 }
glennrpbfd9e612011-04-22 14:02:20 +00002731#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002732
glennrpfaa852b2010-03-30 12:17:00 +00002733 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002734 {
2735 /*
glennrpa6a06632011-01-19 15:15:34 +00002736 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002737 */
2738 int
2739 max_sample;
2740
cristy35ef8242010-06-03 16:24:13 +00002741 size_t
2742 one=1;
2743
cristy3ed852e2009-09-05 21:47:34 +00002744 if (logging != MagickFalse)
2745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2746 " Reading PNG tRNS chunk.");
2747
glennrpfcf06162012-11-05 14:57:08 +00002748 max_sample = (int) ((one << ping_file_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002749
glennrpfaa852b2010-03-30 12:17:00 +00002750 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2751 (int)ping_trans_color->gray > max_sample) ||
2752 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2753 ((int)ping_trans_color->red > max_sample ||
2754 (int)ping_trans_color->green > max_sample ||
2755 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002756 {
2757 if (logging != MagickFalse)
2758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2759 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002760 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002761 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy8a46d822012-08-28 23:32:39 +00002762 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00002763 }
2764 else
2765 {
glennrpa6a06632011-01-19 15:15:34 +00002766 int
2767 scale_to_short;
2768
glennrpfcf06162012-11-05 14:57:08 +00002769 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
glennrpa6a06632011-01-19 15:15:34 +00002770
2771 /* Scale transparent_color to short */
2772 transparent_color.red= scale_to_short*ping_trans_color->red;
2773 transparent_color.green= scale_to_short*ping_trans_color->green;
2774 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy16ea1392012-03-21 20:38:41 +00002775 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002776
glennrpfaa852b2010-03-30 12:17:00 +00002777 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002778 {
glennrp0f111982010-07-07 20:18:33 +00002779 if (logging != MagickFalse)
2780 {
2781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2782 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002783
glennrp0f111982010-07-07 20:18:33 +00002784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00002785 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002786 }
cristy16ea1392012-03-21 20:38:41 +00002787 transparent_color.red=transparent_color.alpha;
2788 transparent_color.green=transparent_color.alpha;
2789 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002790 }
2791 }
2792 }
2793#if defined(PNG_READ_sBIT_SUPPORTED)
2794 if (mng_info->have_global_sbit)
2795 {
glennrpfaa852b2010-03-30 12:17:00 +00002796 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002797 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2798 }
2799#endif
2800 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002801
cristy3ed852e2009-09-05 21:47:34 +00002802 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002803
2804 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2805
cristy3ed852e2009-09-05 21:47:34 +00002806 /*
2807 Initialize image structure.
2808 */
2809 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002810 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002811 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002812 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002813 if (mng_info->mng_type == 0)
2814 {
glennrpfaa852b2010-03-30 12:17:00 +00002815 mng_info->mng_width=ping_width;
2816 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002817 mng_info->frame=mng_info->image_box;
2818 mng_info->clip=mng_info->image_box;
2819 }
glennrp0fe50b42010-11-16 03:52:51 +00002820
cristy3ed852e2009-09-05 21:47:34 +00002821 else
2822 {
2823 image->page.y=mng_info->y_off[mng_info->object_id];
2824 }
glennrp0fe50b42010-11-16 03:52:51 +00002825
cristy3ed852e2009-09-05 21:47:34 +00002826 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002827 image->columns=ping_width;
2828 image->rows=ping_height;
glennrpa6c5d342012-05-14 13:21:17 +00002829
cristy16ea1392012-03-21 20:38:41 +00002830 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2831 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
glennrp8d0bca52012-05-17 02:33:23 +00002832 {
glennrpe88af772012-08-22 13:59:50 +00002833 if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
2834 image->gamma == 1.0) &&
glennrp5cf1bff2013-01-16 02:44:03 +00002835 ping_found_cHRM != MagickTrue && ping_found_sRGB != MagickTrue)
glennrp8d0bca52012-05-17 02:33:23 +00002836 {
2837 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
glennrpe88af772012-08-22 13:59:50 +00002838 * image->colorspace to GRAY, and reset image->chromaticity.
glennrp8d0bca52012-05-17 02:33:23 +00002839 */
2840 SetImageColorspace(image,GRAYColorspace,exception);
2841 }
glennrp8d0bca52012-05-17 02:33:23 +00002842 }
glennrpe88af772012-08-22 13:59:50 +00002843
2844 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2845 " image->colorspace=%d",(int) image->colorspace);
glennrpa6c5d342012-05-14 13:21:17 +00002846
glennrpfaa852b2010-03-30 12:17:00 +00002847 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrp32340ff2012-08-25 17:36:19 +00002848 ((int) ping_bit_depth < 16 &&
2849 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002850 {
cristybefe4d22010-06-07 01:18:58 +00002851 size_t
2852 one;
2853
cristy3ed852e2009-09-05 21:47:34 +00002854 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002855 one=1;
glennrpfcf06162012-11-05 14:57:08 +00002856 image->colors=one << ping_file_depth;
cristy3ed852e2009-09-05 21:47:34 +00002857#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2858 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002859 image->colors=256;
2860#else
2861 if (image->colors > 65536L)
2862 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002863#endif
glennrpfaa852b2010-03-30 12:17:00 +00002864 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002865 {
cristy3ed852e2009-09-05 21:47:34 +00002866 png_colorp
2867 palette;
2868
2869 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002870 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002871
cristy3ed852e2009-09-05 21:47:34 +00002872 if (logging != MagickFalse)
2873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2874 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2875 }
2876 }
2877
2878 if (image->storage_class == PseudoClass)
2879 {
2880 /*
2881 Initialize image colormap.
2882 */
cristy16ea1392012-03-21 20:38:41 +00002883 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
glennrpedaa0382012-04-12 14:16:21 +00002884 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00002885
glennrpfaa852b2010-03-30 12:17:00 +00002886 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002887 {
cristy3ed852e2009-09-05 21:47:34 +00002888 png_colorp
2889 palette;
2890
2891 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002892
glennrp6af6cf12011-04-22 13:05:16 +00002893 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002894 {
2895 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2896 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2897 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2898 }
glennrp6af6cf12011-04-22 13:05:16 +00002899
glennrp67b9c1a2011-04-22 18:47:36 +00002900 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002901 {
2902 image->colormap[i].red=0;
2903 image->colormap[i].green=0;
2904 image->colormap[i].blue=0;
2905 }
cristy3ed852e2009-09-05 21:47:34 +00002906 }
glennrp0fe50b42010-11-16 03:52:51 +00002907
cristy3ed852e2009-09-05 21:47:34 +00002908 else
2909 {
cristybb503372010-05-27 20:51:26 +00002910 size_t
cristy3ed852e2009-09-05 21:47:34 +00002911 scale;
2912
glennrpfcf06162012-11-05 14:57:08 +00002913 scale=(QuantumRange/((1UL << ping_file_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002914
cristy3ed852e2009-09-05 21:47:34 +00002915 if (scale < 1)
2916 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002917
cristybb503372010-05-27 20:51:26 +00002918 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002919 {
2920 image->colormap[i].red=(Quantum) (i*scale);
2921 image->colormap[i].green=(Quantum) (i*scale);
2922 image->colormap[i].blue=(Quantum) (i*scale);
2923 }
2924 }
2925 }
glennrp147bc912011-03-30 18:47:21 +00002926
glennrpcb395ac2011-03-30 19:50:23 +00002927 /* Set some properties for reporting by "identify" */
2928 {
glennrp147bc912011-03-30 18:47:21 +00002929 char
2930 msg[MaxTextExtent];
2931
glennrpfcf06162012-11-05 14:57:08 +00002932 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
glennrp147bc912011-03-30 18:47:21 +00002933 ping_interlace_method in value */
2934
cristy3b6fd2e2011-05-20 12:53:50 +00002935 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002936 "%d, %d",(int) ping_width, (int) ping_height);
cristy16ea1392012-03-21 20:38:41 +00002937 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002938
glennrpfcf06162012-11-05 14:57:08 +00002939 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
cristy16ea1392012-03-21 20:38:41 +00002940 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002941
glennrp5dff4352012-06-06 22:12:04 +00002942 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
2943 (int) ping_color_type,
2944 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
cristy16ea1392012-03-21 20:38:41 +00002945 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002946
glennrp913f9612012-06-27 17:50:00 +00002947 if (ping_interlace_method == 0)
2948 {
2949 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
2950 (int) ping_interlace_method);
2951 }
2952 else if (ping_interlace_method == 1)
2953 {
2954 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
2955 (int) ping_interlace_method);
2956 }
2957 else
2958 {
2959 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
2960 (int) ping_interlace_method);
2961 }
2962 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
2963
2964 if (number_colors != 0)
2965 {
2966 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2967 (int) number_colors);
2968 (void) SetImageProperty(image,"png:PLTE.number_colors ",msg,
2969 exception);
2970 }
glennrpcb395ac2011-03-30 19:50:23 +00002971 }
glennrp147bc912011-03-30 18:47:21 +00002972
cristy3ed852e2009-09-05 21:47:34 +00002973 /*
2974 Read image scanlines.
2975 */
2976 if (image->delay != 0)
2977 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002978
glennrp0ca69b12010-07-26 01:57:52 +00002979 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002980 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2981 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002982 {
glennrp1b888c42012-03-02 15:18:14 +00002983 /* This happens later in non-ping decodes */
2984 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2985 image->storage_class=DirectClass;
2986
cristy3ed852e2009-09-05 21:47:34 +00002987 if (logging != MagickFalse)
2988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002989 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002990 mng_info->scenes_found-1);
2991 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002992
2993#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002994 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002995#endif
glennrpedaa0382012-04-12 14:16:21 +00002996
cristy3ed852e2009-09-05 21:47:34 +00002997 if (logging != MagickFalse)
2998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2999 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00003000
cristy3ed852e2009-09-05 21:47:34 +00003001 return(image);
3002 }
glennrp0fe50b42010-11-16 03:52:51 +00003003
cristy3ed852e2009-09-05 21:47:34 +00003004 if (logging != MagickFalse)
3005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3006 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00003007
cristy3ed852e2009-09-05 21:47:34 +00003008 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00003009 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
3010 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00003011
cristy3ed852e2009-09-05 21:47:34 +00003012 else
glennrpcf002022011-01-30 02:38:15 +00003013 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
3014 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00003015
glennrpcf002022011-01-30 02:38:15 +00003016 if (ping_pixels == (unsigned char *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003017 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003018
cristy3ed852e2009-09-05 21:47:34 +00003019 if (logging != MagickFalse)
3020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3021 " Converting PNG pixels to pixel packets");
3022 /*
3023 Convert PNG pixels to pixel packets.
3024 */
cristy16ea1392012-03-21 20:38:41 +00003025 quantum_info=AcquireQuantumInfo(image_info,image);
3026
3027 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003028 png_error(ping,"Failed to allocate quantum_info");
glennrp0fe50b42010-11-16 03:52:51 +00003029
glennrp4b840d72012-11-22 16:01:16 +00003030 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3031
glennrpc8cbc5d2011-01-01 00:12:34 +00003032 {
3033
3034 MagickBooleanType
3035 found_transparent_pixel;
3036
3037 found_transparent_pixel=MagickFalse;
3038
cristy3ed852e2009-09-05 21:47:34 +00003039 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00003040 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003041 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00003042 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003043 /*
3044 Convert image to DirectClass pixel packets.
3045 */
cristy8a46d822012-08-28 23:32:39 +00003046 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
glennrpc8cbc5d2011-01-01 00:12:34 +00003047 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3048 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
cristyb0a657e2012-08-29 00:45:37 +00003049 BlendPixelTrait : UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00003050
glennrpc8cbc5d2011-01-01 00:12:34 +00003051 for (y=0; y < (ssize_t) image->rows; y++)
3052 {
3053 if (num_passes > 1)
3054 row_offset=ping_rowbytes*y;
3055
3056 else
3057 row_offset=0;
3058
glennrpcf002022011-01-30 02:38:15 +00003059 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc5c31be2012-11-07 18:42:18 +00003060
3061 if (pass < num_passes-1)
3062 continue;
3063
cristy862a33c2012-05-17 22:49:37 +00003064 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003065
cristy16ea1392012-03-21 20:38:41 +00003066 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00003067 break;
3068
cristy16ea1392012-03-21 20:38:41 +00003069 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3070 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3071 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003072
cristy16ea1392012-03-21 20:38:41 +00003073 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3074 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3075 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003076
cristy16ea1392012-03-21 20:38:41 +00003077 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3078 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3079 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003080
cristy16ea1392012-03-21 20:38:41 +00003081 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3082 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3083 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003084
cristy16ea1392012-03-21 20:38:41 +00003085 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3086 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3087 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00003088
glennrpc8cbc5d2011-01-01 00:12:34 +00003089 if (found_transparent_pixel == MagickFalse)
3090 {
3091 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00003092 if (y== 0 && logging != MagickFalse)
3093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3094 " Looking for cheap transparent pixel");
3095
glennrpc8cbc5d2011-01-01 00:12:34 +00003096 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3097 {
glennrp5aa37f62011-01-02 03:07:57 +00003098 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3099 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy16ea1392012-03-21 20:38:41 +00003100 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00003101 {
glennrpa6a06632011-01-19 15:15:34 +00003102 if (logging != MagickFalse)
3103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3104 " ...got one.");
3105
glennrpc8cbc5d2011-01-01 00:12:34 +00003106 found_transparent_pixel = MagickTrue;
3107 break;
3108 }
glennrp4f25bd02011-01-01 18:51:28 +00003109 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3110 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
cristy16ea1392012-03-21 20:38:41 +00003111 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3112 transparent_color.red &&
3113 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3114 transparent_color.green &&
3115 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3116 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00003117 {
glennrpa6a06632011-01-19 15:15:34 +00003118 if (logging != MagickFalse)
3119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3120 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00003121 found_transparent_pixel = MagickTrue;
3122 break;
3123 }
cristy16ea1392012-03-21 20:38:41 +00003124 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00003125 }
3126 }
3127
3128 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3129 {
3130 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3131 image->rows);
3132
3133 if (status == MagickFalse)
3134 break;
3135 }
3136 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3137 break;
3138 }
3139
3140 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3141 {
3142 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003143 if (status == MagickFalse)
3144 break;
3145 }
cristy3ed852e2009-09-05 21:47:34 +00003146 }
cristy3ed852e2009-09-05 21:47:34 +00003147 }
glennrp0fe50b42010-11-16 03:52:51 +00003148
cristy3ed852e2009-09-05 21:47:34 +00003149 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003150
cristy3ed852e2009-09-05 21:47:34 +00003151 for (pass=0; pass < num_passes; pass++)
3152 {
3153 Quantum
3154 *quantum_scanline;
3155
3156 register Quantum
3157 *r;
3158
3159 /*
3160 Convert grayscale image to PseudoClass pixel packets.
3161 */
glennrpc17d96f2011-06-27 01:20:11 +00003162 if (logging != MagickFalse)
3163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3164 " Converting grayscale pixels to pixel packets");
cristy16ea1392012-03-21 20:38:41 +00003165
cristy8a46d822012-08-28 23:32:39 +00003166 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristyb0a657e2012-08-29 00:45:37 +00003167 BlendPixelTrait : UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00003168
cristy3ed852e2009-09-05 21:47:34 +00003169 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
cristyb0a657e2012-08-29 00:45:37 +00003170 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3171 sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003172
cristy3ed852e2009-09-05 21:47:34 +00003173 if (quantum_scanline == (Quantum *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003174 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003175
cristybb503372010-05-27 20:51:26 +00003176 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003177 {
glennrp4f996392013-01-26 22:56:50 +00003178 Quantum
3179 alpha;
3180
cristy3ed852e2009-09-05 21:47:34 +00003181 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003182 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003183
cristy3ed852e2009-09-05 21:47:34 +00003184 else
3185 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003186
glennrpcf002022011-01-30 02:38:15 +00003187 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc5c31be2012-11-07 18:42:18 +00003188
3189 if (pass < num_passes-1)
3190 continue;
3191
glennrp4f996392013-01-26 22:56:50 +00003192 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003193
cristy16ea1392012-03-21 20:38:41 +00003194 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003195 break;
glennrp0fe50b42010-11-16 03:52:51 +00003196
glennrpcf002022011-01-30 02:38:15 +00003197 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003198 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003199
glennrpfaa852b2010-03-30 12:17:00 +00003200 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003201 {
cristy3ed852e2009-09-05 21:47:34 +00003202 case 8:
3203 {
glennrp4f996392013-01-26 22:56:50 +00003204
glennrpfaa852b2010-03-30 12:17:00 +00003205 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003206 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003207 {
glennrpa18d5bc2011-04-23 14:51:34 +00003208 *r++=*p++;
glennrp4f996392013-01-26 22:56:50 +00003209
3210 alpha=ScaleCharToQuantum((unsigned char)*p++);
3211
3212 SetPixelAlpha(image,alpha,q);
3213
3214 if (alpha != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003215 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003216
cristy16ea1392012-03-21 20:38:41 +00003217 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003218 }
glennrp0fe50b42010-11-16 03:52:51 +00003219
cristy3ed852e2009-09-05 21:47:34 +00003220 else
cristybb503372010-05-27 20:51:26 +00003221 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003222 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003223
cristy3ed852e2009-09-05 21:47:34 +00003224 break;
3225 }
glennrp47b9dd52010-11-24 18:12:06 +00003226
cristy3ed852e2009-09-05 21:47:34 +00003227 case 16:
3228 {
cristybb503372010-05-27 20:51:26 +00003229 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003230 {
glennrpc17d96f2011-06-27 01:20:11 +00003231#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003232 size_t
3233 quantum;
3234
3235 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003236 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003237
3238 else
glennrpc17d96f2011-06-27 01:20:11 +00003239 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003240
glennrp58f77c72011-04-23 14:09:09 +00003241 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003242 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003243 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003244
3245 if (ping_color_type == 4)
3246 {
glennrpc17d96f2011-06-27 01:20:11 +00003247 if (image->colors > 256)
3248 quantum=((*p++) << 8);
3249 else
3250 quantum=0;
3251
glennrp58f77c72011-04-23 14:09:09 +00003252 quantum|=(*p++);
glennrp4f996392013-01-26 22:56:50 +00003253
3254 alpha=ScaleShortToQuantum(quantum);
3255 SetPixelAlpha(image,alpha,q);
3256
3257 if (alpha != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003258 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003259
cristy16ea1392012-03-21 20:38:41 +00003260 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003261 }
glennrp58f77c72011-04-23 14:09:09 +00003262
3263#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3264 *r++=(*p++);
3265 p++; /* strip low byte */
3266
3267 if (ping_color_type == 4)
3268 {
cristy16ea1392012-03-21 20:38:41 +00003269 SetPixelAlpha(image,*p++,q);
glennrp4f996392013-01-26 22:56:50 +00003270
cristy16ea1392012-03-21 20:38:41 +00003271 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003272 found_transparent_pixel = MagickTrue;
glennrp4f996392013-01-26 22:56:50 +00003273
glennrp9d0ea4d2011-04-22 18:35:57 +00003274 p++;
cristy16ea1392012-03-21 20:38:41 +00003275 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003276 }
cristy3ed852e2009-09-05 21:47:34 +00003277#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003278 }
glennrp47b9dd52010-11-24 18:12:06 +00003279
cristy3ed852e2009-09-05 21:47:34 +00003280 break;
3281 }
glennrp47b9dd52010-11-24 18:12:06 +00003282
cristy3ed852e2009-09-05 21:47:34 +00003283 default:
3284 break;
3285 }
glennrp3faa9a32011-04-23 14:00:25 +00003286
cristy3ed852e2009-09-05 21:47:34 +00003287 /*
3288 Transfer image scanline.
3289 */
3290 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003291
cristy16ea1392012-03-21 20:38:41 +00003292 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3293
3294 if (q == (Quantum *) NULL)
3295 break;
cristybb503372010-05-27 20:51:26 +00003296 for (x=0; x < (ssize_t) image->columns; x++)
cristy16ea1392012-03-21 20:38:41 +00003297 {
3298 SetPixelIndex(image,*r++,q);
3299 q+=GetPixelChannels(image);
3300 }
glennrp0fe50b42010-11-16 03:52:51 +00003301
cristy3ed852e2009-09-05 21:47:34 +00003302 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3303 break;
glennrp0fe50b42010-11-16 03:52:51 +00003304
cristy7a287bf2010-02-14 02:18:09 +00003305 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3306 {
cristycee97112010-05-28 00:44:52 +00003307 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003308 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003309
cristy7a287bf2010-02-14 02:18:09 +00003310 if (status == MagickFalse)
3311 break;
3312 }
cristy3ed852e2009-09-05 21:47:34 +00003313 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003314
cristy7a287bf2010-02-14 02:18:09 +00003315 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003316 {
3317 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003318
cristy3ed852e2009-09-05 21:47:34 +00003319 if (status == MagickFalse)
3320 break;
3321 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003322
cristy3ed852e2009-09-05 21:47:34 +00003323 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3324 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003325
cristyb0a657e2012-08-29 00:45:37 +00003326 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3327 UndefinedPixelTrait;
glennrpc8cbc5d2011-01-01 00:12:34 +00003328
3329 if (logging != MagickFalse)
3330 {
3331 if (found_transparent_pixel != MagickFalse)
3332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3333 " Found transparent pixel");
3334 else
glennrp5aa37f62011-01-02 03:07:57 +00003335 {
3336 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3337 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003338
glennrp5aa37f62011-01-02 03:07:57 +00003339 ping_color_type&=0x03;
3340 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003341 }
3342 }
3343
cristy16ea1392012-03-21 20:38:41 +00003344 if (quantum_info != (QuantumInfo *) NULL)
3345 quantum_info=DestroyQuantumInfo(quantum_info);
3346
cristy5c6f7892010-05-05 22:53:29 +00003347 if (image->storage_class == PseudoClass)
3348 {
cristyb0a657e2012-08-29 00:45:37 +00003349 PixelTrait
3350 alpha_trait;
cristy5c6f7892010-05-05 22:53:29 +00003351
cristyb0a657e2012-08-29 00:45:37 +00003352 alpha_trait=image->alpha_trait;
cristy8a46d822012-08-28 23:32:39 +00003353 image->alpha_trait=UndefinedPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003354 (void) SyncImage(image,exception);
cristyb0a657e2012-08-29 00:45:37 +00003355 image->alpha_trait=alpha_trait;
cristy5c6f7892010-05-05 22:53:29 +00003356 }
glennrp47b9dd52010-11-24 18:12:06 +00003357
glennrp4eb39312011-03-30 21:34:55 +00003358 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003359
3360 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003361 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003362 {
3363 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003364 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003365 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00003366 (void) SetImageBackgroundColor(image,exception);
glennrpedaa0382012-04-12 14:16:21 +00003367#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00003368 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003369#endif
3370 if (logging != MagickFalse)
3371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3372 " exit ReadOnePNGImage() early.");
3373 return(image);
3374 }
glennrp47b9dd52010-11-24 18:12:06 +00003375
glennrpfaa852b2010-03-30 12:17:00 +00003376 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003377 {
3378 ClassType
3379 storage_class;
3380
3381 /*
3382 Image has a transparent background.
3383 */
3384 storage_class=image->storage_class;
cristy8a46d822012-08-28 23:32:39 +00003385 image->alpha_trait=BlendPixelTrait;
glennrpc11cf6a2010-03-20 16:46:19 +00003386
glennrp3c218112010-11-27 15:31:26 +00003387/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003388
glennrp0fe50b42010-11-16 03:52:51 +00003389 if (storage_class == PseudoClass)
3390 {
3391 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003392 {
glennrp0fe50b42010-11-16 03:52:51 +00003393 for (x=0; x < ping_num_trans; x++)
3394 {
cristy8a46d822012-08-28 23:32:39 +00003395 image->colormap[x].alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003396 image->colormap[x].alpha =
3397 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003398 }
glennrpc11cf6a2010-03-20 16:46:19 +00003399 }
glennrp47b9dd52010-11-24 18:12:06 +00003400
glennrp0fe50b42010-11-16 03:52:51 +00003401 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3402 {
3403 for (x=0; x < (int) image->colors; x++)
3404 {
3405 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy16ea1392012-03-21 20:38:41 +00003406 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003407 {
cristy8a46d822012-08-28 23:32:39 +00003408 image->colormap[x].alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00003409 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003410 }
3411 }
3412 }
cristy16ea1392012-03-21 20:38:41 +00003413 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003414 }
glennrp47b9dd52010-11-24 18:12:06 +00003415
glennrpa6a06632011-01-19 15:15:34 +00003416#if 1 /* Should have already been done above, but glennrp problem P10
3417 * needs this.
3418 */
glennrp0fe50b42010-11-16 03:52:51 +00003419 else
3420 {
3421 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003422 {
glennrp0fe50b42010-11-16 03:52:51 +00003423 image->storage_class=storage_class;
3424 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3425
cristy16ea1392012-03-21 20:38:41 +00003426 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003427 break;
3428
glennrp0fe50b42010-11-16 03:52:51 +00003429
glennrpa6a06632011-01-19 15:15:34 +00003430 /* Caution: on a Q8 build, this does not distinguish between
3431 * 16-bit colors that differ only in the low byte
3432 */
glennrp0fe50b42010-11-16 03:52:51 +00003433 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3434 {
cristy16ea1392012-03-21 20:38:41 +00003435 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3436 transparent_color.red &&
3437 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3438 transparent_color.green &&
3439 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3440 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003441 {
cristy16ea1392012-03-21 20:38:41 +00003442 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003443 }
glennrp0fe50b42010-11-16 03:52:51 +00003444
glennrp67b9c1a2011-04-22 18:47:36 +00003445#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003446 else
glennrp4f25bd02011-01-01 18:51:28 +00003447 {
cristy16ea1392012-03-21 20:38:41 +00003448 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003449 }
glennrpa6a06632011-01-19 15:15:34 +00003450#endif
glennrp0fe50b42010-11-16 03:52:51 +00003451
cristy16ea1392012-03-21 20:38:41 +00003452 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003453 }
3454
3455 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3456 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003457 }
glennrp0fe50b42010-11-16 03:52:51 +00003458 }
glennrpa6a06632011-01-19 15:15:34 +00003459#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003460
cristy3ed852e2009-09-05 21:47:34 +00003461 image->storage_class=DirectClass;
3462 }
glennrp3c218112010-11-27 15:31:26 +00003463
cristyeb3b22a2011-03-31 20:16:11 +00003464 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003465 {
3466 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003467 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3468 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003469 else
glennrpa0ed0092011-04-18 16:36:29 +00003470 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3471 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003472
glennrp4eb39312011-03-30 21:34:55 +00003473 if (status != MagickFalse)
3474 for (i=0; i < (ssize_t) num_text; i++)
3475 {
3476 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003477
glennrp4eb39312011-03-30 21:34:55 +00003478 if (logging != MagickFalse)
3479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3480 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003481
glennrp4eb39312011-03-30 21:34:55 +00003482 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003483 {
glennrpedaa0382012-04-12 14:16:21 +00003484 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3485 (int) i,exception);
glennrp4eb39312011-03-30 21:34:55 +00003486 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003487 }
glennrp0fe50b42010-11-16 03:52:51 +00003488
glennrp4eb39312011-03-30 21:34:55 +00003489 else
3490 {
3491 char
3492 *value;
3493
3494 length=text[i].text_length;
3495 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3496 sizeof(*value));
3497 if (value == (char *) NULL)
3498 {
glennrpedaa0382012-04-12 14:16:21 +00003499 png_error(ping,"Memory allocation failed");
glennrp4eb39312011-03-30 21:34:55 +00003500 break;
3501 }
3502 *value='\0';
3503 (void) ConcatenateMagickString(value,text[i].text,length+2);
3504
3505 /* Don't save "density" or "units" property if we have a pHYs
3506 * chunk
3507 */
3508 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3509 (LocaleCompare(text[i].key,"density") != 0 &&
3510 LocaleCompare(text[i].key,"units") != 0))
cristy16ea1392012-03-21 20:38:41 +00003511 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003512
3513 if (logging != MagickFalse)
3514 {
3515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3516 " length: %lu",(unsigned long) length);
3517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3518 " Keyword: %s",text[i].key);
3519 }
3520
3521 value=DestroyString(value);
3522 }
3523 }
3524 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003525 }
glennrp3c218112010-11-27 15:31:26 +00003526
cristy3ed852e2009-09-05 21:47:34 +00003527#ifdef MNG_OBJECT_BUFFERS
3528 /*
3529 Store the object if necessary.
3530 */
3531 if (object_id && !mng_info->frozen[object_id])
3532 {
3533 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3534 {
3535 /*
3536 create a new object buffer.
3537 */
3538 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003539 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003540
cristy3ed852e2009-09-05 21:47:34 +00003541 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3542 {
3543 mng_info->ob[object_id]->image=(Image *) NULL;
3544 mng_info->ob[object_id]->reference_count=1;
3545 }
3546 }
glennrp47b9dd52010-11-24 18:12:06 +00003547
cristy3ed852e2009-09-05 21:47:34 +00003548 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3549 mng_info->ob[object_id]->frozen)
3550 {
3551 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003552 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003553
cristy3ed852e2009-09-05 21:47:34 +00003554 if (mng_info->ob[object_id]->frozen)
glennrpedaa0382012-04-12 14:16:21 +00003555 png_error(ping,"Cannot overwrite frozen MNG object buffer");
cristy3ed852e2009-09-05 21:47:34 +00003556 }
glennrp0fe50b42010-11-16 03:52:51 +00003557
cristy3ed852e2009-09-05 21:47:34 +00003558 else
3559 {
cristy3ed852e2009-09-05 21:47:34 +00003560
3561 if (mng_info->ob[object_id]->image != (Image *) NULL)
3562 mng_info->ob[object_id]->image=DestroyImage
3563 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003564
cristy3ed852e2009-09-05 21:47:34 +00003565 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristy16ea1392012-03-21 20:38:41 +00003566 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003567
cristy3ed852e2009-09-05 21:47:34 +00003568 if (mng_info->ob[object_id]->image != (Image *) NULL)
3569 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003570
cristy3ed852e2009-09-05 21:47:34 +00003571 else
glennrpedaa0382012-04-12 14:16:21 +00003572 png_error(ping, "Cloning image for object buffer failed");
glennrp0fe50b42010-11-16 03:52:51 +00003573
glennrpfaa852b2010-03-30 12:17:00 +00003574 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003575 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003576
glennrpfaa852b2010-03-30 12:17:00 +00003577 mng_info->ob[object_id]->width=ping_width;
3578 mng_info->ob[object_id]->height=ping_height;
3579 mng_info->ob[object_id]->color_type=ping_color_type;
3580 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3581 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3582 mng_info->ob[object_id]->compression_method=
3583 ping_compression_method;
3584 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003585
glennrpfaa852b2010-03-30 12:17:00 +00003586 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003587 {
cristy3ed852e2009-09-05 21:47:34 +00003588 png_colorp
3589 plte;
3590
3591 /*
3592 Copy the PLTE to the object buffer.
3593 */
3594 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3595 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003596
cristy3ed852e2009-09-05 21:47:34 +00003597 for (i=0; i < number_colors; i++)
3598 {
3599 mng_info->ob[object_id]->plte[i]=plte[i];
3600 }
3601 }
glennrp47b9dd52010-11-24 18:12:06 +00003602
cristy3ed852e2009-09-05 21:47:34 +00003603 else
3604 mng_info->ob[object_id]->plte_length=0;
3605 }
3606 }
3607#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003608
cristy8a46d822012-08-28 23:32:39 +00003609 /* Set image->alpha_trait to MagickTrue if the input colortype supports
glennrp0a55b4c2011-03-23 12:38:38 +00003610 * alpha or if a valid tRNS chunk is present, no matter whether there
3611 * is actual transparency present.
3612 */
cristy8a46d822012-08-28 23:32:39 +00003613 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
glennrp0a55b4c2011-03-23 12:38:38 +00003614 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3615 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
cristyb0a657e2012-08-29 00:45:37 +00003616 BlendPixelTrait : UndefinedPixelTrait;
glennrp0a55b4c2011-03-23 12:38:38 +00003617
glennrp224ad8d2013-02-01 17:04:04 +00003618#if 0 /* I'm not sure what's wrong here but it does not work. */
glennrp5830fbc2013-01-27 06:11:45 +00003619 if (image->alpha_trait == BlendPixelTrait)
3620 {
3621 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3622 (void) SetImageType(image,GrayscaleMatteType,exception);
3623
3624 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3625 (void) SetImageType(image,PaletteMatteType,exception);
3626
3627 else
3628 (void) SetImageType(image,TrueColorMatteType,exception);
3629 }
3630
3631 else
3632 {
3633 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3634 (void) SetImageType(image,GrayscaleType,exception);
3635
3636 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3637 (void) SetImageType(image,PaletteType,exception);
3638
3639 else
3640 (void) SetImageType(image,TrueColorType,exception);
3641 }
glennrp224ad8d2013-02-01 17:04:04 +00003642#endif
glennrp5830fbc2013-01-27 06:11:45 +00003643
glennrpcb395ac2011-03-30 19:50:23 +00003644 /* Set more properties for identify to retrieve */
3645 {
3646 char
3647 msg[MaxTextExtent];
3648
glennrp4eb39312011-03-30 21:34:55 +00003649 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003650 {
3651 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003652 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003653 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy16ea1392012-03-21 20:38:41 +00003654 (void) SetImageProperty(image,"png:text ",msg,
3655 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003656 }
3657
3658 if (num_raw_profiles != 0)
3659 {
cristy3b6fd2e2011-05-20 12:53:50 +00003660 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003661 "%d were found", num_raw_profiles);
cristy16ea1392012-03-21 20:38:41 +00003662 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3663 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003664 }
3665
glennrp98b83d42012-07-23 02:50:31 +00003666 if (ping_found_cHRM != MagickFalse)
glennrp59612252011-03-30 21:45:21 +00003667 {
cristy3b6fd2e2011-05-20 12:53:50 +00003668 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003669 "chunk was found (see Chromaticity, above)");
cristy16ea1392012-03-21 20:38:41 +00003670 (void) SetImageProperty(image,"png:cHRM ",msg,
3671 exception);
glennrp59612252011-03-30 21:45:21 +00003672 }
glennrpcb395ac2011-03-30 19:50:23 +00003673
3674 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003675 {
cristy3b6fd2e2011-05-20 12:53:50 +00003676 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003677 "chunk was found (see Background color, above)");
cristy16ea1392012-03-21 20:38:41 +00003678 (void) SetImageProperty(image,"png:bKGD ",msg,
3679 exception);
glennrp59612252011-03-30 21:45:21 +00003680 }
3681
cristy3b6fd2e2011-05-20 12:53:50 +00003682 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003683 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003684
glennrp98b83d42012-07-23 02:50:31 +00003685#if defined(PNG_iCCP_SUPPORTED)
3686 if (ping_found_iCCP != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00003687 (void) SetImageProperty(image,"png:iCCP ",msg,
3688 exception);
glennrp98b83d42012-07-23 02:50:31 +00003689#endif
glennrpcb395ac2011-03-30 19:50:23 +00003690
glennrpcb395ac2011-03-30 19:50:23 +00003691 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy16ea1392012-03-21 20:38:41 +00003692 (void) SetImageProperty(image,"png:tRNS ",msg,
3693 exception);
glennrp4eb39312011-03-30 21:34:55 +00003694
3695#if defined(PNG_sRGB_SUPPORTED)
glennrp98b83d42012-07-23 02:50:31 +00003696 if (ping_found_sRGB != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003697 {
cristy3b6fd2e2011-05-20 12:53:50 +00003698 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp98b83d42012-07-23 02:50:31 +00003699 "intent=%d (%s)",
3700 (int) intent,
3701 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
cristy16ea1392012-03-21 20:38:41 +00003702 (void) SetImageProperty(image,"png:sRGB ",msg,
glennrp98b83d42012-07-23 02:50:31 +00003703 exception);
glennrp4eb39312011-03-30 21:34:55 +00003704 }
3705#endif
3706
glennrp98b83d42012-07-23 02:50:31 +00003707 if (ping_found_gAMA != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003708 {
cristy3b6fd2e2011-05-20 12:53:50 +00003709 (void) FormatLocaleString(msg,MaxTextExtent,
cristy16ea1392012-03-21 20:38:41 +00003710 "gamma=%.8g (See Gamma, above)",
3711 file_gamma);
3712 (void) SetImageProperty(image,"png:gAMA ",msg,
3713 exception);
glennrp4eb39312011-03-30 21:34:55 +00003714 }
3715
3716#if defined(PNG_pHYs_SUPPORTED)
3717 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3718 {
cristy3b6fd2e2011-05-20 12:53:50 +00003719 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003720 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003721 (double) x_resolution,(double) y_resolution, unit_type);
cristy16ea1392012-03-21 20:38:41 +00003722 (void) SetImageProperty(image,"png:pHYs ",msg,
3723 exception);
glennrp4eb39312011-03-30 21:34:55 +00003724 }
3725#endif
3726
3727#if defined(PNG_oFFs_SUPPORTED)
3728 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3729 {
cristy3b6fd2e2011-05-20 12:53:50 +00003730 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003731 (double) image->page.x,(double) image->page.y);
cristy16ea1392012-03-21 20:38:41 +00003732 (void) SetImageProperty(image,"png:oFFs ",msg,
3733 exception);
glennrp4eb39312011-03-30 21:34:55 +00003734 }
3735#endif
3736
glennrp07523c72011-03-31 18:12:10 +00003737 if ((image->page.width != 0 && image->page.width != image->columns) ||
3738 (image->page.height != 0 && image->page.height != image->rows))
3739 {
cristy3b6fd2e2011-05-20 12:53:50 +00003740 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003741 "width=%.20g, height=%.20g",
3742 (double) image->page.width,(double) image->page.height);
cristy16ea1392012-03-21 20:38:41 +00003743 (void) SetImageProperty(image,"png:vpAg ",msg,
3744 exception);
glennrp07523c72011-03-31 18:12:10 +00003745 }
glennrpcb395ac2011-03-30 19:50:23 +00003746 }
3747
cristy3ed852e2009-09-05 21:47:34 +00003748 /*
3749 Relinquish resources.
3750 */
3751 png_destroy_read_struct(&ping,&ping_info,&end_info);
3752
glennrpcf002022011-01-30 02:38:15 +00003753 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003754
3755 if (logging != MagickFalse)
3756 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3757 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003758
glennrpedaa0382012-04-12 14:16:21 +00003759#ifdef PNG_SETJMP_NOT_THREAD_SAFE
3760 UnlockSemaphoreInfo(ping_semaphore);
3761#endif
3762
3763 /* } for navigation to beginning of SETJMP-protected block, revert to
3764 * Throwing an Exception when an error occurs.
3765 */
3766
cristy3ed852e2009-09-05 21:47:34 +00003767 return(image);
3768
3769/* end of reading one PNG image */
3770}
3771
3772static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3773{
3774 Image
3775 *image,
3776 *previous;
3777
3778 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003779 have_mng_structure,
3780 logging,
cristy3ed852e2009-09-05 21:47:34 +00003781 status;
3782
3783 MngInfo
3784 *mng_info;
3785
3786 char
3787 magic_number[MaxTextExtent];
3788
cristy3ed852e2009-09-05 21:47:34 +00003789 ssize_t
3790 count;
3791
3792 /*
3793 Open image file.
3794 */
3795 assert(image_info != (const ImageInfo *) NULL);
3796 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003797
cristy3ed852e2009-09-05 21:47:34 +00003798 if (image_info->debug != MagickFalse)
3799 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3800 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003801
cristy3ed852e2009-09-05 21:47:34 +00003802 assert(exception != (ExceptionInfo *) NULL);
3803 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003804 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy16ea1392012-03-21 20:38:41 +00003805 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003806 mng_info=(MngInfo *) NULL;
3807 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003808
cristy3ed852e2009-09-05 21:47:34 +00003809 if (status == MagickFalse)
3810 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003811
cristy3ed852e2009-09-05 21:47:34 +00003812 /*
3813 Verify PNG signature.
3814 */
3815 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003816
glennrpdde35db2011-02-21 12:06:32 +00003817 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003818 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003819
cristy3ed852e2009-09-05 21:47:34 +00003820 /*
3821 Allocate a MngInfo structure.
3822 */
3823 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003824 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003825
cristy3ed852e2009-09-05 21:47:34 +00003826 if (mng_info == (MngInfo *) NULL)
3827 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003828
cristy3ed852e2009-09-05 21:47:34 +00003829 /*
3830 Initialize members of the MngInfo structure.
3831 */
3832 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3833 mng_info->image=image;
3834 have_mng_structure=MagickTrue;
3835
3836 previous=image;
3837 image=ReadOnePNGImage(mng_info,image_info,exception);
3838 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003839
cristy3ed852e2009-09-05 21:47:34 +00003840 if (image == (Image *) NULL)
3841 {
3842 if (previous != (Image *) NULL)
3843 {
3844 if (previous->signature != MagickSignature)
3845 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003846
cristy3ed852e2009-09-05 21:47:34 +00003847 (void) CloseBlob(previous);
3848 (void) DestroyImageList(previous);
3849 }
glennrp0fe50b42010-11-16 03:52:51 +00003850
cristy3ed852e2009-09-05 21:47:34 +00003851 if (logging != MagickFalse)
3852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3853 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003854
cristy3ed852e2009-09-05 21:47:34 +00003855 return((Image *) NULL);
3856 }
glennrp47b9dd52010-11-24 18:12:06 +00003857
cristy3ed852e2009-09-05 21:47:34 +00003858 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003859
cristy3ed852e2009-09-05 21:47:34 +00003860 if ((image->columns == 0) || (image->rows == 0))
3861 {
3862 if (logging != MagickFalse)
3863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3864 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003865
cristy3ed852e2009-09-05 21:47:34 +00003866 ThrowReaderException(CorruptImageError,"CorruptImage");
3867 }
glennrp47b9dd52010-11-24 18:12:06 +00003868
cristy72715f52012-06-26 17:55:16 +00003869 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
glennrpa8036d62012-11-04 01:46:06 +00003870 ((image->gamma < .45) || (image->gamma > .46)))
cristy72715f52012-06-26 17:55:16 +00003871 SetImageColorspace(image,RGBColorspace,exception);
3872
cristy3ed852e2009-09-05 21:47:34 +00003873 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3875 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3876 (double) image->page.width,(double) image->page.height,
3877 (double) image->page.x,(double) image->page.y);
3878
3879 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003881
cristy3ed852e2009-09-05 21:47:34 +00003882 return(image);
3883}
3884
3885
3886
3887#if defined(JNG_SUPPORTED)
3888/*
3889%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3890% %
3891% %
3892% %
3893% R e a d O n e J N G I m a g e %
3894% %
3895% %
3896% %
3897%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3898%
3899% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3900% (minus the 8-byte signature) and returns it. It allocates the memory
3901% necessary for the new Image structure and returns a pointer to the new
3902% image.
3903%
3904% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3905%
3906% The format of the ReadOneJNGImage method is:
3907%
3908% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3909% ExceptionInfo *exception)
3910%
3911% A description of each parameter follows:
3912%
3913% o mng_info: Specifies a pointer to a MngInfo structure.
3914%
3915% o image_info: the image info.
3916%
3917% o exception: return any errors or warnings in this structure.
3918%
3919*/
3920static Image *ReadOneJNGImage(MngInfo *mng_info,
3921 const ImageInfo *image_info, ExceptionInfo *exception)
3922{
3923 Image
3924 *alpha_image,
3925 *color_image,
3926 *image,
3927 *jng_image;
3928
3929 ImageInfo
3930 *alpha_image_info,
3931 *color_image_info;
3932
cristy4383ec82011-01-05 15:42:32 +00003933 MagickBooleanType
3934 logging;
3935
cristybb503372010-05-27 20:51:26 +00003936 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003937 y;
3938
3939 MagickBooleanType
3940 status;
3941
3942 png_uint_32
3943 jng_height,
3944 jng_width;
3945
3946 png_byte
3947 jng_color_type,
3948 jng_image_sample_depth,
3949 jng_image_compression_method,
3950 jng_image_interlace_method,
3951 jng_alpha_sample_depth,
3952 jng_alpha_compression_method,
3953 jng_alpha_filter_method,
3954 jng_alpha_interlace_method;
3955
cristy16ea1392012-03-21 20:38:41 +00003956 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003957 *s;
3958
cristybb503372010-05-27 20:51:26 +00003959 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003960 i,
3961 x;
3962
cristy16ea1392012-03-21 20:38:41 +00003963 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003964 *q;
3965
3966 register unsigned char
3967 *p;
3968
3969 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003970 read_JSEP,
3971 reading_idat,
3972 skip_to_iend;
3973
cristybb503372010-05-27 20:51:26 +00003974 size_t
cristy3ed852e2009-09-05 21:47:34 +00003975 length;
3976
3977 jng_alpha_compression_method=0;
3978 jng_alpha_sample_depth=8;
3979 jng_color_type=0;
3980 jng_height=0;
3981 jng_width=0;
3982 alpha_image=(Image *) NULL;
3983 color_image=(Image *) NULL;
3984 alpha_image_info=(ImageInfo *) NULL;
3985 color_image_info=(ImageInfo *) NULL;
3986
3987 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003988 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003989
3990 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003991
cristy16ea1392012-03-21 20:38:41 +00003992 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003993 {
3994 /*
3995 Allocate next image structure.
3996 */
3997 if (logging != MagickFalse)
3998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3999 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004000
cristy16ea1392012-03-21 20:38:41 +00004001 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004002
cristy3ed852e2009-09-05 21:47:34 +00004003 if (GetNextImageInList(image) == (Image *) NULL)
4004 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004005
cristy3ed852e2009-09-05 21:47:34 +00004006 image=SyncNextImageInList(image);
4007 }
4008 mng_info->image=image;
4009
4010 /*
4011 Signature bytes have already been read.
4012 */
4013
4014 read_JSEP=MagickFalse;
4015 reading_idat=MagickFalse;
4016 skip_to_iend=MagickFalse;
4017 for (;;)
4018 {
4019 char
4020 type[MaxTextExtent];
4021
4022 unsigned char
4023 *chunk;
4024
4025 unsigned int
4026 count;
4027
4028 /*
4029 Read a new JNG chunk.
4030 */
4031 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4032 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004033
cristy3ed852e2009-09-05 21:47:34 +00004034 if (status == MagickFalse)
4035 break;
glennrp0fe50b42010-11-16 03:52:51 +00004036
cristy3ed852e2009-09-05 21:47:34 +00004037 type[0]='\0';
4038 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4039 length=ReadBlobMSBLong(image);
4040 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4041
4042 if (logging != MagickFalse)
4043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004044 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4045 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004046
4047 if (length > PNG_UINT_31_MAX || count == 0)
4048 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004049
cristy3ed852e2009-09-05 21:47:34 +00004050 p=NULL;
4051 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00004052
cristy3ed852e2009-09-05 21:47:34 +00004053 if (length)
4054 {
4055 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00004056
cristy3ed852e2009-09-05 21:47:34 +00004057 if (chunk == (unsigned char *) NULL)
4058 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004059
cristybb503372010-05-27 20:51:26 +00004060 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004061 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00004062
cristy3ed852e2009-09-05 21:47:34 +00004063 p=chunk;
4064 }
glennrp47b9dd52010-11-24 18:12:06 +00004065
cristy3ed852e2009-09-05 21:47:34 +00004066 (void) ReadBlobMSBLong(image); /* read crc word */
4067
4068 if (skip_to_iend)
4069 {
4070 if (length)
4071 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004072
cristy3ed852e2009-09-05 21:47:34 +00004073 continue;
4074 }
4075
4076 if (memcmp(type,mng_JHDR,4) == 0)
4077 {
4078 if (length == 16)
4079 {
cristybb503372010-05-27 20:51:26 +00004080 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004081 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00004082 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004083 (p[6] << 8) | p[7]);
4084 jng_color_type=p[8];
4085 jng_image_sample_depth=p[9];
4086 jng_image_compression_method=p[10];
4087 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00004088
cristy3ed852e2009-09-05 21:47:34 +00004089 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4090 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00004091
cristy3ed852e2009-09-05 21:47:34 +00004092 jng_alpha_sample_depth=p[12];
4093 jng_alpha_compression_method=p[13];
4094 jng_alpha_filter_method=p[14];
4095 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00004096
cristy3ed852e2009-09-05 21:47:34 +00004097 if (logging != MagickFalse)
4098 {
4099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004100 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00004101
cristy3ed852e2009-09-05 21:47:34 +00004102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004103 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00004104
cristy3ed852e2009-09-05 21:47:34 +00004105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4106 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00004107
cristy3ed852e2009-09-05 21:47:34 +00004108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4109 " jng_image_sample_depth: %3d",
4110 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004111
cristy3ed852e2009-09-05 21:47:34 +00004112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4113 " jng_image_compression_method:%3d",
4114 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004115
cristy3ed852e2009-09-05 21:47:34 +00004116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4117 " jng_image_interlace_method: %3d",
4118 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00004119
cristy3ed852e2009-09-05 21:47:34 +00004120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4121 " jng_alpha_sample_depth: %3d",
4122 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004123
cristy3ed852e2009-09-05 21:47:34 +00004124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4125 " jng_alpha_compression_method:%3d",
4126 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004127
cristy3ed852e2009-09-05 21:47:34 +00004128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4129 " jng_alpha_filter_method: %3d",
4130 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004131
cristy3ed852e2009-09-05 21:47:34 +00004132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4133 " jng_alpha_interlace_method: %3d",
4134 jng_alpha_interlace_method);
4135 }
4136 }
glennrp47b9dd52010-11-24 18:12:06 +00004137
cristy3ed852e2009-09-05 21:47:34 +00004138 if (length)
4139 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004140
cristy3ed852e2009-09-05 21:47:34 +00004141 continue;
4142 }
4143
4144
4145 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4146 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4147 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4148 {
4149 /*
4150 o create color_image
4151 o open color_blob, attached to color_image
4152 o if (color type has alpha)
4153 open alpha_blob, attached to alpha_image
4154 */
4155
cristy73bd4a52010-10-05 11:24:23 +00004156 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004157
cristy3ed852e2009-09-05 21:47:34 +00004158 if (color_image_info == (ImageInfo *) NULL)
4159 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004160
cristy3ed852e2009-09-05 21:47:34 +00004161 GetImageInfo(color_image_info);
cristy16ea1392012-03-21 20:38:41 +00004162 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004163
cristy3ed852e2009-09-05 21:47:34 +00004164 if (color_image == (Image *) NULL)
4165 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4166
4167 if (logging != MagickFalse)
4168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4169 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004170
cristy3ed852e2009-09-05 21:47:34 +00004171 (void) AcquireUniqueFilename(color_image->filename);
4172 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4173 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004174
cristy3ed852e2009-09-05 21:47:34 +00004175 if (status == MagickFalse)
4176 return((Image *) NULL);
4177
4178 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4179 {
4180 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004181 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004182
cristy3ed852e2009-09-05 21:47:34 +00004183 if (alpha_image_info == (ImageInfo *) NULL)
4184 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004185
cristy3ed852e2009-09-05 21:47:34 +00004186 GetImageInfo(alpha_image_info);
cristy16ea1392012-03-21 20:38:41 +00004187 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004188
cristy3ed852e2009-09-05 21:47:34 +00004189 if (alpha_image == (Image *) NULL)
4190 {
4191 alpha_image=DestroyImage(alpha_image);
4192 ThrowReaderException(ResourceLimitError,
4193 "MemoryAllocationFailed");
4194 }
glennrp0fe50b42010-11-16 03:52:51 +00004195
cristy3ed852e2009-09-05 21:47:34 +00004196 if (logging != MagickFalse)
4197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4198 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004199
cristy3ed852e2009-09-05 21:47:34 +00004200 (void) AcquireUniqueFilename(alpha_image->filename);
4201 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4202 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004203
cristy3ed852e2009-09-05 21:47:34 +00004204 if (status == MagickFalse)
4205 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004206
cristy3ed852e2009-09-05 21:47:34 +00004207 if (jng_alpha_compression_method == 0)
4208 {
4209 unsigned char
4210 data[18];
4211
4212 if (logging != MagickFalse)
4213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4214 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004215
cristy3ed852e2009-09-05 21:47:34 +00004216 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4217 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004218
cristy3ed852e2009-09-05 21:47:34 +00004219 (void) WriteBlobMSBULong(alpha_image,13L);
4220 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004221 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004222 PNGLong(data+4,jng_width);
4223 PNGLong(data+8,jng_height);
4224 data[12]=jng_alpha_sample_depth;
4225 data[13]=0; /* color_type gray */
4226 data[14]=0; /* compression method 0 */
4227 data[15]=0; /* filter_method 0 */
4228 data[16]=0; /* interlace_method 0 */
4229 (void) WriteBlob(alpha_image,17,data);
4230 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4231 }
4232 }
4233 reading_idat=MagickTrue;
4234 }
4235
4236 if (memcmp(type,mng_JDAT,4) == 0)
4237 {
glennrp47b9dd52010-11-24 18:12:06 +00004238 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004239
4240 if (logging != MagickFalse)
4241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4242 " Copying JDAT chunk data to color_blob.");
4243
4244 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004245
cristy3ed852e2009-09-05 21:47:34 +00004246 if (length)
4247 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004248
cristy3ed852e2009-09-05 21:47:34 +00004249 continue;
4250 }
4251
4252 if (memcmp(type,mng_IDAT,4) == 0)
4253 {
4254 png_byte
4255 data[5];
4256
glennrp47b9dd52010-11-24 18:12:06 +00004257 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004258
4259 if (image_info->ping == MagickFalse)
4260 {
4261 if (logging != MagickFalse)
4262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4263 " Copying IDAT chunk data to alpha_blob.");
4264
cristybb503372010-05-27 20:51:26 +00004265 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004266 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004267 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004268 (void) WriteBlob(alpha_image,4,data);
4269 (void) WriteBlob(alpha_image,length,chunk);
4270 (void) WriteBlobMSBULong(alpha_image,
4271 crc32(crc32(0,data,4),chunk,(uInt) length));
4272 }
glennrp0fe50b42010-11-16 03:52:51 +00004273
cristy3ed852e2009-09-05 21:47:34 +00004274 if (length)
4275 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004276
cristy3ed852e2009-09-05 21:47:34 +00004277 continue;
4278 }
4279
4280 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4281 {
glennrp47b9dd52010-11-24 18:12:06 +00004282 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004283
4284 if (image_info->ping == MagickFalse)
4285 {
4286 if (logging != MagickFalse)
4287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4288 " Copying JDAA chunk data to alpha_blob.");
4289
4290 (void) WriteBlob(alpha_image,length,chunk);
4291 }
glennrp0fe50b42010-11-16 03:52:51 +00004292
cristy3ed852e2009-09-05 21:47:34 +00004293 if (length)
4294 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004295
cristy3ed852e2009-09-05 21:47:34 +00004296 continue;
4297 }
4298
4299 if (memcmp(type,mng_JSEP,4) == 0)
4300 {
4301 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004302
cristy3ed852e2009-09-05 21:47:34 +00004303 if (length)
4304 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004305
cristy3ed852e2009-09-05 21:47:34 +00004306 continue;
4307 }
4308
4309 if (memcmp(type,mng_bKGD,4) == 0)
4310 {
4311 if (length == 2)
4312 {
4313 image->background_color.red=ScaleCharToQuantum(p[1]);
4314 image->background_color.green=image->background_color.red;
4315 image->background_color.blue=image->background_color.red;
4316 }
glennrp0fe50b42010-11-16 03:52:51 +00004317
cristy3ed852e2009-09-05 21:47:34 +00004318 if (length == 6)
4319 {
4320 image->background_color.red=ScaleCharToQuantum(p[1]);
4321 image->background_color.green=ScaleCharToQuantum(p[3]);
4322 image->background_color.blue=ScaleCharToQuantum(p[5]);
4323 }
glennrp0fe50b42010-11-16 03:52:51 +00004324
cristy3ed852e2009-09-05 21:47:34 +00004325 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4326 continue;
4327 }
4328
4329 if (memcmp(type,mng_gAMA,4) == 0)
4330 {
4331 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004332 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004333
cristy3ed852e2009-09-05 21:47:34 +00004334 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4335 continue;
4336 }
4337
4338 if (memcmp(type,mng_cHRM,4) == 0)
4339 {
4340 if (length == 32)
4341 {
cristy8182b072010-05-30 20:10:53 +00004342 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4343 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4344 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4345 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4346 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4347 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4348 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4349 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004350 }
glennrp47b9dd52010-11-24 18:12:06 +00004351
cristy3ed852e2009-09-05 21:47:34 +00004352 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4353 continue;
4354 }
4355
4356 if (memcmp(type,mng_sRGB,4) == 0)
4357 {
4358 if (length == 1)
4359 {
glennrpe610a072010-08-05 17:08:46 +00004360 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004361 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristyda7803d2012-05-03 01:22:45 +00004362 image->gamma=1.000f/2.200f;
cristy3ed852e2009-09-05 21:47:34 +00004363 image->chromaticity.red_primary.x=0.6400f;
4364 image->chromaticity.red_primary.y=0.3300f;
4365 image->chromaticity.green_primary.x=0.3000f;
4366 image->chromaticity.green_primary.y=0.6000f;
4367 image->chromaticity.blue_primary.x=0.1500f;
4368 image->chromaticity.blue_primary.y=0.0600f;
4369 image->chromaticity.white_point.x=0.3127f;
4370 image->chromaticity.white_point.y=0.3290f;
4371 }
glennrp47b9dd52010-11-24 18:12:06 +00004372
cristy3ed852e2009-09-05 21:47:34 +00004373 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4374 continue;
4375 }
4376
4377 if (memcmp(type,mng_oFFs,4) == 0)
4378 {
4379 if (length > 8)
4380 {
glennrp5eae7602011-02-22 15:21:32 +00004381 image->page.x=(ssize_t) mng_get_long(p);
4382 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004383
cristy3ed852e2009-09-05 21:47:34 +00004384 if ((int) p[8] != 0)
4385 {
4386 image->page.x/=10000;
4387 image->page.y/=10000;
4388 }
4389 }
glennrp47b9dd52010-11-24 18:12:06 +00004390
cristy3ed852e2009-09-05 21:47:34 +00004391 if (length)
4392 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004393
cristy3ed852e2009-09-05 21:47:34 +00004394 continue;
4395 }
4396
4397 if (memcmp(type,mng_pHYs,4) == 0)
4398 {
4399 if (length > 8)
4400 {
cristy16ea1392012-03-21 20:38:41 +00004401 image->resolution.x=(double) mng_get_long(p);
4402 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004403 if ((int) p[8] == PNG_RESOLUTION_METER)
4404 {
4405 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00004406 image->resolution.x=image->resolution.x/100.0f;
4407 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004408 }
4409 }
glennrp0fe50b42010-11-16 03:52:51 +00004410
cristy3ed852e2009-09-05 21:47:34 +00004411 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4412 continue;
4413 }
4414
4415#if 0
4416 if (memcmp(type,mng_iCCP,4) == 0)
4417 {
glennrpfd05d622011-02-25 04:10:33 +00004418 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004419 if (length)
4420 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004421
cristy3ed852e2009-09-05 21:47:34 +00004422 continue;
4423 }
4424#endif
4425
4426 if (length)
4427 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4428
4429 if (memcmp(type,mng_IEND,4))
4430 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004431
cristy3ed852e2009-09-05 21:47:34 +00004432 break;
4433 }
4434
4435
4436 /* IEND found */
4437
4438 /*
4439 Finish up reading image data:
4440
4441 o read main image from color_blob.
4442
4443 o close color_blob.
4444
4445 o if (color_type has alpha)
4446 if alpha_encoding is PNG
4447 read secondary image from alpha_blob via ReadPNG
4448 if alpha_encoding is JPEG
4449 read secondary image from alpha_blob via ReadJPEG
4450
4451 o close alpha_blob.
4452
4453 o copy intensity of secondary image into
cristy16ea1392012-03-21 20:38:41 +00004454 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004455
4456 o destroy the secondary image.
4457 */
4458
4459 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004460
cristy3ed852e2009-09-05 21:47:34 +00004461 if (logging != MagickFalse)
4462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4463 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004464
cristy3b6fd2e2011-05-20 12:53:50 +00004465 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004466 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004467
cristy3ed852e2009-09-05 21:47:34 +00004468 color_image_info->ping=MagickFalse; /* To do: avoid this */
4469 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004470
cristy3ed852e2009-09-05 21:47:34 +00004471 if (jng_image == (Image *) NULL)
4472 return((Image *) NULL);
4473
4474 (void) RelinquishUniqueFileResource(color_image->filename);
4475 color_image=DestroyImage(color_image);
4476 color_image_info=DestroyImageInfo(color_image_info);
4477
4478 if (jng_image == (Image *) NULL)
4479 return((Image *) NULL);
4480
4481 if (logging != MagickFalse)
4482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4483 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004484
cristy3ed852e2009-09-05 21:47:34 +00004485 image->rows=jng_height;
4486 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004487
cristybb503372010-05-27 20:51:26 +00004488 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004489 {
cristy16ea1392012-03-21 20:38:41 +00004490 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004491 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00004492 for (x=(ssize_t) image->columns; x != 0; x--)
4493 {
4494 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4495 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4496 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4497 q+=GetPixelChannels(image);
4498 s+=GetPixelChannels(jng_image);
4499 }
glennrp47b9dd52010-11-24 18:12:06 +00004500
cristy3ed852e2009-09-05 21:47:34 +00004501 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4502 break;
4503 }
glennrp0fe50b42010-11-16 03:52:51 +00004504
cristy3ed852e2009-09-05 21:47:34 +00004505 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004506
cristy3ed852e2009-09-05 21:47:34 +00004507 if (image_info->ping == MagickFalse)
4508 {
4509 if (jng_color_type >= 12)
4510 {
4511 if (jng_alpha_compression_method == 0)
4512 {
4513 png_byte
4514 data[5];
4515 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4516 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004517 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004518 (void) WriteBlob(alpha_image,4,data);
4519 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4520 }
glennrp0fe50b42010-11-16 03:52:51 +00004521
cristy3ed852e2009-09-05 21:47:34 +00004522 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004523
cristy3ed852e2009-09-05 21:47:34 +00004524 if (logging != MagickFalse)
4525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00004526 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004527
cristy3b6fd2e2011-05-20 12:53:50 +00004528 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004529 "%s",alpha_image->filename);
4530
4531 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004532
cristy3ed852e2009-09-05 21:47:34 +00004533 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004534 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004535 {
4536 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy16ea1392012-03-21 20:38:41 +00004537 exception);
cristy3ed852e2009-09-05 21:47:34 +00004538 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004539
cristy8a46d822012-08-28 23:32:39 +00004540 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00004541 for (x=(ssize_t) image->columns; x != 0; x--)
4542 {
4543 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4544 q+=GetPixelChannels(image);
4545 s+=GetPixelChannels(jng_image);
4546 }
glennrp0fe50b42010-11-16 03:52:51 +00004547
cristy3ed852e2009-09-05 21:47:34 +00004548 else
cristy16ea1392012-03-21 20:38:41 +00004549 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004550 {
cristy16ea1392012-03-21 20:38:41 +00004551 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4552 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy8a46d822012-08-28 23:32:39 +00004553 image->alpha_trait=BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00004554 q+=GetPixelChannels(image);
4555 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004556 }
glennrp0fe50b42010-11-16 03:52:51 +00004557
cristy3ed852e2009-09-05 21:47:34 +00004558 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4559 break;
4560 }
4561 (void) RelinquishUniqueFileResource(alpha_image->filename);
4562 alpha_image=DestroyImage(alpha_image);
4563 alpha_image_info=DestroyImageInfo(alpha_image_info);
4564 if (jng_image != (Image *) NULL)
4565 jng_image=DestroyImage(jng_image);
4566 }
4567 }
4568
glennrp47b9dd52010-11-24 18:12:06 +00004569 /* Read the JNG image. */
4570
cristy3ed852e2009-09-05 21:47:34 +00004571 if (mng_info->mng_type == 0)
4572 {
4573 mng_info->mng_width=jng_width;
4574 mng_info->mng_height=jng_height;
4575 }
glennrp0fe50b42010-11-16 03:52:51 +00004576
cristy3ed852e2009-09-05 21:47:34 +00004577 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004578 {
4579 image->page.width=jng_width;
4580 image->page.height=jng_height;
4581 }
4582
cristy3ed852e2009-09-05 21:47:34 +00004583 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004584 {
4585 image->page.x=mng_info->x_off[mng_info->object_id];
4586 image->page.y=mng_info->y_off[mng_info->object_id];
4587 }
4588
cristy3ed852e2009-09-05 21:47:34 +00004589 else
glennrp0fe50b42010-11-16 03:52:51 +00004590 {
4591 image->page.y=mng_info->y_off[mng_info->object_id];
4592 }
4593
cristy3ed852e2009-09-05 21:47:34 +00004594 mng_info->image_found++;
4595 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4596 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004597
cristy3ed852e2009-09-05 21:47:34 +00004598 if (logging != MagickFalse)
4599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4600 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004601
cristy3ed852e2009-09-05 21:47:34 +00004602 return(image);
4603}
4604
4605/*
4606%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4607% %
4608% %
4609% %
4610% R e a d J N G I m a g e %
4611% %
4612% %
4613% %
4614%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4615%
4616% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4617% (including the 8-byte signature) and returns it. It allocates the memory
4618% necessary for the new Image structure and returns a pointer to the new
4619% image.
4620%
4621% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4622%
4623% The format of the ReadJNGImage method is:
4624%
4625% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4626% *exception)
4627%
4628% A description of each parameter follows:
4629%
4630% o image_info: the image info.
4631%
4632% o exception: return any errors or warnings in this structure.
4633%
4634*/
4635
4636static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4637{
4638 Image
4639 *image,
4640 *previous;
4641
4642 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004643 have_mng_structure,
4644 logging,
cristy3ed852e2009-09-05 21:47:34 +00004645 status;
4646
4647 MngInfo
4648 *mng_info;
4649
4650 char
4651 magic_number[MaxTextExtent];
4652
cristy3ed852e2009-09-05 21:47:34 +00004653 size_t
4654 count;
4655
4656 /*
4657 Open image file.
4658 */
4659 assert(image_info != (const ImageInfo *) NULL);
4660 assert(image_info->signature == MagickSignature);
4661 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4662 assert(exception != (ExceptionInfo *) NULL);
4663 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004664 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004665 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004666 mng_info=(MngInfo *) NULL;
4667 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004668
cristy3ed852e2009-09-05 21:47:34 +00004669 if (status == MagickFalse)
4670 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004671
cristy3ed852e2009-09-05 21:47:34 +00004672 if (LocaleCompare(image_info->magick,"JNG") != 0)
4673 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004674
glennrp47b9dd52010-11-24 18:12:06 +00004675 /* Verify JNG signature. */
4676
cristy3ed852e2009-09-05 21:47:34 +00004677 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004678
glennrp3b8763e2011-02-21 12:08:18 +00004679 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004680 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004681
glennrp47b9dd52010-11-24 18:12:06 +00004682 /* Allocate a MngInfo structure. */
4683
cristy3ed852e2009-09-05 21:47:34 +00004684 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004685 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004686
cristy3ed852e2009-09-05 21:47:34 +00004687 if (mng_info == (MngInfo *) NULL)
4688 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004689
glennrp47b9dd52010-11-24 18:12:06 +00004690 /* Initialize members of the MngInfo structure. */
4691
cristy3ed852e2009-09-05 21:47:34 +00004692 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4693 have_mng_structure=MagickTrue;
4694
4695 mng_info->image=image;
4696 previous=image;
4697 image=ReadOneJNGImage(mng_info,image_info,exception);
4698 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004699
cristy3ed852e2009-09-05 21:47:34 +00004700 if (image == (Image *) NULL)
4701 {
4702 if (IsImageObject(previous) != MagickFalse)
4703 {
4704 (void) CloseBlob(previous);
4705 (void) DestroyImageList(previous);
4706 }
glennrp0fe50b42010-11-16 03:52:51 +00004707
cristy3ed852e2009-09-05 21:47:34 +00004708 if (logging != MagickFalse)
4709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4710 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004711
cristy3ed852e2009-09-05 21:47:34 +00004712 return((Image *) NULL);
4713 }
4714 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004715
cristy3ed852e2009-09-05 21:47:34 +00004716 if (image->columns == 0 || image->rows == 0)
4717 {
4718 if (logging != MagickFalse)
4719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4720 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004721
cristy3ed852e2009-09-05 21:47:34 +00004722 ThrowReaderException(CorruptImageError,"CorruptImage");
4723 }
glennrp0fe50b42010-11-16 03:52:51 +00004724
cristy3ed852e2009-09-05 21:47:34 +00004725 if (logging != MagickFalse)
4726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004727
cristy3ed852e2009-09-05 21:47:34 +00004728 return(image);
4729}
4730#endif
4731
4732static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4733{
4734 char
4735 page_geometry[MaxTextExtent];
4736
4737 Image
4738 *image,
4739 *previous;
4740
cristy4383ec82011-01-05 15:42:32 +00004741 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004742 logging,
4743 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004744
cristy3ed852e2009-09-05 21:47:34 +00004745 volatile int
4746 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004747 object_id,
4748 term_chunk_found,
4749 skip_to_iend;
4750
cristybb503372010-05-27 20:51:26 +00004751 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004752 image_count=0;
4753
4754 MagickBooleanType
4755 status;
4756
4757 MagickOffsetType
4758 offset;
4759
4760 MngInfo
4761 *mng_info;
4762
4763 MngBox
4764 default_fb,
4765 fb,
4766 previous_fb;
4767
4768#if defined(MNG_INSERT_LAYERS)
cristy16ea1392012-03-21 20:38:41 +00004769 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004770 mng_background_color;
4771#endif
4772
4773 register unsigned char
4774 *p;
4775
cristybb503372010-05-27 20:51:26 +00004776 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004777 i;
4778
4779 size_t
4780 count;
4781
cristybb503372010-05-27 20:51:26 +00004782 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004783 loop_level;
4784
4785 volatile short
4786 skipping_loop;
4787
4788#if defined(MNG_INSERT_LAYERS)
4789 unsigned int
4790 mandatory_back=0;
4791#endif
4792
4793 volatile unsigned int
4794#ifdef MNG_OBJECT_BUFFERS
4795 mng_background_object=0,
4796#endif
4797 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4798
cristybb503372010-05-27 20:51:26 +00004799 size_t
cristy3ed852e2009-09-05 21:47:34 +00004800 default_frame_timeout,
4801 frame_timeout,
4802#if defined(MNG_INSERT_LAYERS)
4803 image_height,
4804 image_width,
4805#endif
4806 length;
4807
glennrp38ea0832010-06-02 18:50:28 +00004808 /* These delays are all measured in image ticks_per_second,
4809 * not in MNG ticks_per_second
4810 */
cristybb503372010-05-27 20:51:26 +00004811 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004812 default_frame_delay,
4813 final_delay,
4814 final_image_delay,
4815 frame_delay,
4816#if defined(MNG_INSERT_LAYERS)
4817 insert_layers,
4818#endif
4819 mng_iterations=1,
4820 simplicity=0,
4821 subframe_height=0,
4822 subframe_width=0;
4823
4824 previous_fb.top=0;
4825 previous_fb.bottom=0;
4826 previous_fb.left=0;
4827 previous_fb.right=0;
4828 default_fb.top=0;
4829 default_fb.bottom=0;
4830 default_fb.left=0;
4831 default_fb.right=0;
4832
glennrp47b9dd52010-11-24 18:12:06 +00004833 /* Open image file. */
4834
cristy3ed852e2009-09-05 21:47:34 +00004835 assert(image_info != (const ImageInfo *) NULL);
4836 assert(image_info->signature == MagickSignature);
4837 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4838 assert(exception != (ExceptionInfo *) NULL);
4839 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004840 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004841 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004842 mng_info=(MngInfo *) NULL;
4843 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004844
cristy3ed852e2009-09-05 21:47:34 +00004845 if (status == MagickFalse)
4846 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy3ed852e2009-09-05 21:47:34 +00004848 first_mng_object=MagickFalse;
4849 skipping_loop=(-1);
4850 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004851
4852 /* Allocate a MngInfo structure. */
4853
cristy73bd4a52010-10-05 11:24:23 +00004854 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004855
cristy3ed852e2009-09-05 21:47:34 +00004856 if (mng_info == (MngInfo *) NULL)
4857 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004858
glennrp47b9dd52010-11-24 18:12:06 +00004859 /* Initialize members of the MngInfo structure. */
4860
cristy3ed852e2009-09-05 21:47:34 +00004861 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4862 mng_info->image=image;
4863 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004864
4865 if (LocaleCompare(image_info->magick,"MNG") == 0)
4866 {
4867 char
4868 magic_number[MaxTextExtent];
4869
glennrp47b9dd52010-11-24 18:12:06 +00004870 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004871 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4872 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4873 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004874
4875 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004876 for (i=0; i < MNG_MAX_OBJECTS; i++)
4877 {
cristybb503372010-05-27 20:51:26 +00004878 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4879 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004880 }
4881 mng_info->exists[0]=MagickTrue;
4882 }
glennrp47b9dd52010-11-24 18:12:06 +00004883
cristy3ed852e2009-09-05 21:47:34 +00004884 first_mng_object=MagickTrue;
4885 mng_type=0;
4886#if defined(MNG_INSERT_LAYERS)
4887 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4888#endif
4889 default_frame_delay=0;
4890 default_frame_timeout=0;
4891 frame_delay=0;
4892 final_delay=1;
4893 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4894 object_id=0;
4895 skip_to_iend=MagickFalse;
4896 term_chunk_found=MagickFalse;
4897 mng_info->framing_mode=1;
4898#if defined(MNG_INSERT_LAYERS)
4899 mandatory_back=MagickFalse;
4900#endif
4901#if defined(MNG_INSERT_LAYERS)
4902 mng_background_color=image->background_color;
4903#endif
4904 default_fb=mng_info->frame;
4905 previous_fb=mng_info->frame;
4906 do
4907 {
4908 char
4909 type[MaxTextExtent];
4910
4911 if (LocaleCompare(image_info->magick,"MNG") == 0)
4912 {
4913 unsigned char
4914 *chunk;
4915
4916 /*
4917 Read a new chunk.
4918 */
4919 type[0]='\0';
4920 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4921 length=ReadBlobMSBLong(image);
4922 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4923
4924 if (logging != MagickFalse)
4925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004926 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4927 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004928
4929 if (length > PNG_UINT_31_MAX)
4930 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004931
cristy3ed852e2009-09-05 21:47:34 +00004932 if (count == 0)
4933 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004934
cristy3ed852e2009-09-05 21:47:34 +00004935 p=NULL;
4936 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004937
cristy3ed852e2009-09-05 21:47:34 +00004938 if (length)
4939 {
4940 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004941
cristy3ed852e2009-09-05 21:47:34 +00004942 if (chunk == (unsigned char *) NULL)
4943 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004944
cristybb503372010-05-27 20:51:26 +00004945 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004946 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004947
cristy3ed852e2009-09-05 21:47:34 +00004948 p=chunk;
4949 }
glennrp0fe50b42010-11-16 03:52:51 +00004950
cristy3ed852e2009-09-05 21:47:34 +00004951 (void) ReadBlobMSBLong(image); /* read crc word */
4952
4953#if !defined(JNG_SUPPORTED)
4954 if (memcmp(type,mng_JHDR,4) == 0)
4955 {
4956 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004957
cristy3ed852e2009-09-05 21:47:34 +00004958 if (mng_info->jhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00004959 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004960 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004961
cristy3ed852e2009-09-05 21:47:34 +00004962 mng_info->jhdr_warning++;
4963 }
4964#endif
4965 if (memcmp(type,mng_DHDR,4) == 0)
4966 {
4967 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004968
cristy3ed852e2009-09-05 21:47:34 +00004969 if (mng_info->dhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00004970 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004971 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004972
cristy3ed852e2009-09-05 21:47:34 +00004973 mng_info->dhdr_warning++;
4974 }
4975 if (memcmp(type,mng_MEND,4) == 0)
4976 break;
glennrp47b9dd52010-11-24 18:12:06 +00004977
cristy3ed852e2009-09-05 21:47:34 +00004978 if (skip_to_iend)
4979 {
4980 if (memcmp(type,mng_IEND,4) == 0)
4981 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004982
cristy3ed852e2009-09-05 21:47:34 +00004983 if (length)
4984 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004985
cristy3ed852e2009-09-05 21:47:34 +00004986 if (logging != MagickFalse)
4987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4988 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004989
cristy3ed852e2009-09-05 21:47:34 +00004990 continue;
4991 }
glennrp0fe50b42010-11-16 03:52:51 +00004992
cristy3ed852e2009-09-05 21:47:34 +00004993 if (memcmp(type,mng_MHDR,4) == 0)
4994 {
cristybb503372010-05-27 20:51:26 +00004995 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004996 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004997
cristybb503372010-05-27 20:51:26 +00004998 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004999 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00005000
cristy3ed852e2009-09-05 21:47:34 +00005001 if (logging != MagickFalse)
5002 {
5003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005004 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00005005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005006 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005007 }
glennrp0fe50b42010-11-16 03:52:51 +00005008
cristy3ed852e2009-09-05 21:47:34 +00005009 p+=8;
cristy8182b072010-05-30 20:10:53 +00005010 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005011
cristy3ed852e2009-09-05 21:47:34 +00005012 if (mng_info->ticks_per_second == 0)
5013 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00005014
cristy3ed852e2009-09-05 21:47:34 +00005015 else
5016 default_frame_delay=1UL*image->ticks_per_second/
5017 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005018
cristy3ed852e2009-09-05 21:47:34 +00005019 frame_delay=default_frame_delay;
5020 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00005021
cristy3ed852e2009-09-05 21:47:34 +00005022 if (length > 16)
5023 {
5024 p+=16;
cristy8182b072010-05-30 20:10:53 +00005025 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005026 }
glennrp0fe50b42010-11-16 03:52:51 +00005027
cristy3ed852e2009-09-05 21:47:34 +00005028 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00005029
cristy3ed852e2009-09-05 21:47:34 +00005030 if ((simplicity != 0) && ((simplicity | 11) == 11))
5031 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00005032
cristy3ed852e2009-09-05 21:47:34 +00005033 if ((simplicity != 0) && ((simplicity | 9) == 9))
5034 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00005035
cristy3ed852e2009-09-05 21:47:34 +00005036#if defined(MNG_INSERT_LAYERS)
5037 if (mng_type != 3)
5038 insert_layers=MagickTrue;
5039#endif
cristy16ea1392012-03-21 20:38:41 +00005040 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005041 {
glennrp47b9dd52010-11-24 18:12:06 +00005042 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005043 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005044
cristy3ed852e2009-09-05 21:47:34 +00005045 if (GetNextImageInList(image) == (Image *) NULL)
5046 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00005047
cristy3ed852e2009-09-05 21:47:34 +00005048 image=SyncNextImageInList(image);
5049 mng_info->image=image;
5050 }
5051
5052 if ((mng_info->mng_width > 65535L) ||
5053 (mng_info->mng_height > 65535L))
5054 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00005055
cristy3b6fd2e2011-05-20 12:53:50 +00005056 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00005057 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00005058 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00005059
cristy3ed852e2009-09-05 21:47:34 +00005060 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00005061 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00005062 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00005063 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00005064 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005065
cristy3ed852e2009-09-05 21:47:34 +00005066 for (i=0; i < MNG_MAX_OBJECTS; i++)
5067 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005068
cristy3ed852e2009-09-05 21:47:34 +00005069 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5070 continue;
5071 }
5072
5073 if (memcmp(type,mng_TERM,4) == 0)
5074 {
5075 int
5076 repeat=0;
5077
5078
5079 if (length)
5080 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005081
cristy3ed852e2009-09-05 21:47:34 +00005082 if (repeat == 3)
5083 {
cristy8182b072010-05-30 20:10:53 +00005084 final_delay=(png_uint_32) mng_get_long(&p[2]);
5085 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00005086
cristy3ed852e2009-09-05 21:47:34 +00005087 if (mng_iterations == PNG_UINT_31_MAX)
5088 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00005089
cristy3ed852e2009-09-05 21:47:34 +00005090 image->iterations=mng_iterations;
5091 term_chunk_found=MagickTrue;
5092 }
glennrp0fe50b42010-11-16 03:52:51 +00005093
cristy3ed852e2009-09-05 21:47:34 +00005094 if (logging != MagickFalse)
5095 {
5096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5097 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00005098
cristy3ed852e2009-09-05 21:47:34 +00005099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005100 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00005101
cristy3ed852e2009-09-05 21:47:34 +00005102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005103 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00005104 }
glennrp0fe50b42010-11-16 03:52:51 +00005105
cristy3ed852e2009-09-05 21:47:34 +00005106 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5107 continue;
5108 }
5109 if (memcmp(type,mng_DEFI,4) == 0)
5110 {
5111 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005112 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005113 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5114 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005115
cristy3ed852e2009-09-05 21:47:34 +00005116 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005117
cristy3ed852e2009-09-05 21:47:34 +00005118 if (mng_type == 2 && object_id != 0)
cristy16ea1392012-03-21 20:38:41 +00005119 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005120 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5121 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005122
cristy3ed852e2009-09-05 21:47:34 +00005123 if (object_id > MNG_MAX_OBJECTS)
5124 {
5125 /*
glennrpedaa0382012-04-12 14:16:21 +00005126 Instead of using a warning we should allocate a larger
cristy3ed852e2009-09-05 21:47:34 +00005127 MngInfo structure and continue.
5128 */
cristy16ea1392012-03-21 20:38:41 +00005129 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005130 CoderError,"object id too large","`%s'",image->filename);
5131 object_id=MNG_MAX_OBJECTS;
5132 }
glennrp0fe50b42010-11-16 03:52:51 +00005133
cristy3ed852e2009-09-05 21:47:34 +00005134 if (mng_info->exists[object_id])
5135 if (mng_info->frozen[object_id])
5136 {
5137 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristy16ea1392012-03-21 20:38:41 +00005138 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005139 GetMagickModule(),CoderError,
5140 "DEFI cannot redefine a frozen MNG object","`%s'",
5141 image->filename);
5142 continue;
5143 }
glennrp0fe50b42010-11-16 03:52:51 +00005144
cristy3ed852e2009-09-05 21:47:34 +00005145 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005146
cristy3ed852e2009-09-05 21:47:34 +00005147 if (length > 2)
5148 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005149
cristy3ed852e2009-09-05 21:47:34 +00005150 /*
5151 Extract object offset info.
5152 */
5153 if (length > 11)
5154 {
glennrp0fe50b42010-11-16 03:52:51 +00005155 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5156 (p[5] << 16) | (p[6] << 8) | p[7]);
5157
5158 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5159 (p[9] << 16) | (p[10] << 8) | p[11]);
5160
cristy3ed852e2009-09-05 21:47:34 +00005161 if (logging != MagickFalse)
5162 {
5163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005164 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005165 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005166
cristy3ed852e2009-09-05 21:47:34 +00005167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005168 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005169 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005170 }
5171 }
glennrp0fe50b42010-11-16 03:52:51 +00005172
cristy3ed852e2009-09-05 21:47:34 +00005173 /*
5174 Extract object clipping info.
5175 */
5176 if (length > 27)
5177 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5178 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005179
cristy3ed852e2009-09-05 21:47:34 +00005180 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5181 continue;
5182 }
5183 if (memcmp(type,mng_bKGD,4) == 0)
5184 {
5185 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005186
cristy3ed852e2009-09-05 21:47:34 +00005187 if (length > 5)
5188 {
5189 mng_info->mng_global_bkgd.red=
5190 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005191
cristy3ed852e2009-09-05 21:47:34 +00005192 mng_info->mng_global_bkgd.green=
5193 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005194
cristy3ed852e2009-09-05 21:47:34 +00005195 mng_info->mng_global_bkgd.blue=
5196 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005197
cristy3ed852e2009-09-05 21:47:34 +00005198 mng_info->have_global_bkgd=MagickTrue;
5199 }
glennrp0fe50b42010-11-16 03:52:51 +00005200
cristy3ed852e2009-09-05 21:47:34 +00005201 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5202 continue;
5203 }
5204 if (memcmp(type,mng_BACK,4) == 0)
5205 {
5206#if defined(MNG_INSERT_LAYERS)
5207 if (length > 6)
5208 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005209
cristy3ed852e2009-09-05 21:47:34 +00005210 else
5211 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005212
cristy3ed852e2009-09-05 21:47:34 +00005213 if (mandatory_back && length > 5)
5214 {
5215 mng_background_color.red=
5216 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005217
cristy3ed852e2009-09-05 21:47:34 +00005218 mng_background_color.green=
5219 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005220
cristy3ed852e2009-09-05 21:47:34 +00005221 mng_background_color.blue=
5222 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005223
cristy16ea1392012-03-21 20:38:41 +00005224 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005225 }
glennrp0fe50b42010-11-16 03:52:51 +00005226
cristy3ed852e2009-09-05 21:47:34 +00005227#ifdef MNG_OBJECT_BUFFERS
5228 if (length > 8)
5229 mng_background_object=(p[7] << 8) | p[8];
5230#endif
5231#endif
5232 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5233 continue;
5234 }
glennrp47b9dd52010-11-24 18:12:06 +00005235
cristy3ed852e2009-09-05 21:47:34 +00005236 if (memcmp(type,mng_PLTE,4) == 0)
5237 {
glennrp47b9dd52010-11-24 18:12:06 +00005238 /* Read global PLTE. */
5239
cristy3ed852e2009-09-05 21:47:34 +00005240 if (length && (length < 769))
5241 {
5242 if (mng_info->global_plte == (png_colorp) NULL)
5243 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5244 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005245
cristybb503372010-05-27 20:51:26 +00005246 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005247 {
5248 mng_info->global_plte[i].red=p[3*i];
5249 mng_info->global_plte[i].green=p[3*i+1];
5250 mng_info->global_plte[i].blue=p[3*i+2];
5251 }
glennrp0fe50b42010-11-16 03:52:51 +00005252
cristy35ef8242010-06-03 16:24:13 +00005253 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005254 }
5255#ifdef MNG_LOOSE
5256 for ( ; i < 256; i++)
5257 {
5258 mng_info->global_plte[i].red=i;
5259 mng_info->global_plte[i].green=i;
5260 mng_info->global_plte[i].blue=i;
5261 }
glennrp0fe50b42010-11-16 03:52:51 +00005262
cristy3ed852e2009-09-05 21:47:34 +00005263 if (length)
5264 mng_info->global_plte_length=256;
5265#endif
5266 else
5267 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005268
cristy3ed852e2009-09-05 21:47:34 +00005269 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5270 continue;
5271 }
glennrp47b9dd52010-11-24 18:12:06 +00005272
cristy3ed852e2009-09-05 21:47:34 +00005273 if (memcmp(type,mng_tRNS,4) == 0)
5274 {
5275 /* read global tRNS */
5276
5277 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005278 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005279 mng_info->global_trns[i]=p[i];
5280
5281#ifdef MNG_LOOSE
5282 for ( ; i < 256; i++)
5283 mng_info->global_trns[i]=255;
5284#endif
cristy12560f32010-06-03 16:51:08 +00005285 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005286 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5287 continue;
5288 }
5289 if (memcmp(type,mng_gAMA,4) == 0)
5290 {
5291 if (length == 4)
5292 {
cristybb503372010-05-27 20:51:26 +00005293 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005294 igamma;
5295
cristy8182b072010-05-30 20:10:53 +00005296 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005297 mng_info->global_gamma=((float) igamma)*0.00001;
5298 mng_info->have_global_gama=MagickTrue;
5299 }
glennrp0fe50b42010-11-16 03:52:51 +00005300
cristy3ed852e2009-09-05 21:47:34 +00005301 else
5302 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005303
cristy3ed852e2009-09-05 21:47:34 +00005304 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5305 continue;
5306 }
5307
5308 if (memcmp(type,mng_cHRM,4) == 0)
5309 {
glennrp47b9dd52010-11-24 18:12:06 +00005310 /* Read global cHRM */
5311
cristy3ed852e2009-09-05 21:47:34 +00005312 if (length == 32)
5313 {
cristy8182b072010-05-30 20:10:53 +00005314 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5315 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5316 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005317 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005318 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005319 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005320 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005321 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005322 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005323 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005324 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005325 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005326 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005327 mng_info->have_global_chrm=MagickTrue;
5328 }
5329 else
5330 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005331
cristy3ed852e2009-09-05 21:47:34 +00005332 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5333 continue;
5334 }
glennrp47b9dd52010-11-24 18:12:06 +00005335
cristy3ed852e2009-09-05 21:47:34 +00005336 if (memcmp(type,mng_sRGB,4) == 0)
5337 {
5338 /*
5339 Read global sRGB.
5340 */
5341 if (length)
5342 {
glennrpe610a072010-08-05 17:08:46 +00005343 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005344 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005345 mng_info->have_global_srgb=MagickTrue;
5346 }
5347 else
5348 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005349
cristy3ed852e2009-09-05 21:47:34 +00005350 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5351 continue;
5352 }
glennrp47b9dd52010-11-24 18:12:06 +00005353
cristy3ed852e2009-09-05 21:47:34 +00005354 if (memcmp(type,mng_iCCP,4) == 0)
5355 {
glennrpfd05d622011-02-25 04:10:33 +00005356 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005357
5358 /*
5359 Read global iCCP.
5360 */
5361 if (length)
5362 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005363
cristy3ed852e2009-09-05 21:47:34 +00005364 continue;
5365 }
glennrp47b9dd52010-11-24 18:12:06 +00005366
cristy3ed852e2009-09-05 21:47:34 +00005367 if (memcmp(type,mng_FRAM,4) == 0)
5368 {
5369 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005370 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005371 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5372 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005373
cristy3ed852e2009-09-05 21:47:34 +00005374 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5375 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005376
cristy3ed852e2009-09-05 21:47:34 +00005377 frame_delay=default_frame_delay;
5378 frame_timeout=default_frame_timeout;
5379 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005380
cristy3ed852e2009-09-05 21:47:34 +00005381 if (length)
5382 if (p[0])
5383 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005384
cristy3ed852e2009-09-05 21:47:34 +00005385 if (logging != MagickFalse)
5386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5387 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005388
cristy3ed852e2009-09-05 21:47:34 +00005389 if (length > 6)
5390 {
glennrp47b9dd52010-11-24 18:12:06 +00005391 /* Note the delay and frame clipping boundaries. */
5392
cristy3ed852e2009-09-05 21:47:34 +00005393 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005394
cristybb503372010-05-27 20:51:26 +00005395 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005396 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005397
cristy3ed852e2009-09-05 21:47:34 +00005398 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005399
cristybb503372010-05-27 20:51:26 +00005400 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005401 {
5402 int
5403 change_delay,
5404 change_timeout,
5405 change_clipping;
5406
5407 change_delay=(*p++);
5408 change_timeout=(*p++);
5409 change_clipping=(*p++);
5410 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005411
cristy3ed852e2009-09-05 21:47:34 +00005412 if (change_delay)
5413 {
cristy8182b072010-05-30 20:10:53 +00005414 frame_delay=1UL*image->ticks_per_second*
5415 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005416
cristy8182b072010-05-30 20:10:53 +00005417 if (mng_info->ticks_per_second != 0)
5418 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005419
glennrpbb010dd2010-06-01 13:07:15 +00005420 else
5421 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005422
cristy3ed852e2009-09-05 21:47:34 +00005423 if (change_delay == 2)
5424 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005425
cristy3ed852e2009-09-05 21:47:34 +00005426 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005427
cristy3ed852e2009-09-05 21:47:34 +00005428 if (logging != MagickFalse)
5429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005430 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005431 }
glennrp47b9dd52010-11-24 18:12:06 +00005432
cristy3ed852e2009-09-05 21:47:34 +00005433 if (change_timeout)
5434 {
glennrpbb010dd2010-06-01 13:07:15 +00005435 frame_timeout=1UL*image->ticks_per_second*
5436 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005437
glennrpbb010dd2010-06-01 13:07:15 +00005438 if (mng_info->ticks_per_second != 0)
5439 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005440
glennrpbb010dd2010-06-01 13:07:15 +00005441 else
5442 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005443
cristy3ed852e2009-09-05 21:47:34 +00005444 if (change_delay == 2)
5445 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005446
cristy3ed852e2009-09-05 21:47:34 +00005447 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005448
cristy3ed852e2009-09-05 21:47:34 +00005449 if (logging != MagickFalse)
5450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005451 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005452 }
glennrp47b9dd52010-11-24 18:12:06 +00005453
cristy3ed852e2009-09-05 21:47:34 +00005454 if (change_clipping)
5455 {
5456 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5457 p+=17;
5458 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005459
cristy3ed852e2009-09-05 21:47:34 +00005460 if (logging != MagickFalse)
5461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005462 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005463 (double) fb.left,(double) fb.right,(double) fb.top,
5464 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005465
cristy3ed852e2009-09-05 21:47:34 +00005466 if (change_clipping == 2)
5467 default_fb=fb;
5468 }
5469 }
5470 }
5471 mng_info->clip=fb;
5472 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005473
cristybb503372010-05-27 20:51:26 +00005474 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005475 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005476
cristybb503372010-05-27 20:51:26 +00005477 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005478 -mng_info->clip.top);
5479 /*
5480 Insert a background layer behind the frame if framing_mode is 4.
5481 */
5482#if defined(MNG_INSERT_LAYERS)
5483 if (logging != MagickFalse)
5484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005485 " subframe_width=%.20g, subframe_height=%.20g",(double)
5486 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005487
cristy3ed852e2009-09-05 21:47:34 +00005488 if (insert_layers && (mng_info->framing_mode == 4) &&
5489 (subframe_width) && (subframe_height))
5490 {
glennrp47b9dd52010-11-24 18:12:06 +00005491 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005492 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005493 {
cristy16ea1392012-03-21 20:38:41 +00005494 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005495
cristy3ed852e2009-09-05 21:47:34 +00005496 if (GetNextImageInList(image) == (Image *) NULL)
5497 {
5498 image=DestroyImageList(image);
5499 MngInfoFreeStruct(mng_info,&have_mng_structure);
5500 return((Image *) NULL);
5501 }
glennrp47b9dd52010-11-24 18:12:06 +00005502
cristy3ed852e2009-09-05 21:47:34 +00005503 image=SyncNextImageInList(image);
5504 }
glennrp0fe50b42010-11-16 03:52:51 +00005505
cristy3ed852e2009-09-05 21:47:34 +00005506 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005507
cristy3ed852e2009-09-05 21:47:34 +00005508 if (term_chunk_found)
5509 {
5510 image->start_loop=MagickTrue;
5511 image->iterations=mng_iterations;
5512 term_chunk_found=MagickFalse;
5513 }
glennrp0fe50b42010-11-16 03:52:51 +00005514
cristy3ed852e2009-09-05 21:47:34 +00005515 else
5516 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005517
cristy3ed852e2009-09-05 21:47:34 +00005518 image->columns=subframe_width;
5519 image->rows=subframe_height;
5520 image->page.width=subframe_width;
5521 image->page.height=subframe_height;
5522 image->page.x=mng_info->clip.left;
5523 image->page.y=mng_info->clip.top;
5524 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00005525 image->alpha_trait=UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00005526 image->delay=0;
cristy16ea1392012-03-21 20:38:41 +00005527 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005528
cristy3ed852e2009-09-05 21:47:34 +00005529 if (logging != MagickFalse)
5530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005531 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005532 (double) mng_info->clip.left,(double) mng_info->clip.right,
5533 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005534 }
5535#endif
5536 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5537 continue;
5538 }
5539 if (memcmp(type,mng_CLIP,4) == 0)
5540 {
5541 unsigned int
5542 first_object,
5543 last_object;
5544
5545 /*
5546 Read CLIP.
5547 */
5548 first_object=(p[0] << 8) | p[1];
5549 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005550
cristy3ed852e2009-09-05 21:47:34 +00005551 for (i=(int) first_object; i <= (int) last_object; i++)
5552 {
5553 if (mng_info->exists[i] && !mng_info->frozen[i])
5554 {
5555 MngBox
5556 box;
5557
5558 box=mng_info->object_clip[i];
5559 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5560 }
5561 }
glennrp47b9dd52010-11-24 18:12:06 +00005562
cristy3ed852e2009-09-05 21:47:34 +00005563 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5564 continue;
5565 }
5566 if (memcmp(type,mng_SAVE,4) == 0)
5567 {
5568 for (i=1; i < MNG_MAX_OBJECTS; i++)
5569 if (mng_info->exists[i])
5570 {
5571 mng_info->frozen[i]=MagickTrue;
5572#ifdef MNG_OBJECT_BUFFERS
5573 if (mng_info->ob[i] != (MngBuffer *) NULL)
5574 mng_info->ob[i]->frozen=MagickTrue;
5575#endif
5576 }
glennrp0fe50b42010-11-16 03:52:51 +00005577
cristy3ed852e2009-09-05 21:47:34 +00005578 if (length)
5579 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005580
cristy3ed852e2009-09-05 21:47:34 +00005581 continue;
5582 }
5583
5584 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5585 {
glennrp47b9dd52010-11-24 18:12:06 +00005586 /* Read DISC or SEEK. */
5587
cristy3ed852e2009-09-05 21:47:34 +00005588 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5589 {
5590 for (i=1; i < MNG_MAX_OBJECTS; i++)
5591 MngInfoDiscardObject(mng_info,i);
5592 }
glennrp0fe50b42010-11-16 03:52:51 +00005593
cristy3ed852e2009-09-05 21:47:34 +00005594 else
5595 {
cristybb503372010-05-27 20:51:26 +00005596 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005597 j;
5598
cristybb503372010-05-27 20:51:26 +00005599 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005600 {
5601 i=p[j] << 8 | p[j+1];
5602 MngInfoDiscardObject(mng_info,i);
5603 }
5604 }
glennrp0fe50b42010-11-16 03:52:51 +00005605
cristy3ed852e2009-09-05 21:47:34 +00005606 if (length)
5607 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005608
cristy3ed852e2009-09-05 21:47:34 +00005609 continue;
5610 }
glennrp47b9dd52010-11-24 18:12:06 +00005611
cristy3ed852e2009-09-05 21:47:34 +00005612 if (memcmp(type,mng_MOVE,4) == 0)
5613 {
cristybb503372010-05-27 20:51:26 +00005614 size_t
cristy3ed852e2009-09-05 21:47:34 +00005615 first_object,
5616 last_object;
5617
glennrp47b9dd52010-11-24 18:12:06 +00005618 /* read MOVE */
5619
cristy3ed852e2009-09-05 21:47:34 +00005620 first_object=(p[0] << 8) | p[1];
5621 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005622 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005623 {
5624 if (mng_info->exists[i] && !mng_info->frozen[i])
5625 {
5626 MngPair
5627 new_pair;
5628
5629 MngPair
5630 old_pair;
5631
5632 old_pair.a=mng_info->x_off[i];
5633 old_pair.b=mng_info->y_off[i];
5634 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5635 mng_info->x_off[i]=new_pair.a;
5636 mng_info->y_off[i]=new_pair.b;
5637 }
5638 }
glennrp47b9dd52010-11-24 18:12:06 +00005639
cristy3ed852e2009-09-05 21:47:34 +00005640 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5641 continue;
5642 }
5643
5644 if (memcmp(type,mng_LOOP,4) == 0)
5645 {
cristybb503372010-05-27 20:51:26 +00005646 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005647 loop_level=chunk[0];
5648 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005649
5650 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005651 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005652
cristy3ed852e2009-09-05 21:47:34 +00005653 if (logging != MagickFalse)
5654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005655 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5656 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005657
cristy3ed852e2009-09-05 21:47:34 +00005658 if (loop_iters == 0)
5659 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005660
cristy3ed852e2009-09-05 21:47:34 +00005661 else
5662 {
5663 mng_info->loop_jump[loop_level]=TellBlob(image);
5664 mng_info->loop_count[loop_level]=loop_iters;
5665 }
glennrp0fe50b42010-11-16 03:52:51 +00005666
cristy3ed852e2009-09-05 21:47:34 +00005667 mng_info->loop_iteration[loop_level]=0;
5668 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5669 continue;
5670 }
glennrp47b9dd52010-11-24 18:12:06 +00005671
cristy3ed852e2009-09-05 21:47:34 +00005672 if (memcmp(type,mng_ENDL,4) == 0)
5673 {
5674 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005675
cristy3ed852e2009-09-05 21:47:34 +00005676 if (skipping_loop > 0)
5677 {
5678 if (skipping_loop == loop_level)
5679 {
5680 /*
5681 Found end of zero-iteration loop.
5682 */
5683 skipping_loop=(-1);
5684 mng_info->loop_active[loop_level]=0;
5685 }
5686 }
glennrp47b9dd52010-11-24 18:12:06 +00005687
cristy3ed852e2009-09-05 21:47:34 +00005688 else
5689 {
5690 if (mng_info->loop_active[loop_level] == 1)
5691 {
5692 mng_info->loop_count[loop_level]--;
5693 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005694
cristy3ed852e2009-09-05 21:47:34 +00005695 if (logging != MagickFalse)
5696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005697 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005698 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005699 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005700
cristy3ed852e2009-09-05 21:47:34 +00005701 if (mng_info->loop_count[loop_level] != 0)
5702 {
5703 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5704 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005705
cristy3ed852e2009-09-05 21:47:34 +00005706 if (offset < 0)
5707 ThrowReaderException(CorruptImageError,
5708 "ImproperImageHeader");
5709 }
glennrp47b9dd52010-11-24 18:12:06 +00005710
cristy3ed852e2009-09-05 21:47:34 +00005711 else
5712 {
5713 short
5714 last_level;
5715
5716 /*
5717 Finished loop.
5718 */
5719 mng_info->loop_active[loop_level]=0;
5720 last_level=(-1);
5721 for (i=0; i < loop_level; i++)
5722 if (mng_info->loop_active[i] == 1)
5723 last_level=(short) i;
5724 loop_level=last_level;
5725 }
5726 }
5727 }
glennrp47b9dd52010-11-24 18:12:06 +00005728
cristy3ed852e2009-09-05 21:47:34 +00005729 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5730 continue;
5731 }
glennrp47b9dd52010-11-24 18:12:06 +00005732
cristy3ed852e2009-09-05 21:47:34 +00005733 if (memcmp(type,mng_CLON,4) == 0)
5734 {
5735 if (mng_info->clon_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005736 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005737 CoderError,"CLON is not implemented yet","`%s'",
5738 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005739
cristy3ed852e2009-09-05 21:47:34 +00005740 mng_info->clon_warning++;
5741 }
glennrp47b9dd52010-11-24 18:12:06 +00005742
cristy3ed852e2009-09-05 21:47:34 +00005743 if (memcmp(type,mng_MAGN,4) == 0)
5744 {
5745 png_uint_16
5746 magn_first,
5747 magn_last,
5748 magn_mb,
5749 magn_ml,
5750 magn_mr,
5751 magn_mt,
5752 magn_mx,
5753 magn_my,
5754 magn_methx,
5755 magn_methy;
5756
5757 if (length > 1)
5758 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005759
cristy3ed852e2009-09-05 21:47:34 +00005760 else
5761 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005762
cristy3ed852e2009-09-05 21:47:34 +00005763 if (length > 3)
5764 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005765
cristy3ed852e2009-09-05 21:47:34 +00005766 else
5767 magn_last=magn_first;
5768#ifndef MNG_OBJECT_BUFFERS
5769 if (magn_first || magn_last)
5770 if (mng_info->magn_warning == 0)
5771 {
cristy16ea1392012-03-21 20:38:41 +00005772 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005773 GetMagickModule(),CoderError,
5774 "MAGN is not implemented yet for nonzero objects",
5775 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005776
cristy3ed852e2009-09-05 21:47:34 +00005777 mng_info->magn_warning++;
5778 }
5779#endif
5780 if (length > 4)
5781 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005782
cristy3ed852e2009-09-05 21:47:34 +00005783 else
5784 magn_methx=0;
5785
5786 if (length > 6)
5787 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005788
cristy3ed852e2009-09-05 21:47:34 +00005789 else
5790 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005791
cristy3ed852e2009-09-05 21:47:34 +00005792 if (magn_mx == 0)
5793 magn_mx=1;
5794
5795 if (length > 8)
5796 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005797
cristy3ed852e2009-09-05 21:47:34 +00005798 else
5799 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005800
cristy3ed852e2009-09-05 21:47:34 +00005801 if (magn_my == 0)
5802 magn_my=1;
5803
5804 if (length > 10)
5805 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005806
cristy3ed852e2009-09-05 21:47:34 +00005807 else
5808 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005809
cristy3ed852e2009-09-05 21:47:34 +00005810 if (magn_ml == 0)
5811 magn_ml=1;
5812
5813 if (length > 12)
5814 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005815
cristy3ed852e2009-09-05 21:47:34 +00005816 else
5817 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005818
cristy3ed852e2009-09-05 21:47:34 +00005819 if (magn_mr == 0)
5820 magn_mr=1;
5821
5822 if (length > 14)
5823 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005824
cristy3ed852e2009-09-05 21:47:34 +00005825 else
5826 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005827
cristy3ed852e2009-09-05 21:47:34 +00005828 if (magn_mt == 0)
5829 magn_mt=1;
5830
5831 if (length > 16)
5832 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005833
cristy3ed852e2009-09-05 21:47:34 +00005834 else
5835 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005836
cristy3ed852e2009-09-05 21:47:34 +00005837 if (magn_mb == 0)
5838 magn_mb=1;
5839
5840 if (length > 17)
5841 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005842
cristy3ed852e2009-09-05 21:47:34 +00005843 else
5844 magn_methy=magn_methx;
5845
glennrp47b9dd52010-11-24 18:12:06 +00005846
cristy3ed852e2009-09-05 21:47:34 +00005847 if (magn_methx > 5 || magn_methy > 5)
5848 if (mng_info->magn_warning == 0)
5849 {
cristy16ea1392012-03-21 20:38:41 +00005850 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005851 GetMagickModule(),CoderError,
5852 "Unknown MAGN method in MNG datastream","`%s'",
5853 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005854
cristy3ed852e2009-09-05 21:47:34 +00005855 mng_info->magn_warning++;
5856 }
5857#ifdef MNG_OBJECT_BUFFERS
5858 /* Magnify existing objects in the range magn_first to magn_last */
5859#endif
5860 if (magn_first == 0 || magn_last == 0)
5861 {
5862 /* Save the magnification factors for object 0 */
5863 mng_info->magn_mb=magn_mb;
5864 mng_info->magn_ml=magn_ml;
5865 mng_info->magn_mr=magn_mr;
5866 mng_info->magn_mt=magn_mt;
5867 mng_info->magn_mx=magn_mx;
5868 mng_info->magn_my=magn_my;
5869 mng_info->magn_methx=magn_methx;
5870 mng_info->magn_methy=magn_methy;
5871 }
5872 }
glennrp47b9dd52010-11-24 18:12:06 +00005873
cristy3ed852e2009-09-05 21:47:34 +00005874 if (memcmp(type,mng_PAST,4) == 0)
5875 {
5876 if (mng_info->past_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005877 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005878 CoderError,"PAST is not implemented yet","`%s'",
5879 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005880
cristy3ed852e2009-09-05 21:47:34 +00005881 mng_info->past_warning++;
5882 }
glennrp47b9dd52010-11-24 18:12:06 +00005883
cristy3ed852e2009-09-05 21:47:34 +00005884 if (memcmp(type,mng_SHOW,4) == 0)
5885 {
5886 if (mng_info->show_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005887 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005888 CoderError,"SHOW is not implemented yet","`%s'",
5889 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005890
cristy3ed852e2009-09-05 21:47:34 +00005891 mng_info->show_warning++;
5892 }
glennrp47b9dd52010-11-24 18:12:06 +00005893
cristy3ed852e2009-09-05 21:47:34 +00005894 if (memcmp(type,mng_sBIT,4) == 0)
5895 {
5896 if (length < 4)
5897 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005898
cristy3ed852e2009-09-05 21:47:34 +00005899 else
5900 {
5901 mng_info->global_sbit.gray=p[0];
5902 mng_info->global_sbit.red=p[0];
5903 mng_info->global_sbit.green=p[1];
5904 mng_info->global_sbit.blue=p[2];
5905 mng_info->global_sbit.alpha=p[3];
5906 mng_info->have_global_sbit=MagickTrue;
5907 }
5908 }
5909 if (memcmp(type,mng_pHYs,4) == 0)
5910 {
5911 if (length > 8)
5912 {
5913 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005914 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005915 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005916 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005917 mng_info->global_phys_unit_type=p[8];
5918 mng_info->have_global_phys=MagickTrue;
5919 }
glennrp47b9dd52010-11-24 18:12:06 +00005920
cristy3ed852e2009-09-05 21:47:34 +00005921 else
5922 mng_info->have_global_phys=MagickFalse;
5923 }
5924 if (memcmp(type,mng_pHYg,4) == 0)
5925 {
5926 if (mng_info->phyg_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005927 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005928 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005929
cristy3ed852e2009-09-05 21:47:34 +00005930 mng_info->phyg_warning++;
5931 }
5932 if (memcmp(type,mng_BASI,4) == 0)
5933 {
5934 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005935
cristy3ed852e2009-09-05 21:47:34 +00005936 if (mng_info->basi_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005937 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005938 CoderError,"BASI is not implemented yet","`%s'",
5939 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005940
cristy3ed852e2009-09-05 21:47:34 +00005941 mng_info->basi_warning++;
5942#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005943 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005944 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005945 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005946 (p[6] << 8) | p[7]);
5947 basi_color_type=p[8];
5948 basi_compression_method=p[9];
5949 basi_filter_type=p[10];
5950 basi_interlace_method=p[11];
5951 if (length > 11)
5952 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005953
cristy3ed852e2009-09-05 21:47:34 +00005954 else
5955 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005956
cristy3ed852e2009-09-05 21:47:34 +00005957 if (length > 13)
5958 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005959
cristy3ed852e2009-09-05 21:47:34 +00005960 else
5961 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005962
cristy3ed852e2009-09-05 21:47:34 +00005963 if (length > 15)
5964 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005965
cristy3ed852e2009-09-05 21:47:34 +00005966 else
5967 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005968
cristy3ed852e2009-09-05 21:47:34 +00005969 if (length > 17)
5970 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005971
cristy3ed852e2009-09-05 21:47:34 +00005972 else
5973 {
5974 if (basi_sample_depth == 16)
5975 basi_alpha=65535L;
5976 else
5977 basi_alpha=255;
5978 }
glennrp47b9dd52010-11-24 18:12:06 +00005979
cristy3ed852e2009-09-05 21:47:34 +00005980 if (length > 19)
5981 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005982
cristy3ed852e2009-09-05 21:47:34 +00005983 else
5984 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005985
cristy3ed852e2009-09-05 21:47:34 +00005986#endif
5987 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5988 continue;
5989 }
glennrp47b9dd52010-11-24 18:12:06 +00005990
cristy3ed852e2009-09-05 21:47:34 +00005991 if (memcmp(type,mng_IHDR,4)
5992#if defined(JNG_SUPPORTED)
5993 && memcmp(type,mng_JHDR,4)
5994#endif
5995 )
5996 {
5997 /* Not an IHDR or JHDR chunk */
5998 if (length)
5999 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00006000
cristy3ed852e2009-09-05 21:47:34 +00006001 continue;
6002 }
6003/* Process IHDR */
6004 if (logging != MagickFalse)
6005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6006 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006007
cristy3ed852e2009-09-05 21:47:34 +00006008 mng_info->exists[object_id]=MagickTrue;
6009 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00006010
cristy3ed852e2009-09-05 21:47:34 +00006011 if (mng_info->invisible[object_id])
6012 {
6013 if (logging != MagickFalse)
6014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6015 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00006016
cristy3ed852e2009-09-05 21:47:34 +00006017 skip_to_iend=MagickTrue;
6018 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6019 continue;
6020 }
6021#if defined(MNG_INSERT_LAYERS)
6022 if (length < 8)
6023 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00006024
cristy8182b072010-05-30 20:10:53 +00006025 image_width=(size_t) mng_get_long(p);
6026 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00006027#endif
6028 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6029
6030 /*
6031 Insert a transparent background layer behind the entire animation
6032 if it is not full screen.
6033 */
6034#if defined(MNG_INSERT_LAYERS)
6035 if (insert_layers && mng_type && first_mng_object)
6036 {
6037 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6038 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00006039 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00006040 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00006041 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00006042 {
cristy16ea1392012-03-21 20:38:41 +00006043 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006044 {
6045 /*
6046 Allocate next image structure.
6047 */
cristy16ea1392012-03-21 20:38:41 +00006048 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006049
cristy3ed852e2009-09-05 21:47:34 +00006050 if (GetNextImageInList(image) == (Image *) NULL)
6051 {
6052 image=DestroyImageList(image);
6053 MngInfoFreeStruct(mng_info,&have_mng_structure);
6054 return((Image *) NULL);
6055 }
glennrp47b9dd52010-11-24 18:12:06 +00006056
cristy3ed852e2009-09-05 21:47:34 +00006057 image=SyncNextImageInList(image);
6058 }
6059 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00006060
cristy3ed852e2009-09-05 21:47:34 +00006061 if (term_chunk_found)
6062 {
6063 image->start_loop=MagickTrue;
6064 image->iterations=mng_iterations;
6065 term_chunk_found=MagickFalse;
6066 }
glennrp47b9dd52010-11-24 18:12:06 +00006067
cristy3ed852e2009-09-05 21:47:34 +00006068 else
6069 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006070
6071 /* Make a background rectangle. */
6072
cristy3ed852e2009-09-05 21:47:34 +00006073 image->delay=0;
6074 image->columns=mng_info->mng_width;
6075 image->rows=mng_info->mng_height;
6076 image->page.width=mng_info->mng_width;
6077 image->page.height=mng_info->mng_height;
6078 image->page.x=0;
6079 image->page.y=0;
6080 image->background_color=mng_background_color;
cristy16ea1392012-03-21 20:38:41 +00006081 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006082 if (logging != MagickFalse)
6083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006084 " Inserted transparent background layer, W=%.20g, H=%.20g",
6085 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00006086 }
6087 }
6088 /*
6089 Insert a background layer behind the upcoming image if
6090 framing_mode is 3, and we haven't already inserted one.
6091 */
6092 if (insert_layers && (mng_info->framing_mode == 3) &&
6093 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6094 (simplicity & 0x08)))
6095 {
cristy16ea1392012-03-21 20:38:41 +00006096 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006097 {
6098 /*
6099 Allocate next image structure.
6100 */
cristy16ea1392012-03-21 20:38:41 +00006101 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006102
cristy3ed852e2009-09-05 21:47:34 +00006103 if (GetNextImageInList(image) == (Image *) NULL)
6104 {
6105 image=DestroyImageList(image);
6106 MngInfoFreeStruct(mng_info,&have_mng_structure);
6107 return((Image *) NULL);
6108 }
glennrp47b9dd52010-11-24 18:12:06 +00006109
cristy3ed852e2009-09-05 21:47:34 +00006110 image=SyncNextImageInList(image);
6111 }
glennrp0fe50b42010-11-16 03:52:51 +00006112
cristy3ed852e2009-09-05 21:47:34 +00006113 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00006114
cristy3ed852e2009-09-05 21:47:34 +00006115 if (term_chunk_found)
6116 {
6117 image->start_loop=MagickTrue;
6118 image->iterations=mng_iterations;
6119 term_chunk_found=MagickFalse;
6120 }
glennrp0fe50b42010-11-16 03:52:51 +00006121
cristy3ed852e2009-09-05 21:47:34 +00006122 else
6123 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006124
cristy3ed852e2009-09-05 21:47:34 +00006125 image->delay=0;
6126 image->columns=subframe_width;
6127 image->rows=subframe_height;
6128 image->page.width=subframe_width;
6129 image->page.height=subframe_height;
6130 image->page.x=mng_info->clip.left;
6131 image->page.y=mng_info->clip.top;
6132 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00006133 image->alpha_trait=UndefinedPixelTrait;
cristy16ea1392012-03-21 20:38:41 +00006134 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006135
cristy3ed852e2009-09-05 21:47:34 +00006136 if (logging != MagickFalse)
6137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006138 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006139 (double) mng_info->clip.left,(double) mng_info->clip.right,
6140 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006141 }
6142#endif /* MNG_INSERT_LAYERS */
6143 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006144
cristy16ea1392012-03-21 20:38:41 +00006145 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006146 {
6147 /*
6148 Allocate next image structure.
6149 */
cristy16ea1392012-03-21 20:38:41 +00006150 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006151
cristy3ed852e2009-09-05 21:47:34 +00006152 if (GetNextImageInList(image) == (Image *) NULL)
6153 {
6154 image=DestroyImageList(image);
6155 MngInfoFreeStruct(mng_info,&have_mng_structure);
6156 return((Image *) NULL);
6157 }
glennrp47b9dd52010-11-24 18:12:06 +00006158
cristy3ed852e2009-09-05 21:47:34 +00006159 image=SyncNextImageInList(image);
6160 }
6161 mng_info->image=image;
6162 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6163 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006164
cristy3ed852e2009-09-05 21:47:34 +00006165 if (status == MagickFalse)
6166 break;
glennrp0fe50b42010-11-16 03:52:51 +00006167
cristy3ed852e2009-09-05 21:47:34 +00006168 if (term_chunk_found)
6169 {
6170 image->start_loop=MagickTrue;
6171 term_chunk_found=MagickFalse;
6172 }
glennrp0fe50b42010-11-16 03:52:51 +00006173
cristy3ed852e2009-09-05 21:47:34 +00006174 else
6175 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006176
cristy3ed852e2009-09-05 21:47:34 +00006177 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6178 {
6179 image->delay=frame_delay;
6180 frame_delay=default_frame_delay;
6181 }
glennrp0fe50b42010-11-16 03:52:51 +00006182
cristy3ed852e2009-09-05 21:47:34 +00006183 else
6184 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006185
cristy3ed852e2009-09-05 21:47:34 +00006186 image->page.width=mng_info->mng_width;
6187 image->page.height=mng_info->mng_height;
6188 image->page.x=mng_info->x_off[object_id];
6189 image->page.y=mng_info->y_off[object_id];
6190 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006191
cristy3ed852e2009-09-05 21:47:34 +00006192 /*
6193 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6194 */
glennrp47b9dd52010-11-24 18:12:06 +00006195
cristy3ed852e2009-09-05 21:47:34 +00006196 if (logging != MagickFalse)
6197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6198 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6199 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006200
cristybb503372010-05-27 20:51:26 +00006201 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006202
cristy3ed852e2009-09-05 21:47:34 +00006203 if (offset < 0)
6204 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6205 }
6206
6207 previous=image;
6208 mng_info->image=image;
6209 mng_info->mng_type=mng_type;
6210 mng_info->object_id=object_id;
6211
6212 if (memcmp(type,mng_IHDR,4) == 0)
6213 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006214
cristy3ed852e2009-09-05 21:47:34 +00006215#if defined(JNG_SUPPORTED)
6216 else
6217 image=ReadOneJNGImage(mng_info,image_info,exception);
6218#endif
6219
6220 if (image == (Image *) NULL)
6221 {
6222 if (IsImageObject(previous) != MagickFalse)
6223 {
6224 (void) DestroyImageList(previous);
6225 (void) CloseBlob(previous);
6226 }
glennrp47b9dd52010-11-24 18:12:06 +00006227
cristy3ed852e2009-09-05 21:47:34 +00006228 MngInfoFreeStruct(mng_info,&have_mng_structure);
6229 return((Image *) NULL);
6230 }
glennrp0fe50b42010-11-16 03:52:51 +00006231
cristy3ed852e2009-09-05 21:47:34 +00006232 if (image->columns == 0 || image->rows == 0)
6233 {
6234 (void) CloseBlob(image);
6235 image=DestroyImageList(image);
6236 MngInfoFreeStruct(mng_info,&have_mng_structure);
6237 return((Image *) NULL);
6238 }
glennrp0fe50b42010-11-16 03:52:51 +00006239
cristy3ed852e2009-09-05 21:47:34 +00006240 mng_info->image=image;
6241
6242 if (mng_type)
6243 {
6244 MngBox
6245 crop_box;
6246
6247 if (mng_info->magn_methx || mng_info->magn_methy)
6248 {
6249 png_uint_32
6250 magnified_height,
6251 magnified_width;
6252
6253 if (logging != MagickFalse)
6254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6255 " Processing MNG MAGN chunk");
6256
6257 if (mng_info->magn_methx == 1)
6258 {
6259 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006260
cristy3ed852e2009-09-05 21:47:34 +00006261 if (image->columns > 1)
6262 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006263
cristy3ed852e2009-09-05 21:47:34 +00006264 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006265 magnified_width += (png_uint_32)
6266 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006267 }
glennrp47b9dd52010-11-24 18:12:06 +00006268
cristy3ed852e2009-09-05 21:47:34 +00006269 else
6270 {
cristy4e5bc842010-06-09 13:56:01 +00006271 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006272
cristy3ed852e2009-09-05 21:47:34 +00006273 if (image->columns > 1)
6274 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006275
cristy3ed852e2009-09-05 21:47:34 +00006276 if (image->columns > 2)
6277 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006278
cristy3ed852e2009-09-05 21:47:34 +00006279 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006280 magnified_width += (png_uint_32)
6281 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006282 }
glennrp47b9dd52010-11-24 18:12:06 +00006283
cristy3ed852e2009-09-05 21:47:34 +00006284 if (mng_info->magn_methy == 1)
6285 {
6286 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006287
cristy3ed852e2009-09-05 21:47:34 +00006288 if (image->rows > 1)
6289 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006290
cristy3ed852e2009-09-05 21:47:34 +00006291 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006292 magnified_height += (png_uint_32)
6293 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006294 }
glennrp47b9dd52010-11-24 18:12:06 +00006295
cristy3ed852e2009-09-05 21:47:34 +00006296 else
6297 {
cristy4e5bc842010-06-09 13:56:01 +00006298 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006299
cristy3ed852e2009-09-05 21:47:34 +00006300 if (image->rows > 1)
6301 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006302
cristy3ed852e2009-09-05 21:47:34 +00006303 if (image->rows > 2)
6304 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006305
cristy3ed852e2009-09-05 21:47:34 +00006306 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006307 magnified_height += (png_uint_32)
6308 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006309 }
glennrp47b9dd52010-11-24 18:12:06 +00006310
cristy3ed852e2009-09-05 21:47:34 +00006311 if (magnified_height > image->rows ||
6312 magnified_width > image->columns)
6313 {
6314 Image
6315 *large_image;
6316
6317 int
6318 yy;
6319
cristy16ea1392012-03-21 20:38:41 +00006320 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006321 *next,
6322 *prev;
6323
6324 png_uint_16
6325 magn_methx,
6326 magn_methy;
6327
cristy16ea1392012-03-21 20:38:41 +00006328 ssize_t
6329 m,
6330 y;
6331
6332 register Quantum
6333 *n,
6334 *q;
6335
6336 register ssize_t
6337 x;
6338
glennrp47b9dd52010-11-24 18:12:06 +00006339 /* Allocate next image structure. */
6340
cristy3ed852e2009-09-05 21:47:34 +00006341 if (logging != MagickFalse)
6342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6343 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006344
cristy16ea1392012-03-21 20:38:41 +00006345 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006346
cristy3ed852e2009-09-05 21:47:34 +00006347 if (GetNextImageInList(image) == (Image *) NULL)
6348 {
6349 image=DestroyImageList(image);
6350 MngInfoFreeStruct(mng_info,&have_mng_structure);
6351 return((Image *) NULL);
6352 }
6353
6354 large_image=SyncNextImageInList(image);
6355
6356 large_image->columns=magnified_width;
6357 large_image->rows=magnified_height;
6358
6359 magn_methx=mng_info->magn_methx;
6360 magn_methy=mng_info->magn_methy;
6361
glennrp3faa9a32011-04-23 14:00:25 +00006362#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006363#define QM unsigned short
6364 if (magn_methx != 1 || magn_methy != 1)
6365 {
6366 /*
6367 Scale pixels to unsigned shorts to prevent
6368 overflow of intermediate values of interpolations
6369 */
cristybb503372010-05-27 20:51:26 +00006370 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006371 {
6372 q=GetAuthenticPixels(image,0,y,image->columns,1,
6373 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006374
cristybb503372010-05-27 20:51:26 +00006375 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006376 {
cristy16ea1392012-03-21 20:38:41 +00006377 SetPixelRed(image,ScaleQuantumToShort(
6378 GetPixelRed(image,q)),q);
6379 SetPixelGreen(image,ScaleQuantumToShort(
6380 GetPixelGreen(image,q)),q);
6381 SetPixelBlue(image,ScaleQuantumToShort(
6382 GetPixelBlue(image,q)),q);
6383 SetPixelAlpha(image,ScaleQuantumToShort(
6384 GetPixelAlpha(image,q)),q);
6385 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006386 }
glennrp47b9dd52010-11-24 18:12:06 +00006387
cristy3ed852e2009-09-05 21:47:34 +00006388 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6389 break;
6390 }
6391 }
6392#else
6393#define QM Quantum
6394#endif
6395
cristy8a46d822012-08-28 23:32:39 +00006396 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006397 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006398
cristy3ed852e2009-09-05 21:47:34 +00006399 else
6400 {
cristy16ea1392012-03-21 20:38:41 +00006401 large_image->background_color.alpha=OpaqueAlpha;
6402 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006403
cristy3ed852e2009-09-05 21:47:34 +00006404 if (magn_methx == 4)
6405 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006406
cristy3ed852e2009-09-05 21:47:34 +00006407 if (magn_methx == 5)
6408 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006409
cristy3ed852e2009-09-05 21:47:34 +00006410 if (magn_methy == 4)
6411 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006412
cristy3ed852e2009-09-05 21:47:34 +00006413 if (magn_methy == 5)
6414 magn_methy=3;
6415 }
6416
6417 /* magnify the rows into the right side of the large image */
6418
6419 if (logging != MagickFalse)
6420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006421 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006422 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006423 yy=0;
cristy16ea1392012-03-21 20:38:41 +00006424 length=(size_t) image->columns*GetPixelChannels(image);
6425 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6426 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006427
cristy16ea1392012-03-21 20:38:41 +00006428 if ((prev == (Quantum *) NULL) ||
6429 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006430 {
6431 image=DestroyImageList(image);
6432 MngInfoFreeStruct(mng_info,&have_mng_structure);
6433 ThrowReaderException(ResourceLimitError,
6434 "MemoryAllocationFailed");
6435 }
glennrp47b9dd52010-11-24 18:12:06 +00006436
cristy3ed852e2009-09-05 21:47:34 +00006437 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6438 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006439
cristybb503372010-05-27 20:51:26 +00006440 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006441 {
6442 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006443 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006444
cristybb503372010-05-27 20:51:26 +00006445 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6446 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006447
cristybb503372010-05-27 20:51:26 +00006448 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6449 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006450
cristybb503372010-05-27 20:51:26 +00006451 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006452 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006453
cristy3ed852e2009-09-05 21:47:34 +00006454 else
cristybb503372010-05-27 20:51:26 +00006455 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006456
cristy3ed852e2009-09-05 21:47:34 +00006457 n=prev;
6458 prev=next;
6459 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006460
cristybb503372010-05-27 20:51:26 +00006461 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006462 {
6463 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6464 exception);
6465 (void) CopyMagickMemory(next,n,length);
6466 }
glennrp47b9dd52010-11-24 18:12:06 +00006467
cristy3ed852e2009-09-05 21:47:34 +00006468 for (i=0; i < m; i++, yy++)
6469 {
cristy16ea1392012-03-21 20:38:41 +00006470 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006471 *pixels;
6472
cristybb503372010-05-27 20:51:26 +00006473 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006474 pixels=prev;
6475 n=next;
6476 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006477 1,exception);
cristy16ea1392012-03-21 20:38:41 +00006478 q+=(large_image->columns-image->columns)*
6479 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006480
cristybb503372010-05-27 20:51:26 +00006481 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006482 {
glennrpfd05d622011-02-25 04:10:33 +00006483 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006484 /*
6485 if (image->storage_class == PseudoClass)
6486 {
6487 }
6488 */
6489
6490 if (magn_methy <= 1)
6491 {
glennrpbb4f99d2011-05-22 11:13:17 +00006492 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006493 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6494 SetPixelGreen(large_image,GetPixelGreen(image,
6495 pixels),q);
6496 SetPixelBlue(large_image,GetPixelBlue(image,
6497 pixels),q);
6498 SetPixelAlpha(large_image,GetPixelAlpha(image,
6499 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006500 }
glennrp47b9dd52010-11-24 18:12:06 +00006501
cristy3ed852e2009-09-05 21:47:34 +00006502 else if (magn_methy == 2 || magn_methy == 4)
6503 {
6504 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006505 {
cristy16ea1392012-03-21 20:38:41 +00006506 SetPixelRed(large_image,GetPixelRed(image,
6507 pixels),q);
6508 SetPixelGreen(large_image,GetPixelGreen(image,
6509 pixels),q);
6510 SetPixelBlue(large_image,GetPixelBlue(image,
6511 pixels),q);
6512 SetPixelAlpha(large_image,GetPixelAlpha(image,
6513 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006514 }
glennrp47b9dd52010-11-24 18:12:06 +00006515
cristy3ed852e2009-09-05 21:47:34 +00006516 else
6517 {
6518 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006519 SetPixelRed(large_image,((QM) (((ssize_t)
6520 (2*i*(GetPixelRed(image,n)
6521 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006522 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006523 +GetPixelRed(image,pixels)))),q);
6524 SetPixelGreen(large_image,((QM) (((ssize_t)
6525 (2*i*(GetPixelGreen(image,n)
6526 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006527 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006528 +GetPixelGreen(image,pixels)))),q);
6529 SetPixelBlue(large_image,((QM) (((ssize_t)
6530 (2*i*(GetPixelBlue(image,n)
6531 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006532 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006533 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006534
cristy8a46d822012-08-28 23:32:39 +00006535 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006536 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6537 (2*i*(GetPixelAlpha(image,n)
6538 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006539 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006540 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006541 }
glennrp47b9dd52010-11-24 18:12:06 +00006542
cristy3ed852e2009-09-05 21:47:34 +00006543 if (magn_methy == 4)
6544 {
6545 /* Replicate nearest */
6546 if (i <= ((m+1) << 1))
cristy16ea1392012-03-21 20:38:41 +00006547 SetPixelAlpha(large_image,GetPixelAlpha(image,
6548 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006549 else
cristy16ea1392012-03-21 20:38:41 +00006550 SetPixelAlpha(large_image,GetPixelAlpha(image,
6551 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006552 }
6553 }
glennrp47b9dd52010-11-24 18:12:06 +00006554
cristy3ed852e2009-09-05 21:47:34 +00006555 else /* if (magn_methy == 3 || magn_methy == 5) */
6556 {
6557 /* Replicate nearest */
6558 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006559 {
cristy16ea1392012-03-21 20:38:41 +00006560 SetPixelRed(large_image,GetPixelRed(image,
6561 pixels),q);
6562 SetPixelGreen(large_image,GetPixelGreen(image,
6563 pixels),q);
6564 SetPixelBlue(large_image,GetPixelBlue(image,
6565 pixels),q);
6566 SetPixelAlpha(large_image,GetPixelAlpha(image,
6567 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006568 }
glennrp47b9dd52010-11-24 18:12:06 +00006569
cristy3ed852e2009-09-05 21:47:34 +00006570 else
glennrpbb4f99d2011-05-22 11:13:17 +00006571 {
cristy16ea1392012-03-21 20:38:41 +00006572 SetPixelRed(large_image,GetPixelRed(image,n),q);
6573 SetPixelGreen(large_image,GetPixelGreen(image,n),
6574 q);
6575 SetPixelBlue(large_image,GetPixelBlue(image,n),
6576 q);
6577 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6578 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006579 }
glennrp47b9dd52010-11-24 18:12:06 +00006580
cristy3ed852e2009-09-05 21:47:34 +00006581 if (magn_methy == 5)
6582 {
cristy16ea1392012-03-21 20:38:41 +00006583 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6584 (GetPixelAlpha(image,n)
6585 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006586 +m))/((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006587 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006588 }
6589 }
cristy16ea1392012-03-21 20:38:41 +00006590 n+=GetPixelChannels(image);
6591 q+=GetPixelChannels(large_image);
6592 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006593 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006594
cristy3ed852e2009-09-05 21:47:34 +00006595 if (SyncAuthenticPixels(large_image,exception) == 0)
6596 break;
glennrp47b9dd52010-11-24 18:12:06 +00006597
cristy3ed852e2009-09-05 21:47:34 +00006598 } /* i */
6599 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006600
cristy16ea1392012-03-21 20:38:41 +00006601 prev=(Quantum *) RelinquishMagickMemory(prev);
6602 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006603
6604 length=image->columns;
6605
6606 if (logging != MagickFalse)
6607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6608 " Delete original image");
6609
6610 DeleteImageFromList(&image);
6611
6612 image=large_image;
6613
6614 mng_info->image=image;
6615
6616 /* magnify the columns */
6617 if (logging != MagickFalse)
6618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006619 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006620
cristybb503372010-05-27 20:51:26 +00006621 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006622 {
cristy16ea1392012-03-21 20:38:41 +00006623 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006624 *pixels;
6625
6626 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00006627 pixels=q+(image->columns-length)*GetPixelChannels(image);
6628 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006629
cristybb503372010-05-27 20:51:26 +00006630 for (x=(ssize_t) (image->columns-length);
6631 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006632 {
cristy16ea1392012-03-21 20:38:41 +00006633 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006634
cristybb503372010-05-27 20:51:26 +00006635 if (x == (ssize_t) (image->columns-length))
6636 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006637
cristybb503372010-05-27 20:51:26 +00006638 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6639 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006640
cristybb503372010-05-27 20:51:26 +00006641 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6642 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006643
cristybb503372010-05-27 20:51:26 +00006644 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006645 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006646
cristy3ed852e2009-09-05 21:47:34 +00006647 else
cristybb503372010-05-27 20:51:26 +00006648 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006649
cristy3ed852e2009-09-05 21:47:34 +00006650 for (i=0; i < m; i++)
6651 {
6652 if (magn_methx <= 1)
6653 {
6654 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006655 SetPixelRed(image,GetPixelRed(image,pixels),q);
6656 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6657 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6658 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006659 }
glennrp47b9dd52010-11-24 18:12:06 +00006660
cristy3ed852e2009-09-05 21:47:34 +00006661 else if (magn_methx == 2 || magn_methx == 4)
6662 {
6663 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006664 {
cristy16ea1392012-03-21 20:38:41 +00006665 SetPixelRed(image,GetPixelRed(image,pixels),q);
6666 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6667 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6668 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006669 }
glennrp47b9dd52010-11-24 18:12:06 +00006670
cristy16ea1392012-03-21 20:38:41 +00006671 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006672 else
6673 {
6674 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006675 SetPixelRed(image,(QM) ((2*i*(
6676 GetPixelRed(image,n)
6677 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006678 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006679 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006680
cristy16ea1392012-03-21 20:38:41 +00006681 SetPixelGreen(image,(QM) ((2*i*(
6682 GetPixelGreen(image,n)
6683 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006684 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006685 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006686
cristy16ea1392012-03-21 20:38:41 +00006687 SetPixelBlue(image,(QM) ((2*i*(
6688 GetPixelBlue(image,n)
6689 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006690 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006691 GetPixelBlue(image,pixels)),q);
cristy8a46d822012-08-28 23:32:39 +00006692 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +00006693 SetPixelAlpha(image,(QM) ((2*i*(
6694 GetPixelAlpha(image,n)
6695 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006696 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006697 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006698 }
glennrp47b9dd52010-11-24 18:12:06 +00006699
cristy3ed852e2009-09-05 21:47:34 +00006700 if (magn_methx == 4)
6701 {
6702 /* Replicate nearest */
6703 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006704 {
cristy16ea1392012-03-21 20:38:41 +00006705 SetPixelAlpha(image,
6706 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006707 }
cristy3ed852e2009-09-05 21:47:34 +00006708 else
glennrpbb4f99d2011-05-22 11:13:17 +00006709 {
cristy16ea1392012-03-21 20:38:41 +00006710 SetPixelAlpha(image,
6711 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006712 }
cristy3ed852e2009-09-05 21:47:34 +00006713 }
6714 }
glennrp47b9dd52010-11-24 18:12:06 +00006715
cristy3ed852e2009-09-05 21:47:34 +00006716 else /* if (magn_methx == 3 || magn_methx == 5) */
6717 {
6718 /* Replicate nearest */
6719 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006720 {
cristy16ea1392012-03-21 20:38:41 +00006721 SetPixelRed(image,GetPixelRed(image,pixels),q);
6722 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6723 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6724 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006725 }
glennrp47b9dd52010-11-24 18:12:06 +00006726
cristy3ed852e2009-09-05 21:47:34 +00006727 else
glennrpbb4f99d2011-05-22 11:13:17 +00006728 {
cristy16ea1392012-03-21 20:38:41 +00006729 SetPixelRed(image,GetPixelRed(image,n),q);
6730 SetPixelGreen(image,GetPixelGreen(image,n),q);
6731 SetPixelBlue(image,GetPixelBlue(image,n),q);
6732 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006733 }
glennrp47b9dd52010-11-24 18:12:06 +00006734
cristy3ed852e2009-09-05 21:47:34 +00006735 if (magn_methx == 5)
6736 {
6737 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006738 SetPixelAlpha(image,
6739 (QM) ((2*i*( GetPixelAlpha(image,n)
6740 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006741 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006742 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006743 }
6744 }
cristy16ea1392012-03-21 20:38:41 +00006745 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006746 }
cristy16ea1392012-03-21 20:38:41 +00006747 n+=GetPixelChannels(image);
6748 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006749 }
glennrp47b9dd52010-11-24 18:12:06 +00006750
cristy3ed852e2009-09-05 21:47:34 +00006751 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6752 break;
6753 }
glennrp3faa9a32011-04-23 14:00:25 +00006754#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006755 if (magn_methx != 1 || magn_methy != 1)
6756 {
6757 /*
6758 Rescale pixels to Quantum
6759 */
cristybb503372010-05-27 20:51:26 +00006760 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006761 {
6762 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006763
cristybb503372010-05-27 20:51:26 +00006764 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006765 {
cristy16ea1392012-03-21 20:38:41 +00006766 SetPixelRed(image,ScaleShortToQuantum(
6767 GetPixelRed(image,q)),q);
6768 SetPixelGreen(image,ScaleShortToQuantum(
6769 GetPixelGreen(image,q)),q);
6770 SetPixelBlue(image,ScaleShortToQuantum(
6771 GetPixelBlue(image,q)),q);
6772 SetPixelAlpha(image,ScaleShortToQuantum(
6773 GetPixelAlpha(image,q)),q);
6774 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006775 }
glennrp47b9dd52010-11-24 18:12:06 +00006776
cristy3ed852e2009-09-05 21:47:34 +00006777 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6778 break;
6779 }
6780 }
6781#endif
6782 if (logging != MagickFalse)
6783 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6784 " Finished MAGN processing");
6785 }
6786 }
6787
6788 /*
6789 Crop_box is with respect to the upper left corner of the MNG.
6790 */
6791 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6792 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6793 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6794 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6795 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6796 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6797 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6798 if ((crop_box.left != (mng_info->image_box.left
6799 +mng_info->x_off[object_id])) ||
6800 (crop_box.right != (mng_info->image_box.right
6801 +mng_info->x_off[object_id])) ||
6802 (crop_box.top != (mng_info->image_box.top
6803 +mng_info->y_off[object_id])) ||
6804 (crop_box.bottom != (mng_info->image_box.bottom
6805 +mng_info->y_off[object_id])))
6806 {
6807 if (logging != MagickFalse)
6808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6809 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006810
cristy3ed852e2009-09-05 21:47:34 +00006811 if ((crop_box.left < crop_box.right) &&
6812 (crop_box.top < crop_box.bottom))
6813 {
6814 Image
6815 *im;
6816
6817 RectangleInfo
6818 crop_info;
6819
6820 /*
6821 Crop_info is with respect to the upper left corner of
6822 the image.
6823 */
6824 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6825 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006826 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6827 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006828 image->page.width=image->columns;
6829 image->page.height=image->rows;
6830 image->page.x=0;
6831 image->page.y=0;
6832 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006833
cristy3ed852e2009-09-05 21:47:34 +00006834 if (im != (Image *) NULL)
6835 {
6836 image->columns=im->columns;
6837 image->rows=im->rows;
6838 im=DestroyImage(im);
6839 image->page.width=image->columns;
6840 image->page.height=image->rows;
6841 image->page.x=crop_box.left;
6842 image->page.y=crop_box.top;
6843 }
6844 }
glennrp47b9dd52010-11-24 18:12:06 +00006845
cristy3ed852e2009-09-05 21:47:34 +00006846 else
6847 {
6848 /*
6849 No pixels in crop area. The MNG spec still requires
6850 a layer, though, so make a single transparent pixel in
6851 the top left corner.
6852 */
6853 image->columns=1;
6854 image->rows=1;
6855 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00006856 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006857 image->page.width=1;
6858 image->page.height=1;
6859 image->page.x=0;
6860 image->page.y=0;
6861 }
6862 }
6863#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6864 image=mng_info->image;
6865#endif
6866 }
6867
glennrp2b013e42010-11-24 16:55:50 +00006868#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6869 /* PNG does not handle depths greater than 16 so reduce it even
cristy16ea1392012-03-21 20:38:41 +00006870 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00006871 */
6872 if (image->depth > 16)
6873 image->depth=16;
6874#endif
6875
glennrp3faa9a32011-04-23 14:00:25 +00006876#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00006877 if (image->depth > 8)
6878 {
6879 /* To do: fill low byte properly */
6880 image->depth=16;
6881 }
6882
cristy16ea1392012-03-21 20:38:41 +00006883 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006884 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006885#endif
glennrpd6afd542010-11-19 01:53:05 +00006886
cristy3ed852e2009-09-05 21:47:34 +00006887 if (image_info->number_scenes != 0)
6888 {
6889 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006890 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006891 break;
6892 }
glennrpd6afd542010-11-19 01:53:05 +00006893
cristy3ed852e2009-09-05 21:47:34 +00006894 if (logging != MagickFalse)
6895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6896 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006897
cristy3ed852e2009-09-05 21:47:34 +00006898 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006899
cristy3ed852e2009-09-05 21:47:34 +00006900 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006901
cristy3ed852e2009-09-05 21:47:34 +00006902 if (logging != MagickFalse)
6903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6904 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006905
cristy3ed852e2009-09-05 21:47:34 +00006906#if defined(MNG_INSERT_LAYERS)
6907 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6908 (mng_info->mng_height))
6909 {
6910 /*
6911 Insert a background layer if nothing else was found.
6912 */
6913 if (logging != MagickFalse)
6914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6915 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006916
cristy16ea1392012-03-21 20:38:41 +00006917 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006918 {
6919 /*
6920 Allocate next image structure.
6921 */
cristy16ea1392012-03-21 20:38:41 +00006922 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006923 if (GetNextImageInList(image) == (Image *) NULL)
6924 {
6925 image=DestroyImageList(image);
6926 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006927
cristy3ed852e2009-09-05 21:47:34 +00006928 if (logging != MagickFalse)
6929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6930 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006931
cristy3ed852e2009-09-05 21:47:34 +00006932 return((Image *) NULL);
6933 }
6934 image=SyncNextImageInList(image);
6935 }
6936 image->columns=mng_info->mng_width;
6937 image->rows=mng_info->mng_height;
6938 image->page.width=mng_info->mng_width;
6939 image->page.height=mng_info->mng_height;
6940 image->page.x=0;
6941 image->page.y=0;
6942 image->background_color=mng_background_color;
cristy8a46d822012-08-28 23:32:39 +00006943 image->alpha_trait=UndefinedPixelTrait;
glennrp0fe50b42010-11-16 03:52:51 +00006944
cristy3ed852e2009-09-05 21:47:34 +00006945 if (image_info->ping == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006946 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006947
cristy3ed852e2009-09-05 21:47:34 +00006948 mng_info->image_found++;
6949 }
6950#endif
6951 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006952
cristy3ed852e2009-09-05 21:47:34 +00006953 if (mng_iterations == 1)
6954 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006955
cristy3ed852e2009-09-05 21:47:34 +00006956 while (GetPreviousImageInList(image) != (Image *) NULL)
6957 {
6958 image_count++;
6959 if (image_count > 10*mng_info->image_found)
6960 {
6961 if (logging != MagickFalse)
6962 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006963
cristy16ea1392012-03-21 20:38:41 +00006964 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006965 CoderError,"Linked list is corrupted, beginning of list not found",
6966 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006967
cristy3ed852e2009-09-05 21:47:34 +00006968 return((Image *) NULL);
6969 }
glennrp0fe50b42010-11-16 03:52:51 +00006970
cristy3ed852e2009-09-05 21:47:34 +00006971 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006972
cristy3ed852e2009-09-05 21:47:34 +00006973 if (GetNextImageInList(image) == (Image *) NULL)
6974 {
6975 if (logging != MagickFalse)
6976 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006977
cristy16ea1392012-03-21 20:38:41 +00006978 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006979 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6980 image_info->filename);
6981 }
6982 }
glennrp47b9dd52010-11-24 18:12:06 +00006983
cristy3ed852e2009-09-05 21:47:34 +00006984 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6985 GetNextImageInList(image) ==
6986 (Image *) NULL)
6987 {
6988 if (logging != MagickFalse)
6989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6990 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006991
cristy16ea1392012-03-21 20:38:41 +00006992 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006993 CoderError,"image->next for first image is NULL but shouldn't be.",
6994 "`%s'",image_info->filename);
6995 }
glennrp47b9dd52010-11-24 18:12:06 +00006996
cristy3ed852e2009-09-05 21:47:34 +00006997 if (mng_info->image_found == 0)
6998 {
6999 if (logging != MagickFalse)
7000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7001 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00007002
cristy16ea1392012-03-21 20:38:41 +00007003 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00007004 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007005
cristy3ed852e2009-09-05 21:47:34 +00007006 if (image != (Image *) NULL)
7007 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007008
cristy3ed852e2009-09-05 21:47:34 +00007009 MngInfoFreeStruct(mng_info,&have_mng_structure);
7010 return((Image *) NULL);
7011 }
7012
7013 if (mng_info->ticks_per_second)
7014 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7015 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00007016
cristy3ed852e2009-09-05 21:47:34 +00007017 else
7018 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00007019
cristy3ed852e2009-09-05 21:47:34 +00007020 /* Find final nonzero image delay */
7021 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00007022
cristy3ed852e2009-09-05 21:47:34 +00007023 while (GetNextImageInList(image) != (Image *) NULL)
7024 {
7025 if (image->delay)
7026 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00007027
cristy3ed852e2009-09-05 21:47:34 +00007028 image=GetNextImageInList(image);
7029 }
glennrp0fe50b42010-11-16 03:52:51 +00007030
cristy3ed852e2009-09-05 21:47:34 +00007031 if (final_delay < final_image_delay)
7032 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00007033
cristy3ed852e2009-09-05 21:47:34 +00007034 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00007035
cristy3ed852e2009-09-05 21:47:34 +00007036 if (logging != MagickFalse)
7037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007038 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7039 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00007040
cristy3ed852e2009-09-05 21:47:34 +00007041 if (logging != MagickFalse)
7042 {
7043 int
7044 scene;
7045
7046 scene=0;
7047 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007048
cristy3ed852e2009-09-05 21:47:34 +00007049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7050 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007051
cristy3ed852e2009-09-05 21:47:34 +00007052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007053 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00007054
cristy3ed852e2009-09-05 21:47:34 +00007055 while (GetNextImageInList(image) != (Image *) NULL)
7056 {
7057 image=GetNextImageInList(image);
7058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007059 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00007060 }
7061 }
7062
7063 image=GetFirstImageInList(image);
7064#ifdef MNG_COALESCE_LAYERS
7065 if (insert_layers)
7066 {
7067 Image
7068 *next_image,
7069 *next;
7070
cristybb503372010-05-27 20:51:26 +00007071 size_t
cristy3ed852e2009-09-05 21:47:34 +00007072 scene;
7073
7074 if (logging != MagickFalse)
7075 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00007076
cristy3ed852e2009-09-05 21:47:34 +00007077 scene=image->scene;
cristy16ea1392012-03-21 20:38:41 +00007078 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00007079
cristy3ed852e2009-09-05 21:47:34 +00007080 if (next_image == (Image *) NULL)
7081 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00007082
cristy3ed852e2009-09-05 21:47:34 +00007083 image=DestroyImageList(image);
7084 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00007085
cristy3ed852e2009-09-05 21:47:34 +00007086 for (next=image; next != (Image *) NULL; next=next_image)
7087 {
7088 next->page.width=mng_info->mng_width;
7089 next->page.height=mng_info->mng_height;
7090 next->page.x=0;
7091 next->page.y=0;
7092 next->scene=scene++;
7093 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00007094
cristy3ed852e2009-09-05 21:47:34 +00007095 if (next_image == (Image *) NULL)
7096 break;
glennrp47b9dd52010-11-24 18:12:06 +00007097
cristy3ed852e2009-09-05 21:47:34 +00007098 if (next->delay == 0)
7099 {
7100 scene--;
7101 next_image->previous=GetPreviousImageInList(next);
7102 if (GetPreviousImageInList(next) == (Image *) NULL)
7103 image=next_image;
7104 else
7105 next->previous->next=next_image;
7106 next=DestroyImage(next);
7107 }
7108 }
7109 }
7110#endif
7111
7112 while (GetNextImageInList(image) != (Image *) NULL)
7113 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007114
cristy3ed852e2009-09-05 21:47:34 +00007115 image->dispose=BackgroundDispose;
7116
7117 if (logging != MagickFalse)
7118 {
7119 int
7120 scene;
7121
7122 scene=0;
7123 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007124
cristy3ed852e2009-09-05 21:47:34 +00007125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7126 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007127
cristy3ed852e2009-09-05 21:47:34 +00007128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007129 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7130 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007131
cristy3ed852e2009-09-05 21:47:34 +00007132 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007133 {
7134 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007135
cristyf2faecf2010-05-28 19:19:36 +00007136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007137 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7138 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007139 }
7140 }
glennrp47b9dd52010-11-24 18:12:06 +00007141
cristy3ed852e2009-09-05 21:47:34 +00007142 image=GetFirstImageInList(image);
7143 MngInfoFreeStruct(mng_info,&have_mng_structure);
7144 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007145
cristy3ed852e2009-09-05 21:47:34 +00007146 if (logging != MagickFalse)
7147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007148
cristy3ed852e2009-09-05 21:47:34 +00007149 return(GetFirstImageInList(image));
7150}
glennrp25c1e2b2010-03-25 01:39:56 +00007151#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007152static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7153{
7154 printf("Your PNG library is too old: You have libpng-%s\n",
7155 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007156
cristy3ed852e2009-09-05 21:47:34 +00007157 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7158 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007159
cristy3ed852e2009-09-05 21:47:34 +00007160 return(Image *) NULL;
7161}
glennrp47b9dd52010-11-24 18:12:06 +00007162
cristy3ed852e2009-09-05 21:47:34 +00007163static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7164{
7165 return(ReadPNGImage(image_info,exception));
7166}
glennrp25c1e2b2010-03-25 01:39:56 +00007167#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007168#endif
7169
7170/*
7171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7172% %
7173% %
7174% %
7175% R e g i s t e r P N G I m a g e %
7176% %
7177% %
7178% %
7179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7180%
7181% RegisterPNGImage() adds properties for the PNG image format to
7182% the list of supported formats. The properties include the image format
7183% tag, a method to read and/or write the format, whether the format
7184% supports the saving of more than one frame to the same file or blob,
7185% whether the format supports native in-memory I/O, and a brief
7186% description of the format.
7187%
7188% The format of the RegisterPNGImage method is:
7189%
cristybb503372010-05-27 20:51:26 +00007190% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007191%
7192*/
cristybb503372010-05-27 20:51:26 +00007193ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007194{
7195 char
7196 version[MaxTextExtent];
7197
7198 MagickInfo
7199 *entry;
7200
7201 static const char
7202 *PNGNote=
7203 {
7204 "See http://www.libpng.org/ for details about the PNG format."
7205 },
glennrp47b9dd52010-11-24 18:12:06 +00007206
cristy3ed852e2009-09-05 21:47:34 +00007207 *JNGNote=
7208 {
7209 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7210 "format."
7211 },
glennrp47b9dd52010-11-24 18:12:06 +00007212
cristy3ed852e2009-09-05 21:47:34 +00007213 *MNGNote=
7214 {
7215 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7216 "format."
7217 };
7218
7219 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007220
cristy3ed852e2009-09-05 21:47:34 +00007221#if defined(PNG_LIBPNG_VER_STRING)
7222 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7223 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007224
cristy3ed852e2009-09-05 21:47:34 +00007225 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7226 {
7227 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7228 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7229 MaxTextExtent);
7230 }
7231#endif
glennrp47b9dd52010-11-24 18:12:06 +00007232
cristy3ed852e2009-09-05 21:47:34 +00007233 entry=SetMagickInfo("MNG");
7234 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007235
cristy3ed852e2009-09-05 21:47:34 +00007236#if defined(MAGICKCORE_PNG_DELEGATE)
7237 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7238 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7239#endif
glennrp47b9dd52010-11-24 18:12:06 +00007240
cristy3ed852e2009-09-05 21:47:34 +00007241 entry->magick=(IsImageFormatHandler *) IsMNG;
7242 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007243
cristy3ed852e2009-09-05 21:47:34 +00007244 if (*version != '\0')
7245 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007246
cristy3ed852e2009-09-05 21:47:34 +00007247 entry->module=ConstantString("PNG");
7248 entry->note=ConstantString(MNGNote);
7249 (void) RegisterMagickInfo(entry);
7250
7251 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007252
cristy3ed852e2009-09-05 21:47:34 +00007253#if defined(MAGICKCORE_PNG_DELEGATE)
7254 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7255 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7256#endif
glennrp47b9dd52010-11-24 18:12:06 +00007257
cristy3ed852e2009-09-05 21:47:34 +00007258 entry->magick=(IsImageFormatHandler *) IsPNG;
7259 entry->adjoin=MagickFalse;
7260 entry->description=ConstantString("Portable Network Graphics");
7261 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007262
cristy3ed852e2009-09-05 21:47:34 +00007263 if (*version != '\0')
7264 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007265
cristy3ed852e2009-09-05 21:47:34 +00007266 entry->note=ConstantString(PNGNote);
7267 (void) RegisterMagickInfo(entry);
7268
7269 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007270
cristy3ed852e2009-09-05 21:47:34 +00007271#if defined(MAGICKCORE_PNG_DELEGATE)
7272 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7273 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7274#endif
glennrp47b9dd52010-11-24 18:12:06 +00007275
cristy3ed852e2009-09-05 21:47:34 +00007276 entry->magick=(IsImageFormatHandler *) IsPNG;
7277 entry->adjoin=MagickFalse;
7278 entry->description=ConstantString(
7279 "8-bit indexed with optional binary transparency");
7280 entry->module=ConstantString("PNG");
7281 (void) RegisterMagickInfo(entry);
7282
7283 entry=SetMagickInfo("PNG24");
7284 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007285
cristy3ed852e2009-09-05 21:47:34 +00007286#if defined(ZLIB_VERSION)
7287 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7288 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007289
cristy3ed852e2009-09-05 21:47:34 +00007290 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7291 {
7292 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7293 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7294 }
7295#endif
glennrp47b9dd52010-11-24 18:12:06 +00007296
cristy3ed852e2009-09-05 21:47:34 +00007297 if (*version != '\0')
7298 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007299
cristy3ed852e2009-09-05 21:47:34 +00007300#if defined(MAGICKCORE_PNG_DELEGATE)
7301 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7302 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7303#endif
glennrp47b9dd52010-11-24 18:12:06 +00007304
cristy3ed852e2009-09-05 21:47:34 +00007305 entry->magick=(IsImageFormatHandler *) IsPNG;
7306 entry->adjoin=MagickFalse;
glennrpfd164d22013-01-26 21:10:22 +00007307 entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
cristy3ed852e2009-09-05 21:47:34 +00007308 entry->module=ConstantString("PNG");
7309 (void) RegisterMagickInfo(entry);
7310
7311 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007312
cristy3ed852e2009-09-05 21:47:34 +00007313#if defined(MAGICKCORE_PNG_DELEGATE)
7314 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7315 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7316#endif
glennrp47b9dd52010-11-24 18:12:06 +00007317
cristy3ed852e2009-09-05 21:47:34 +00007318 entry->magick=(IsImageFormatHandler *) IsPNG;
7319 entry->adjoin=MagickFalse;
7320 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7321 entry->module=ConstantString("PNG");
7322 (void) RegisterMagickInfo(entry);
7323
glennrpfd164d22013-01-26 21:10:22 +00007324 entry=SetMagickInfo("PNG48");
7325
7326#if defined(MAGICKCORE_PNG_DELEGATE)
7327 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7328 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7329#endif
7330
7331 entry->magick=(IsImageFormatHandler *) IsPNG;
7332 entry->adjoin=MagickFalse;
7333 entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7334 entry->module=ConstantString("PNG");
7335 (void) RegisterMagickInfo(entry);
7336
7337 entry=SetMagickInfo("PNG64");
7338
7339#if defined(MAGICKCORE_PNG_DELEGATE)
7340 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7341 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7342#endif
7343
7344 entry->magick=(IsImageFormatHandler *) IsPNG;
7345 entry->adjoin=MagickFalse;
7346 entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7347 entry->module=ConstantString("PNG");
7348 (void) RegisterMagickInfo(entry);
7349
glennrp5830fbc2013-01-27 06:11:45 +00007350 entry=SetMagickInfo("PNG00");
7351
7352#if defined(MAGICKCORE_PNG_DELEGATE)
7353 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7354 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7355#endif
7356
7357 entry->magick=(IsImageFormatHandler *) IsPNG;
7358 entry->adjoin=MagickFalse;
glennrp62708572013-02-15 12:51:48 +00007359 entry->description=ConstantString(
7360 "PNG inheriting bit-depth and color-type from original");
glennrp5830fbc2013-01-27 06:11:45 +00007361 entry->module=ConstantString("PNG");
7362 (void) RegisterMagickInfo(entry);
7363
cristy3ed852e2009-09-05 21:47:34 +00007364 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007365
cristy3ed852e2009-09-05 21:47:34 +00007366#if defined(JNG_SUPPORTED)
7367#if defined(MAGICKCORE_PNG_DELEGATE)
7368 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7369 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7370#endif
7371#endif
glennrp47b9dd52010-11-24 18:12:06 +00007372
cristy3ed852e2009-09-05 21:47:34 +00007373 entry->magick=(IsImageFormatHandler *) IsJNG;
7374 entry->adjoin=MagickFalse;
7375 entry->description=ConstantString("JPEG Network Graphics");
7376 entry->module=ConstantString("PNG");
7377 entry->note=ConstantString(JNGNote);
7378 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007379
glennrpedaa0382012-04-12 14:16:21 +00007380#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007381 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007382#endif
glennrp47b9dd52010-11-24 18:12:06 +00007383
cristy3ed852e2009-09-05 21:47:34 +00007384 return(MagickImageCoderSignature);
7385}
7386
7387/*
7388%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7389% %
7390% %
7391% %
7392% U n r e g i s t e r P N G I m a g e %
7393% %
7394% %
7395% %
7396%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7397%
7398% UnregisterPNGImage() removes format registrations made by the
7399% PNG module from the list of supported formats.
7400%
7401% The format of the UnregisterPNGImage method is:
7402%
7403% UnregisterPNGImage(void)
7404%
7405*/
7406ModuleExport void UnregisterPNGImage(void)
7407{
7408 (void) UnregisterMagickInfo("MNG");
7409 (void) UnregisterMagickInfo("PNG");
7410 (void) UnregisterMagickInfo("PNG8");
7411 (void) UnregisterMagickInfo("PNG24");
7412 (void) UnregisterMagickInfo("PNG32");
glennrpfd164d22013-01-26 21:10:22 +00007413 (void) UnregisterMagickInfo("PNG48");
7414 (void) UnregisterMagickInfo("PNG64");
glennrp5830fbc2013-01-27 06:11:45 +00007415 (void) UnregisterMagickInfo("PNG00");
cristy3ed852e2009-09-05 21:47:34 +00007416 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007417
glennrpedaa0382012-04-12 14:16:21 +00007418#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007419 if (ping_semaphore != (SemaphoreInfo *) NULL)
7420 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007421#endif
7422}
7423
7424#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007425#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007426/*
7427%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7428% %
7429% %
7430% %
7431% W r i t e M N G I m a g e %
7432% %
7433% %
7434% %
7435%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7436%
7437% WriteMNGImage() writes an image in the Portable Network Graphics
7438% Group's "Multiple-image Network Graphics" encoded image format.
7439%
7440% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7441%
7442% The format of the WriteMNGImage method is:
7443%
cristy16ea1392012-03-21 20:38:41 +00007444% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7445% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007446%
7447% A description of each parameter follows.
7448%
7449% o image_info: the image info.
7450%
7451% o image: The image.
7452%
cristy16ea1392012-03-21 20:38:41 +00007453% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007454%
7455% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7456% "To do" under ReadPNGImage):
7457%
cristy3ed852e2009-09-05 21:47:34 +00007458% Preserve all unknown and not-yet-handled known chunks found in input
7459% PNG file and copy them into output PNG files according to the PNG
7460% copying rules.
7461%
7462% Write the iCCP chunk at MNG level when (icc profile length > 0)
7463%
7464% Improve selection of color type (use indexed-colour or indexed-colour
7465% with tRNS when 256 or fewer unique RGBA values are present).
7466%
7467% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7468% This will be complicated if we limit ourselves to generating MNG-LC
7469% files. For now we ignore disposal method 3 and simply overlay the next
7470% image on it.
7471%
7472% Check for identical PLTE's or PLTE/tRNS combinations and use a
7473% global MNG PLTE or PLTE/tRNS combination when appropriate.
7474% [mostly done 15 June 1999 but still need to take care of tRNS]
7475%
7476% Check for identical sRGB and replace with a global sRGB (and remove
7477% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7478% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7479% local gAMA/cHRM with local sRGB if appropriate).
7480%
7481% Check for identical sBIT chunks and write global ones.
7482%
7483% Provide option to skip writing the signature tEXt chunks.
7484%
7485% Use signatures to detect identical objects and reuse the first
7486% instance of such objects instead of writing duplicate objects.
7487%
7488% Use a smaller-than-32k value of compression window size when
7489% appropriate.
7490%
7491% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7492% ancillary text chunks and save profiles.
7493%
7494% Provide an option to force LC files (to ensure exact framing rate)
7495% instead of VLC.
7496%
7497% Provide an option to force VLC files instead of LC, even when offsets
7498% are present. This will involve expanding the embedded images with a
7499% transparent region at the top and/or left.
7500*/
7501
cristy3ed852e2009-09-05 21:47:34 +00007502static void
glennrpcf002022011-01-30 02:38:15 +00007503Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007504 png_info *ping_info, unsigned char *profile_type, unsigned char
7505 *profile_description, unsigned char *profile_data, png_uint_32 length)
7506{
cristy3ed852e2009-09-05 21:47:34 +00007507 png_textp
7508 text;
7509
cristybb503372010-05-27 20:51:26 +00007510 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007511 i;
7512
7513 unsigned char
7514 *sp;
7515
7516 png_charp
7517 dp;
7518
7519 png_uint_32
7520 allocated_length,
7521 description_length;
7522
7523 unsigned char
7524 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007525
cristy3ed852e2009-09-05 21:47:34 +00007526 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7527 return;
7528
7529 if (image_info->verbose)
7530 {
glennrp0fe50b42010-11-16 03:52:51 +00007531 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7532 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007533 }
glennrp0fe50b42010-11-16 03:52:51 +00007534
cristya865ccd2012-07-28 00:33:10 +00007535#if PNG_LIBPNG_VER >= 14000
7536 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7537#else
7538 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7539#endif
cristy3ed852e2009-09-05 21:47:34 +00007540 description_length=(png_uint_32) strlen((const char *) profile_description);
7541 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7542 + description_length);
cristya865ccd2012-07-28 00:33:10 +00007543#if PNG_LIBPNG_VER >= 14000
7544 text[0].text=(png_charp) png_malloc(ping,
7545 (png_alloc_size_t) allocated_length);
7546 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7547#else
7548 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7549 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7550#endif
cristy3ed852e2009-09-05 21:47:34 +00007551 text[0].key[0]='\0';
7552 (void) ConcatenateMagickString(text[0].key,
7553 "Raw profile type ",MaxTextExtent);
7554 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7555 sp=profile_data;
7556 dp=text[0].text;
7557 *dp++='\n';
7558 (void) CopyMagickString(dp,(const char *) profile_description,
7559 allocated_length);
7560 dp+=description_length;
7561 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007562 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007563 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007564 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007565
cristybb503372010-05-27 20:51:26 +00007566 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007567 {
7568 if (i%36 == 0)
7569 *dp++='\n';
7570 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7571 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7572 }
glennrp47b9dd52010-11-24 18:12:06 +00007573
cristy3ed852e2009-09-05 21:47:34 +00007574 *dp++='\n';
7575 *dp='\0';
7576 text[0].text_length=(png_size_t) (dp-text[0].text);
7577 text[0].compression=image_info->compression == NoCompression ||
7578 (image_info->compression == UndefinedCompression &&
7579 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007580
cristy3ed852e2009-09-05 21:47:34 +00007581 if (text[0].text_length <= allocated_length)
7582 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007583
cristy3ed852e2009-09-05 21:47:34 +00007584 png_free(ping,text[0].text);
7585 png_free(ping,text[0].key);
7586 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007587}
7588
glennrpcf002022011-01-30 02:38:15 +00007589static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007590 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007591{
7592 char
7593 *name;
7594
7595 const StringInfo
7596 *profile;
7597
7598 unsigned char
7599 *data;
7600
7601 png_uint_32 length;
7602
7603 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007604
7605 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7606 {
cristy3ed852e2009-09-05 21:47:34 +00007607 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007608
cristy3ed852e2009-09-05 21:47:34 +00007609 if (profile != (const StringInfo *) NULL)
7610 {
7611 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007612 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007613
glennrp47b9dd52010-11-24 18:12:06 +00007614 if (LocaleNCompare(name,string,11) == 0)
7615 {
7616 if (logging != MagickFalse)
7617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7618 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007619
glennrpcf002022011-01-30 02:38:15 +00007620 ping_profile=CloneStringInfo(profile);
7621 data=GetStringInfoDatum(ping_profile),
7622 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007623 data[4]=data[3];
7624 data[3]=data[2];
7625 data[2]=data[1];
7626 data[1]=data[0];
7627 (void) WriteBlobMSBULong(image,length-5); /* data length */
7628 (void) WriteBlob(image,length-1,data+1);
7629 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007630 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007631 }
cristy3ed852e2009-09-05 21:47:34 +00007632 }
glennrp47b9dd52010-11-24 18:12:06 +00007633
cristy3ed852e2009-09-05 21:47:34 +00007634 name=GetNextImageProfile(image);
7635 }
glennrp47b9dd52010-11-24 18:12:06 +00007636
cristy3ed852e2009-09-05 21:47:34 +00007637 return(MagickTrue);
7638}
7639
glennrpb9cfe272010-12-21 15:08:06 +00007640
cristy3ed852e2009-09-05 21:47:34 +00007641/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007642static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +00007643 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007644{
cristy16ea1392012-03-21 20:38:41 +00007645 Image
7646 *image;
7647
7648 ImageInfo
7649 *image_info;
7650
cristy3ed852e2009-09-05 21:47:34 +00007651 char
7652 s[2];
7653
7654 const char
7655 *name,
7656 *property,
7657 *value;
7658
7659 const StringInfo
7660 *profile;
7661
cristy3ed852e2009-09-05 21:47:34 +00007662 int
cristy3ed852e2009-09-05 21:47:34 +00007663 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007664 pass;
7665
glennrpe9c26dc2010-05-30 01:56:35 +00007666 png_byte
7667 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007668
glennrp39992b42010-11-14 00:03:43 +00007669 png_color
7670 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007671
glennrp5af765f2010-03-30 11:12:18 +00007672 png_color_16
7673 ping_background,
7674 ping_trans_color;
7675
cristy3ed852e2009-09-05 21:47:34 +00007676 png_info
7677 *ping_info;
7678
7679 png_struct
7680 *ping;
7681
glennrp5af765f2010-03-30 11:12:18 +00007682 png_uint_32
7683 ping_height,
7684 ping_width;
7685
cristybb503372010-05-27 20:51:26 +00007686 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007687 y;
7688
7689 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007690 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007691 logging,
glennrp58e01762011-01-07 15:28:54 +00007692 matte,
7693
glennrpda8f3a72011-02-27 23:54:12 +00007694 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007695 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007696 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007697 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007698 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007699 ping_have_bKGD,
7700 ping_have_pHYs,
7701 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007702
7703 ping_exclude_bKGD,
7704 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007705 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007706 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007707 ping_exclude_gAMA,
7708 ping_exclude_iCCP,
7709 /* ping_exclude_iTXt, */
7710 ping_exclude_oFFs,
7711 ping_exclude_pHYs,
7712 ping_exclude_sRGB,
7713 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007714 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007715 ping_exclude_vpAg,
7716 ping_exclude_zCCP, /* hex-encoded iCCP */
7717 ping_exclude_zTXt,
7718
glennrp8d3d6e52011-04-19 04:39:51 +00007719 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007720 ping_need_colortype_warning,
7721
glennrp82b3c532011-03-22 19:20:54 +00007722 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007723 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007724 tried_333,
7725 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007726
7727 QuantumInfo
7728 *quantum_info;
7729
cristy16ea1392012-03-21 20:38:41 +00007730 PNGErrorInfo
7731 error_info;
7732
cristybb503372010-05-27 20:51:26 +00007733 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007734 i,
7735 x;
7736
7737 unsigned char
cristy75fc68f2012-10-08 16:26:00 +00007738 *volatile ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007739
glennrp5af765f2010-03-30 11:12:18 +00007740 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007741 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007742 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007743 ping_color_type,
7744 ping_interlace_method,
7745 ping_compression_method,
7746 ping_filter_method,
7747 ping_num_trans;
7748
cristybb503372010-05-27 20:51:26 +00007749 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007750 image_depth,
7751 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007752
cristybb503372010-05-27 20:51:26 +00007753 size_t
cristy3ed852e2009-09-05 21:47:34 +00007754 quality,
7755 rowbytes,
7756 save_image_depth;
7757
glennrpdfd70802010-11-14 01:23:35 +00007758 int
glennrpfd05d622011-02-25 04:10:33 +00007759 j,
glennrpf09bded2011-01-08 01:15:59 +00007760 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007761 number_opaque,
7762 number_semitransparent,
7763 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007764 ping_pHYs_unit_type;
7765
7766 png_uint_32
7767 ping_pHYs_x_resolution,
7768 ping_pHYs_y_resolution;
7769
cristy3ed852e2009-09-05 21:47:34 +00007770 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007771 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007772
cristy16ea1392012-03-21 20:38:41 +00007773 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7774 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7775 if (image_info == (ImageInfo *) NULL)
7776 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007777
glennrp5af765f2010-03-30 11:12:18 +00007778 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007779 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007780 ping_color_type=0,
7781 ping_interlace_method=0,
7782 ping_compression_method=0,
7783 ping_filter_method=0,
7784 ping_num_trans = 0;
7785
7786 ping_background.red = 0;
7787 ping_background.green = 0;
7788 ping_background.blue = 0;
7789 ping_background.gray = 0;
7790 ping_background.index = 0;
7791
7792 ping_trans_color.red=0;
7793 ping_trans_color.green=0;
7794 ping_trans_color.blue=0;
7795 ping_trans_color.gray=0;
7796
glennrpdfd70802010-11-14 01:23:35 +00007797 ping_pHYs_unit_type = 0;
7798 ping_pHYs_x_resolution = 0;
7799 ping_pHYs_y_resolution = 0;
7800
glennrpda8f3a72011-02-27 23:54:12 +00007801 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007802 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007803 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007804 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007805 ping_have_bKGD=MagickFalse;
7806 ping_have_pHYs=MagickFalse;
7807 ping_have_tRNS=MagickFalse;
7808
glennrp0e8ea192010-12-24 18:00:33 +00007809 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7810 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007811 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007812 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007813 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007814 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7815 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7816 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7817 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7818 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7819 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007820 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007821 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7822 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7823 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7824
glennrp8d3d6e52011-04-19 04:39:51 +00007825 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007826 ping_need_colortype_warning = MagickFalse;
7827
cristy0d57eec2011-09-04 22:13:56 +00007828 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7829 * i.e., eliminate the ICC profile and set image->rendering_intent.
7830 * Note that this will not involve any changes to the actual pixels
7831 * but merely passes information to applications that read the resulting
7832 * PNG image.
7833 */
7834 if (ping_exclude_sRGB == MagickFalse)
7835 {
7836 char
7837 *name;
7838
7839 const StringInfo
7840 *profile;
7841
7842 ResetImageProfileIterator(image);
7843 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7844 {
7845 profile=GetImageProfile(image,name);
7846
7847 if (profile != (StringInfo *) NULL)
7848 {
7849 if ((LocaleCompare(name,"ICC") == 0) ||
cristy16ea1392012-03-21 20:38:41 +00007850 (LocaleCompare(name,"ICM") == 0))
7851 {
glennrpee7b4c02011-10-04 01:21:09 +00007852 int
7853 icheck;
7854
7855 /* 0: not a known sRGB profile
7856 * 1: HP-Microsoft sRGB v2
7857 * 2: ICC sRGB v4 perceptual
7858 * 3: ICC sRGB v2 perceptual no black-compensation
7859 */
7860 png_uint_32
7861 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7862 check_len[4] = {0, 3144, 60960, 3052};
7863
7864 png_uint_32
7865 length,
7866 profile_crc;
7867
cristy0d57eec2011-09-04 22:13:56 +00007868 unsigned char
7869 *data;
7870
glennrp29a106e2011-09-06 17:11:42 +00007871 length=(png_uint_32) GetStringInfoLength(profile);
7872
glennrpee7b4c02011-10-04 01:21:09 +00007873 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007874 {
glennrpee7b4c02011-10-04 01:21:09 +00007875 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007876 {
glennrpee7b4c02011-10-04 01:21:09 +00007877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7878 " Got a %lu-byte ICC profile (potentially sRGB)",
7879 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007880
glennrpee7b4c02011-10-04 01:21:09 +00007881 data=GetStringInfoDatum(profile);
7882 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007883
glennrpee7b4c02011-10-04 01:21:09 +00007884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007885 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007886
7887 if (profile_crc == check_crc[icheck])
7888 {
7889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7890 " It is sRGB.");
7891 if (image->rendering_intent==UndefinedIntent)
7892 image->rendering_intent=PerceptualIntent;
7893 break;
7894 }
glennrp29a106e2011-09-06 17:11:42 +00007895 }
glennrp29a106e2011-09-06 17:11:42 +00007896 }
glennrpee7b4c02011-10-04 01:21:09 +00007897 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007899 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007900 (unsigned long) length);
7901 }
cristy0d57eec2011-09-04 22:13:56 +00007902 }
7903 name=GetNextImageProfile(image);
7904 }
7905 }
7906
glennrp8bb3a022010-12-13 20:40:04 +00007907 number_opaque = 0;
7908 number_semitransparent = 0;
7909 number_transparent = 0;
7910
glennrpfd05d622011-02-25 04:10:33 +00007911 if (logging != MagickFalse)
7912 {
7913 if (image->storage_class == UndefinedClass)
7914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7915 " storage_class=UndefinedClass");
7916 if (image->storage_class == DirectClass)
7917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7918 " storage_class=DirectClass");
7919 if (image->storage_class == PseudoClass)
7920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7921 " storage_class=PseudoClass");
7922 }
glennrp28af3712011-04-06 18:07:30 +00007923
glennrp750105b2012-04-25 16:20:45 +00007924 if (image->storage_class == PseudoClass &&
glennrp7e65e932011-08-19 02:31:16 +00007925 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
glennrpfd164d22013-01-26 21:10:22 +00007926 mng_info->write_png48 || mng_info->write_png64 ||
7927 (mng_info->write_png_colortype != 1 &&
7928 mng_info->write_png_colortype != 5)))
glennrp7e65e932011-08-19 02:31:16 +00007929 {
cristy16ea1392012-03-21 20:38:41 +00007930 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007931 image->storage_class = DirectClass;
7932 }
7933
glennrpc6c391a2011-04-27 02:23:56 +00007934 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007935 {
glennrpc6c391a2011-04-27 02:23:56 +00007936 if (image->storage_class != PseudoClass && image->colormap != NULL)
7937 {
7938 /* Free the bogus colormap; it can cause trouble later */
7939 if (logging != MagickFalse)
7940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7941 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007942 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007943 image->colormap=NULL;
7944 }
glennrp28af3712011-04-06 18:07:30 +00007945 }
glennrpbb4f99d2011-05-22 11:13:17 +00007946
cristy3d9f5ba2012-06-26 13:37:31 +00007947 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00007948 (void) TransformImageColorspace(image,sRGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007949
glennrp3241bd02010-12-12 04:36:28 +00007950 /*
7951 Sometimes we get PseudoClass images whose RGB values don't match
7952 the colors in the colormap. This code syncs the RGB values.
7953 */
7954 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristy16ea1392012-03-21 20:38:41 +00007955 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007956
glennrpa6a06632011-01-19 15:15:34 +00007957#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7958 if (image->depth > 8)
7959 {
7960 if (logging != MagickFalse)
7961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7962 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7963
7964 image->depth=8;
7965 }
7966#endif
7967
glennrp8e58efd2011-05-20 12:16:29 +00007968 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007969 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7970 {
cristy16ea1392012-03-21 20:38:41 +00007971 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007972 *r;
7973
glennrp8e58efd2011-05-20 12:16:29 +00007974 if (image->depth > 8)
7975 {
7976#if MAGICKCORE_QUANTUM_DEPTH > 16
7977 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007978 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007979
7980 for (y=0; y < (ssize_t) image->rows; y++)
7981 {
cristy16ea1392012-03-21 20:38:41 +00007982 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007983
cristy16ea1392012-03-21 20:38:41 +00007984 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007985 break;
7986
7987 for (x=0; x < (ssize_t) image->columns; x++)
7988 {
cristy16ea1392012-03-21 20:38:41 +00007989 LBR16PixelRGBA(r);
7990 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007991 }
glennrpbb4f99d2011-05-22 11:13:17 +00007992
glennrp8e58efd2011-05-20 12:16:29 +00007993 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7994 break;
7995 }
7996
7997 if (image->storage_class == PseudoClass && image->colormap != NULL)
7998 {
cristy3e08f112011-05-24 13:19:30 +00007999 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008000 {
glennrp91d99252011-06-25 14:30:13 +00008001 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008002 }
8003 }
8004#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
8005 }
8006
8007 else if (image->depth > 4)
8008 {
8009#if MAGICKCORE_QUANTUM_DEPTH > 8
8010 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00008011 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008012
8013 for (y=0; y < (ssize_t) image->rows; y++)
8014 {
cristy16ea1392012-03-21 20:38:41 +00008015 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008016
cristy16ea1392012-03-21 20:38:41 +00008017 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008018 break;
8019
8020 for (x=0; x < (ssize_t) image->columns; x++)
8021 {
cristy16ea1392012-03-21 20:38:41 +00008022 LBR08PixelRGBA(r);
8023 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008024 }
glennrpbb4f99d2011-05-22 11:13:17 +00008025
glennrp8e58efd2011-05-20 12:16:29 +00008026 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8027 break;
8028 }
8029
8030 if (image->storage_class == PseudoClass && image->colormap != NULL)
8031 {
cristy3e08f112011-05-24 13:19:30 +00008032 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008033 {
glennrp91d99252011-06-25 14:30:13 +00008034 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008035 }
8036 }
8037#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
8038 }
8039 else
8040 if (image->depth > 2)
8041 {
8042 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00008043 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008044
8045 for (y=0; y < (ssize_t) image->rows; y++)
8046 {
cristy16ea1392012-03-21 20:38:41 +00008047 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008048
cristy16ea1392012-03-21 20:38:41 +00008049 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008050 break;
8051
8052 for (x=0; x < (ssize_t) image->columns; x++)
8053 {
cristy16ea1392012-03-21 20:38:41 +00008054 LBR04PixelRGBA(r);
8055 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008056 }
glennrpbb4f99d2011-05-22 11:13:17 +00008057
glennrp8e58efd2011-05-20 12:16:29 +00008058 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8059 break;
8060 }
8061
8062 if (image->storage_class == PseudoClass && image->colormap != NULL)
8063 {
cristy3e08f112011-05-24 13:19:30 +00008064 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008065 {
glennrp91d99252011-06-25 14:30:13 +00008066 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008067 }
8068 }
8069 }
8070
8071 else if (image->depth > 1)
8072 {
8073 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00008074 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008075
8076 for (y=0; y < (ssize_t) image->rows; y++)
8077 {
cristy16ea1392012-03-21 20:38:41 +00008078 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008079
cristy16ea1392012-03-21 20:38:41 +00008080 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008081 break;
8082
8083 for (x=0; x < (ssize_t) image->columns; x++)
8084 {
cristy16ea1392012-03-21 20:38:41 +00008085 LBR02PixelRGBA(r);
8086 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008087 }
glennrpbb4f99d2011-05-22 11:13:17 +00008088
glennrp8e58efd2011-05-20 12:16:29 +00008089 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8090 break;
8091 }
8092
8093 if (image->storage_class == PseudoClass && image->colormap != NULL)
8094 {
cristy3e08f112011-05-24 13:19:30 +00008095 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008096 {
glennrp91d99252011-06-25 14:30:13 +00008097 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008098 }
8099 }
8100 }
8101 else
8102 {
8103 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00008104 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008105
8106 for (y=0; y < (ssize_t) image->rows; y++)
8107 {
cristy16ea1392012-03-21 20:38:41 +00008108 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008109
cristy16ea1392012-03-21 20:38:41 +00008110 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008111 break;
8112
8113 for (x=0; x < (ssize_t) image->columns; x++)
8114 {
cristy16ea1392012-03-21 20:38:41 +00008115 LBR01PixelRGBA(r);
8116 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008117 }
glennrpbb4f99d2011-05-22 11:13:17 +00008118
glennrp8e58efd2011-05-20 12:16:29 +00008119 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8120 break;
8121 }
8122
8123 if (image->storage_class == PseudoClass && image->colormap != NULL)
8124 {
cristy3e08f112011-05-24 13:19:30 +00008125 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008126 {
glennrp91d99252011-06-25 14:30:13 +00008127 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008128 }
8129 }
8130 }
glennrp9d0ea4d2011-04-22 18:35:57 +00008131 }
8132
glennrp67b9c1a2011-04-22 18:47:36 +00008133 /* To do: set to next higher multiple of 8 */
8134 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00008135 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00008136
glennrp2b013e42010-11-24 16:55:50 +00008137#if (MAGICKCORE_QUANTUM_DEPTH > 16)
8138 /* PNG does not handle depths greater than 16 so reduce it even
8139 * if lossy
8140 */
glennrp8e58efd2011-05-20 12:16:29 +00008141 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00008142 image->depth=16;
8143#endif
8144
glennrp3faa9a32011-04-23 14:00:25 +00008145#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00008146 if (image->depth > 8)
8147 {
8148 /* To do: fill low byte properly */
8149 image->depth=16;
8150 }
8151
glennrpc722dd82011-02-24 05:13:21 +00008152 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristy16ea1392012-03-21 20:38:41 +00008153 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00008154 image->depth = 8;
8155#endif
8156
glennrpa8036d62012-11-04 01:46:06 +00008157 if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
8158 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8159 mng_info->write_png_colortype < 4 &&
8160 image->alpha_trait != BlendPixelTrait)))
8161 {
8162 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8163 * are not going to need the result.
8164 */
glennrp2feb1412013-01-22 15:02:16 +00008165 image_colors = (int) image->colors;
8166 number_opaque = (int) image->colors;
glennrpa8036d62012-11-04 01:46:06 +00008167 if (mng_info->write_png_colortype == 1 ||
8168 mng_info->write_png_colortype == 5)
8169 ping_have_color=MagickFalse;
8170 else
8171 ping_have_color=MagickTrue;
8172 ping_have_non_bw=MagickFalse;
8173
8174 if (image->alpha_trait == BlendPixelTrait)
8175 {
8176 number_transparent = 2;
8177 number_semitransparent = 1;
8178 }
8179
8180 else
8181 {
8182 number_transparent = 0;
8183 number_semitransparent = 0;
8184 }
8185 }
8186
8187 else
8188 {
8189 /* BUILD_PALETTE
8190 *
8191 * Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00008192 * we reduce the transparency to binary and run again, then if there
8193 * 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 +00008194 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8195 * palette. Then (To do) we take care of a final reduction that is only
8196 * needed if there are still 256 colors present and one of them has both
8197 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00008198 */
glennrp82b3c532011-03-22 19:20:54 +00008199
glennrp8ca51ad2011-05-12 21:22:32 +00008200 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008201 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00008202 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008203
glennrp8ca51ad2011-05-12 21:22:32 +00008204 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00008205 {
glennrpa8036d62012-11-04 01:46:06 +00008206 /*
glennrpd71e86a2011-02-24 01:28:37 +00008207 * Sometimes we get DirectClass images that have 256 colors or fewer.
8208 * This code will build a colormap.
8209 *
8210 * Also, sometimes we get PseudoClass images with an out-of-date
8211 * colormap. This code will replace the colormap with a new one.
8212 * Sometimes we get PseudoClass images that have more than 256 colors.
8213 * This code will delete the colormap and change the image to
8214 * DirectClass.
8215 *
cristy8a46d822012-08-28 23:32:39 +00008216 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008217 * even though it sometimes contains left-over non-opaque values.
8218 *
8219 * Also we gather some information (number of opaque, transparent,
8220 * and semitransparent pixels, and whether the image has any non-gray
8221 * pixels or only black-and-white pixels) that we might need later.
8222 *
8223 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8224 * we need to check for bogus non-opaque values, at least.
8225 */
glennrp3c218112010-11-27 15:31:26 +00008226
glennrpd71e86a2011-02-24 01:28:37 +00008227 int
8228 n;
glennrp3c218112010-11-27 15:31:26 +00008229
cristy16ea1392012-03-21 20:38:41 +00008230 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008231 opaque[260],
8232 semitransparent[260],
8233 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008234
cristy16ea1392012-03-21 20:38:41 +00008235 register const Quantum
8236 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008237
cristy16ea1392012-03-21 20:38:41 +00008238 register Quantum
8239 *q,
glennrpfd05d622011-02-25 04:10:33 +00008240 *r;
8241
glennrpd71e86a2011-02-24 01:28:37 +00008242 if (logging != MagickFalse)
8243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8244 " Enter BUILD_PALETTE:");
8245
8246 if (logging != MagickFalse)
8247 {
glennrp03812ae2010-12-24 01:31:34 +00008248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008249 " image->columns=%.20g",(double) image->columns);
8250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8251 " image->rows=%.20g",(double) image->rows);
8252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00008253 " image->alpha_trait=%.20g",(double) image->alpha_trait);
glennrpd71e86a2011-02-24 01:28:37 +00008254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8255 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008256
glennrpfd05d622011-02-25 04:10:33 +00008257 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008258 {
8259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008260 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008262 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008263
glennrpd71e86a2011-02-24 01:28:37 +00008264 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008265 {
glennrpd71e86a2011-02-24 01:28:37 +00008266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8267 " %d (%d,%d,%d,%d)",
8268 (int) i,
8269 (int) image->colormap[i].red,
8270 (int) image->colormap[i].green,
8271 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008272 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008273 }
glennrp2cc891a2010-12-24 13:44:32 +00008274
glennrpd71e86a2011-02-24 01:28:37 +00008275 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8276 {
8277 if (i > 255)
8278 {
8279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8280 " %d (%d,%d,%d,%d)",
8281 (int) i,
8282 (int) image->colormap[i].red,
8283 (int) image->colormap[i].green,
8284 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008285 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008286 }
8287 }
glennrp03812ae2010-12-24 01:31:34 +00008288 }
glennrp7ddcc222010-12-11 05:01:05 +00008289
glennrpd71e86a2011-02-24 01:28:37 +00008290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8291 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008292
glennrpd71e86a2011-02-24 01:28:37 +00008293 if (image->colors == 0)
cristy16ea1392012-03-21 20:38:41 +00008294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8295 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008296
glennrp8d3d6e52011-04-19 04:39:51 +00008297 if (ping_preserve_colormap == MagickFalse)
8298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8299 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008300 }
8301
glennrpd71e86a2011-02-24 01:28:37 +00008302 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008303 number_opaque = 0;
8304 number_semitransparent = 0;
8305 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008306
8307 for (y=0; y < (ssize_t) image->rows; y++)
8308 {
8309 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8310
cristy16ea1392012-03-21 20:38:41 +00008311 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008312 break;
8313
8314 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008315 {
cristy8a46d822012-08-28 23:32:39 +00008316 if (image->alpha_trait != BlendPixelTrait ||
cristy16ea1392012-03-21 20:38:41 +00008317 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008318 {
8319 if (number_opaque < 259)
8320 {
8321 if (number_opaque == 0)
8322 {
cristy16ea1392012-03-21 20:38:41 +00008323 GetPixelInfoPixel(image, q, opaque);
8324 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008325 number_opaque=1;
8326 }
glennrp2cc891a2010-12-24 13:44:32 +00008327
glennrpd71e86a2011-02-24 01:28:37 +00008328 for (i=0; i< (ssize_t) number_opaque; i++)
8329 {
cristy16ea1392012-03-21 20:38:41 +00008330 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008331 break;
8332 }
glennrp7ddcc222010-12-11 05:01:05 +00008333
cristy16ea1392012-03-21 20:38:41 +00008334 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008335 {
8336 number_opaque++;
cristy16ea1392012-03-21 20:38:41 +00008337 GetPixelInfoPixel(image, q, opaque+i);
8338 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008339 }
8340 }
8341 }
cristy16ea1392012-03-21 20:38:41 +00008342 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008343 {
8344 if (number_transparent < 259)
8345 {
8346 if (number_transparent == 0)
8347 {
cristy16ea1392012-03-21 20:38:41 +00008348 GetPixelInfoPixel(image, q, transparent);
8349 ping_trans_color.red=(unsigned short)
8350 GetPixelRed(image,q);
8351 ping_trans_color.green=(unsigned short)
8352 GetPixelGreen(image,q);
8353 ping_trans_color.blue=(unsigned short)
8354 GetPixelBlue(image,q);
8355 ping_trans_color.gray=(unsigned short)
cristy972d1c42012-07-14 23:29:14 +00008356 GetPixelGray(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008357 number_transparent = 1;
8358 }
8359
8360 for (i=0; i< (ssize_t) number_transparent; i++)
8361 {
cristy16ea1392012-03-21 20:38:41 +00008362 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008363 break;
8364 }
8365
8366 if (i == (ssize_t) number_transparent &&
8367 number_transparent < 259)
8368 {
8369 number_transparent++;
cristy16ea1392012-03-21 20:38:41 +00008370 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008371 }
8372 }
8373 }
8374 else
8375 {
8376 if (number_semitransparent < 259)
8377 {
8378 if (number_semitransparent == 0)
8379 {
cristy16ea1392012-03-21 20:38:41 +00008380 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008381 number_semitransparent = 1;
8382 }
8383
8384 for (i=0; i< (ssize_t) number_semitransparent; i++)
8385 {
cristy16ea1392012-03-21 20:38:41 +00008386 if (IsPixelEquivalent(image,q, semitransparent+i)
8387 && GetPixelAlpha(image,q) ==
8388 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008389 break;
8390 }
8391
8392 if (i == (ssize_t) number_semitransparent &&
8393 number_semitransparent < 259)
8394 {
8395 number_semitransparent++;
cristy16ea1392012-03-21 20:38:41 +00008396 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008397 }
8398 }
8399 }
cristy16ea1392012-03-21 20:38:41 +00008400 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008401 }
8402 }
8403
cristy4054bfb2011-08-29 23:41:39 +00008404 if (mng_info->write_png8 == MagickFalse &&
8405 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008406 {
8407 /* Add the background color to the palette, if it
8408 * isn't already there.
8409 */
glennrpc6c391a2011-04-27 02:23:56 +00008410 if (logging != MagickFalse)
8411 {
8412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8413 " Check colormap for background (%d,%d,%d)",
8414 (int) image->background_color.red,
8415 (int) image->background_color.green,
8416 (int) image->background_color.blue);
8417 }
glennrpd71e86a2011-02-24 01:28:37 +00008418 for (i=0; i<number_opaque; i++)
8419 {
glennrpca7ad3a2011-04-26 04:44:54 +00008420 if (opaque[i].red == image->background_color.red &&
8421 opaque[i].green == image->background_color.green &&
8422 opaque[i].blue == image->background_color.blue)
8423 break;
glennrpd71e86a2011-02-24 01:28:37 +00008424 }
glennrpd71e86a2011-02-24 01:28:37 +00008425 if (number_opaque < 259 && i == number_opaque)
8426 {
glennrp8e045c82011-04-27 16:40:27 +00008427 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008428 ping_background.index = i;
glennrp388a8c82012-06-27 13:41:51 +00008429 number_opaque++;
glennrpc6c391a2011-04-27 02:23:56 +00008430 if (logging != MagickFalse)
8431 {
8432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8433 " background_color index is %d",(int) i);
8434 }
8435
glennrpd71e86a2011-02-24 01:28:37 +00008436 }
glennrpa080bc32011-03-11 18:03:44 +00008437 else if (logging != MagickFalse)
8438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8439 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008440 }
8441
8442 image_colors=number_opaque+number_transparent+number_semitransparent;
8443
glennrpa080bc32011-03-11 18:03:44 +00008444 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8445 {
8446 /* No room for the background color; remove it. */
8447 number_opaque--;
8448 image_colors--;
8449 }
8450
glennrpd71e86a2011-02-24 01:28:37 +00008451 if (logging != MagickFalse)
8452 {
8453 if (image_colors > 256)
8454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8455 " image has more than 256 colors");
8456
8457 else
8458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8459 " image has %d colors",image_colors);
8460 }
8461
glennrp8d3d6e52011-04-19 04:39:51 +00008462 if (ping_preserve_colormap != MagickFalse)
8463 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008464
glennrpfd05d622011-02-25 04:10:33 +00008465 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008466 {
8467 ping_have_color=MagickFalse;
8468 ping_have_non_bw=MagickFalse;
8469
glennrp98b95772012-11-29 01:32:00 +00008470 if ((IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) ||
8471 (IssRGBColorspace(image->colorspace) != MagickFalse))
glennrp0fa25802012-07-20 14:01:06 +00008472 {
8473 ping_have_color=MagickTrue;
glennrp98b95772012-11-29 01:32:00 +00008474 ping_have_non_bw=MagickTrue;
glennrp0fa25802012-07-20 14:01:06 +00008475 }
8476
glennrpd71e86a2011-02-24 01:28:37 +00008477 if(image_colors > 256)
8478 {
8479 for (y=0; y < (ssize_t) image->rows; y++)
8480 {
8481 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8482
cristy16ea1392012-03-21 20:38:41 +00008483 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008484 break;
8485
glennrpe5e6b802011-07-20 14:44:40 +00008486 s=q;
8487 for (x=0; x < (ssize_t) image->columns; x++)
8488 {
cristy16ea1392012-03-21 20:38:41 +00008489 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8490 GetPixelRed(image,s) != GetPixelBlue(image,s))
glennrpe5e6b802011-07-20 14:44:40 +00008491 {
8492 ping_have_color=MagickTrue;
8493 ping_have_non_bw=MagickTrue;
8494 break;
8495 }
cristy16ea1392012-03-21 20:38:41 +00008496 s+=GetPixelChannels(image);
glennrpe5e6b802011-07-20 14:44:40 +00008497 }
8498
8499 if (ping_have_color != MagickFalse)
8500 break;
8501
glennrpd71e86a2011-02-24 01:28:37 +00008502 /* Worst case is black-and-white; we are looking at every
8503 * pixel twice.
8504 */
8505
glennrpd71e86a2011-02-24 01:28:37 +00008506 if (ping_have_non_bw == MagickFalse)
8507 {
8508 s=q;
8509 for (x=0; x < (ssize_t) image->columns; x++)
8510 {
cristy16ea1392012-03-21 20:38:41 +00008511 if (GetPixelRed(image,s) != 0 &&
8512 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008513 {
8514 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008515 break;
glennrpd71e86a2011-02-24 01:28:37 +00008516 }
cristy16ea1392012-03-21 20:38:41 +00008517 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008518 }
glennrpe5e6b802011-07-20 14:44:40 +00008519 }
glennrpd71e86a2011-02-24 01:28:37 +00008520 }
glennrpbb4f99d2011-05-22 11:13:17 +00008521 }
8522 }
glennrpd71e86a2011-02-24 01:28:37 +00008523
8524 if (image_colors < 257)
8525 {
cristy16ea1392012-03-21 20:38:41 +00008526 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008527 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008528
glennrpd71e86a2011-02-24 01:28:37 +00008529 /*
8530 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008531 */
8532
glennrpd71e86a2011-02-24 01:28:37 +00008533 if (logging != MagickFalse)
8534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8535 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008536
glennrpd71e86a2011-02-24 01:28:37 +00008537 /* Sort palette, transparent first */;
8538
8539 n = 0;
8540
8541 for (i=0; i<number_transparent; i++)
8542 colormap[n++] = transparent[i];
8543
8544 for (i=0; i<number_semitransparent; i++)
8545 colormap[n++] = semitransparent[i];
8546
8547 for (i=0; i<number_opaque; i++)
8548 colormap[n++] = opaque[i];
8549
glennrpc6c391a2011-04-27 02:23:56 +00008550 ping_background.index +=
8551 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008552
glennrpd71e86a2011-02-24 01:28:37 +00008553 /* image_colors < 257; search the colormap instead of the pixels
8554 * to get ping_have_color and ping_have_non_bw
8555 */
8556 for (i=0; i<n; i++)
8557 {
8558 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008559 {
glennrpd71e86a2011-02-24 01:28:37 +00008560 if (colormap[i].red != colormap[i].green ||
8561 colormap[i].red != colormap[i].blue)
8562 {
8563 ping_have_color=MagickTrue;
8564 ping_have_non_bw=MagickTrue;
8565 break;
8566 }
8567 }
8568
8569 if (ping_have_non_bw == MagickFalse)
8570 {
8571 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008572 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008573 }
glennrp8bb3a022010-12-13 20:40:04 +00008574 }
8575
glennrpd71e86a2011-02-24 01:28:37 +00008576 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8577 (number_transparent == 0 && number_semitransparent == 0)) &&
8578 (((mng_info->write_png_colortype-1) ==
8579 PNG_COLOR_TYPE_PALETTE) ||
8580 (mng_info->write_png_colortype == 0)))
8581 {
glennrp6185c532011-01-14 17:58:40 +00008582 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008583 {
glennrpd71e86a2011-02-24 01:28:37 +00008584 if (n != (ssize_t) image_colors)
8585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8586 " image_colors (%d) and n (%d) don't match",
8587 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008588
glennrpd71e86a2011-02-24 01:28:37 +00008589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8590 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008591 }
glennrp03812ae2010-12-24 01:31:34 +00008592
glennrpd71e86a2011-02-24 01:28:37 +00008593 image->colors = image_colors;
8594
cristy16ea1392012-03-21 20:38:41 +00008595 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008596 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008597 ThrowWriterException(ResourceLimitError,
8598 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008599
8600 for (i=0; i< (ssize_t) image_colors; i++)
8601 image->colormap[i] = colormap[i];
8602
8603 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008604 {
glennrpd71e86a2011-02-24 01:28:37 +00008605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8606 " image->colors=%d (%d)",
8607 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008608
glennrpd71e86a2011-02-24 01:28:37 +00008609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8610 " Update the pixel indexes");
8611 }
glennrp6185c532011-01-14 17:58:40 +00008612
glennrpfd05d622011-02-25 04:10:33 +00008613 /* Sync the pixel indices with the new colormap */
8614
glennrpd71e86a2011-02-24 01:28:37 +00008615 for (y=0; y < (ssize_t) image->rows; y++)
8616 {
cristy16ea1392012-03-21 20:38:41 +00008617 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008618
cristy16ea1392012-03-21 20:38:41 +00008619 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008620 break;
glennrp6185c532011-01-14 17:58:40 +00008621
glennrpd71e86a2011-02-24 01:28:37 +00008622 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008623 {
glennrpd71e86a2011-02-24 01:28:37 +00008624 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008625 {
cristy8a46d822012-08-28 23:32:39 +00008626 if ((image->alpha_trait != BlendPixelTrait ||
cristy16ea1392012-03-21 20:38:41 +00008627 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8628 image->colormap[i].red == GetPixelRed(image,q) &&
8629 image->colormap[i].green == GetPixelGreen(image,q) &&
8630 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008631 {
cristy16ea1392012-03-21 20:38:41 +00008632 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008633 break;
glennrp6185c532011-01-14 17:58:40 +00008634 }
glennrp6185c532011-01-14 17:58:40 +00008635 }
cristy16ea1392012-03-21 20:38:41 +00008636 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008637 }
glennrp6185c532011-01-14 17:58:40 +00008638
glennrpd71e86a2011-02-24 01:28:37 +00008639 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8640 break;
8641 }
8642 }
8643 }
8644
8645 if (logging != MagickFalse)
8646 {
8647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8648 " image->colors=%d", (int) image->colors);
8649
8650 if (image->colormap != NULL)
8651 {
8652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008653 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008654
8655 for (i=0; i < (ssize_t) image->colors; i++)
8656 {
cristy72988482011-03-29 16:34:38 +00008657 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008658 {
8659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8660 " %d (%d,%d,%d,%d)",
8661 (int) i,
8662 (int) image->colormap[i].red,
8663 (int) image->colormap[i].green,
8664 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008665 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008666 }
glennrp6185c532011-01-14 17:58:40 +00008667 }
8668 }
glennrp03812ae2010-12-24 01:31:34 +00008669
glennrpd71e86a2011-02-24 01:28:37 +00008670 if (number_transparent < 257)
8671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8672 " number_transparent = %d",
8673 number_transparent);
8674 else
glennrp03812ae2010-12-24 01:31:34 +00008675
glennrpd71e86a2011-02-24 01:28:37 +00008676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8677 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008678
glennrpd71e86a2011-02-24 01:28:37 +00008679 if (number_opaque < 257)
8680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8681 " number_opaque = %d",
8682 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008683
glennrpd71e86a2011-02-24 01:28:37 +00008684 else
8685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8686 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008687
glennrpd71e86a2011-02-24 01:28:37 +00008688 if (number_semitransparent < 257)
8689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8690 " number_semitransparent = %d",
8691 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008692
glennrpd71e86a2011-02-24 01:28:37 +00008693 else
8694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8695 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008696
glennrpd71e86a2011-02-24 01:28:37 +00008697 if (ping_have_non_bw == MagickFalse)
8698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8699 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008700
glennrpd71e86a2011-02-24 01:28:37 +00008701 else if (ping_have_color == MagickFalse)
8702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8703 " All pixels and the background are gray");
8704
8705 else
8706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8707 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008708
glennrp03812ae2010-12-24 01:31:34 +00008709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8710 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008711 }
glennrpfd05d622011-02-25 04:10:33 +00008712
glennrpc8c2f062011-02-25 19:00:33 +00008713 if (mng_info->write_png8 == MagickFalse)
8714 break;
glennrpfd05d622011-02-25 04:10:33 +00008715
glennrpc8c2f062011-02-25 19:00:33 +00008716 /* Make any reductions necessary for the PNG8 format */
8717 if (image_colors <= 256 &&
8718 image_colors != 0 && image->colormap != NULL &&
8719 number_semitransparent == 0 &&
8720 number_transparent <= 1)
8721 break;
8722
8723 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008724 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8725 * transparent color so if more than one is transparent we merge
8726 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008727 */
glennrp130fc452011-08-20 03:43:18 +00008728 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008729 {
8730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8731 " Thresholding the alpha channel to binary");
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);
glennrpc8c2f062011-02-25 19:00:33 +00008736
cristy16ea1392012-03-21 20:38:41 +00008737 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +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/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008743 {
cristy16ea1392012-03-21 20:38:41 +00008744 SetPixelInfoPixel(image,&image->background_color,r);
8745 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008746 }
8747 else
cristy16ea1392012-03-21 20:38:41 +00008748 SetPixelAlpha(image,OpaqueAlpha,r);
8749 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008750 }
glennrpbb4f99d2011-05-22 11:13:17 +00008751
glennrpc8c2f062011-02-25 19:00:33 +00008752 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8753 break;
8754
8755 if (image_colors != 0 && image_colors <= 256 &&
8756 image->colormap != NULL)
8757 for (i=0; i<image_colors; i++)
cristy16ea1392012-03-21 20:38:41 +00008758 image->colormap[i].alpha =
8759 (image->colormap[i].alpha > TransparentAlpha/2 ?
8760 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008761 }
8762 continue;
8763 }
8764
8765 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008766 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8767 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8768 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008769 */
glennrpd3371642011-03-22 19:42:23 +00008770 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8771 {
8772 if (logging != MagickFalse)
8773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8774 " Quantizing the background color to 4-4-4");
8775
8776 tried_444 = MagickTrue;
8777
glennrp91d99252011-06-25 14:30:13 +00008778 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008779
8780 if (logging != MagickFalse)
8781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8782 " Quantizing the pixel colors to 4-4-4");
8783
8784 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);
glennrpd3371642011-03-22 19:42:23 +00008789
cristy16ea1392012-03-21 20:38:41 +00008790 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +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 LBR04PixelRGB(r);
cristy16ea1392012-03-21 20:38:41 +00008797 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00008798 }
glennrpbb4f99d2011-05-22 11:13:17 +00008799
glennrpd3371642011-03-22 19:42:23 +00008800 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8801 break;
8802 }
8803 }
8804
8805 else /* Should not reach this; colormap already exists and
8806 must be <= 256 */
8807 {
8808 if (logging != MagickFalse)
8809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8810 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008811
glennrpd3371642011-03-22 19:42:23 +00008812 for (i=0; i<image_colors; i++)
8813 {
glennrp91d99252011-06-25 14:30:13 +00008814 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008815 }
8816 }
8817 continue;
8818 }
8819
glennrp82b3c532011-03-22 19:20:54 +00008820 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8821 {
8822 if (logging != MagickFalse)
8823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8824 " Quantizing the background color to 3-3-3");
8825
8826 tried_333 = MagickTrue;
8827
glennrp91d99252011-06-25 14:30:13 +00008828 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008829
8830 if (logging != MagickFalse)
8831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008832 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008833
8834 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);
glennrp82b3c532011-03-22 19:20:54 +00008839
cristy16ea1392012-03-21 20:38:41 +00008840 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008841 break;
8842
8843 for (x=0; x < (ssize_t) image->columns; x++)
8844 {
cristy16ea1392012-03-21 20:38:41 +00008845 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8846 LBR03RGB(r);
8847 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00008848 }
glennrpbb4f99d2011-05-22 11:13:17 +00008849
glennrp82b3c532011-03-22 19:20:54 +00008850 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8851 break;
8852 }
8853 }
8854
8855 else /* Should not reach this; colormap already exists and
8856 must be <= 256 */
8857 {
8858 if (logging != MagickFalse)
8859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008860 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008861 for (i=0; i<image_colors; i++)
8862 {
glennrp91d99252011-06-25 14:30:13 +00008863 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008864 }
glennrpd3371642011-03-22 19:42:23 +00008865 }
8866 continue;
glennrp82b3c532011-03-22 19:20:54 +00008867 }
glennrpc8c2f062011-02-25 19:00:33 +00008868
glennrp8ca51ad2011-05-12 21:22:32 +00008869 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008870 {
8871 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008873 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008874
glennrp8ca51ad2011-05-12 21:22:32 +00008875 tried_332 = MagickTrue;
8876
glennrp3faa9a32011-04-23 14:00:25 +00008877 /* Red and green were already done so we only quantize the blue
8878 * channel
8879 */
8880
glennrp91d99252011-06-25 14:30:13 +00008881 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008882
glennrpc8c2f062011-02-25 19:00:33 +00008883 if (logging != MagickFalse)
8884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008885 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008886
glennrpc8c2f062011-02-25 19:00:33 +00008887 if (image->colormap == NULL)
8888 {
8889 for (y=0; y < (ssize_t) image->rows; y++)
8890 {
cristy16ea1392012-03-21 20:38:41 +00008891 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008892
cristy16ea1392012-03-21 20:38:41 +00008893 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008894 break;
8895
8896 for (x=0; x < (ssize_t) image->columns; x++)
8897 {
cristy16ea1392012-03-21 20:38:41 +00008898 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008899 LBR02PixelBlue(r);
cristy16ea1392012-03-21 20:38:41 +00008900 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008901 }
glennrpbb4f99d2011-05-22 11:13:17 +00008902
glennrpc8c2f062011-02-25 19:00:33 +00008903 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8904 break;
8905 }
8906 }
glennrpfd05d622011-02-25 04:10:33 +00008907
glennrpc8c2f062011-02-25 19:00:33 +00008908 else /* Should not reach this; colormap already exists and
8909 must be <= 256 */
8910 {
8911 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008913 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008914 for (i=0; i<image_colors; i++)
8915 {
glennrp91d99252011-06-25 14:30:13 +00008916 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008917 }
8918 }
8919 continue;
8920 }
8921 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008922
8923 if (image_colors == 0 || image_colors > 256)
8924 {
8925 /* Take care of special case with 256 colors + 1 transparent
8926 * color. We don't need to quantize to 2-3-2-1; we only need to
8927 * eliminate one color, so we'll merge the two darkest red
8928 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8929 */
8930 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8931 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8932 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8933 {
8934 image->background_color.red=ScaleCharToQuantum(0x24);
8935 }
glennrpbb4f99d2011-05-22 11:13:17 +00008936
glennrp8ca51ad2011-05-12 21:22:32 +00008937 if (image->colormap == NULL)
8938 {
8939 for (y=0; y < (ssize_t) image->rows; y++)
8940 {
cristy16ea1392012-03-21 20:38:41 +00008941 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008942
cristy16ea1392012-03-21 20:38:41 +00008943 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008944 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008945
glennrp8ca51ad2011-05-12 21:22:32 +00008946 for (x=0; x < (ssize_t) image->columns; x++)
8947 {
cristy16ea1392012-03-21 20:38:41 +00008948 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8949 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8950 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8951 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008952 {
cristy16ea1392012-03-21 20:38:41 +00008953 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008954 }
cristy16ea1392012-03-21 20:38:41 +00008955 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008956 }
glennrpbb4f99d2011-05-22 11:13:17 +00008957
glennrp8ca51ad2011-05-12 21:22:32 +00008958 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8959 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008960
glennrp8ca51ad2011-05-12 21:22:32 +00008961 }
8962 }
8963
8964 else
8965 {
8966 for (i=0; i<image_colors; i++)
8967 {
8968 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8969 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8970 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8971 {
8972 image->colormap[i].red=ScaleCharToQuantum(0x24);
8973 }
8974 }
8975 }
8976 }
glennrpd71e86a2011-02-24 01:28:37 +00008977 }
glennrpa8036d62012-11-04 01:46:06 +00008978 }
glennrpfd05d622011-02-25 04:10:33 +00008979 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008980
glennrpfd05d622011-02-25 04:10:33 +00008981 /* If we are excluding the tRNS chunk and there is transparency,
8982 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8983 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008984 */
glennrp0e8ea192010-12-24 18:00:33 +00008985 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8986 (number_transparent != 0 || number_semitransparent != 0))
8987 {
glennrpd17915c2011-04-29 14:24:22 +00008988 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008989
8990 if (ping_have_color == MagickFalse)
8991 mng_info->write_png_colortype = 5;
8992
8993 else
8994 mng_info->write_png_colortype = 7;
8995
glennrp8d579662011-02-23 02:05:02 +00008996 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008997 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008998 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008999
glennrp0e8ea192010-12-24 18:00:33 +00009000 }
9001
glennrpfd05d622011-02-25 04:10:33 +00009002 /* See if cheap transparency is possible. It is only possible
9003 * when there is a single transparent color, no semitransparent
9004 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00009005 * as the transparent color. We only need this information if
9006 * we are writing a PNG with colortype 0 or 2, and we have not
9007 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00009008 */
glennrp5a39f372011-02-25 04:52:16 +00009009 if (number_transparent == 1 &&
9010 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00009011 {
9012 ping_have_cheap_transparency = MagickTrue;
9013
9014 if (number_semitransparent != 0)
9015 ping_have_cheap_transparency = MagickFalse;
9016
9017 else if (image_colors == 0 || image_colors > 256 ||
9018 image->colormap == NULL)
9019 {
cristy16ea1392012-03-21 20:38:41 +00009020 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00009021 *q;
9022
glennrpfd05d622011-02-25 04:10:33 +00009023 for (y=0; y < (ssize_t) image->rows; y++)
9024 {
9025 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9026
cristy16ea1392012-03-21 20:38:41 +00009027 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00009028 break;
9029
9030 for (x=0; x < (ssize_t) image->columns; x++)
9031 {
cristy16ea1392012-03-21 20:38:41 +00009032 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9033 (unsigned short) GetPixelRed(image,q) ==
9034 ping_trans_color.red &&
9035 (unsigned short) GetPixelGreen(image,q) ==
9036 ping_trans_color.green &&
9037 (unsigned short) GetPixelBlue(image,q) ==
9038 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00009039 {
9040 ping_have_cheap_transparency = MagickFalse;
9041 break;
9042 }
9043
cristy16ea1392012-03-21 20:38:41 +00009044 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00009045 }
glennrpbb4f99d2011-05-22 11:13:17 +00009046
glennrpfd05d622011-02-25 04:10:33 +00009047 if (ping_have_cheap_transparency == MagickFalse)
9048 break;
9049 }
9050 }
9051 else
9052 {
glennrp67b9c1a2011-04-22 18:47:36 +00009053 /* Assuming that image->colormap[0] is the one transparent color
9054 * and that all others are opaque.
9055 */
glennrpfd05d622011-02-25 04:10:33 +00009056 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00009057 for (i=1; i<image_colors; i++)
9058 if (image->colormap[i].red == image->colormap[0].red &&
9059 image->colormap[i].green == image->colormap[0].green &&
9060 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00009061 {
glennrp67b9c1a2011-04-22 18:47:36 +00009062 ping_have_cheap_transparency = MagickFalse;
9063 break;
glennrpfd05d622011-02-25 04:10:33 +00009064 }
9065 }
glennrpbb4f99d2011-05-22 11:13:17 +00009066
glennrpfd05d622011-02-25 04:10:33 +00009067 if (logging != MagickFalse)
9068 {
9069 if (ping_have_cheap_transparency == MagickFalse)
9070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9071 " Cheap transparency is not possible.");
9072
9073 else
9074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9075 " Cheap transparency is possible.");
9076 }
9077 }
9078 else
9079 ping_have_cheap_transparency = MagickFalse;
9080
glennrp8640fb52010-11-23 15:48:26 +00009081 image_depth=image->depth;
9082
glennrp26c990a2010-11-23 02:23:20 +00009083 quantum_info = (QuantumInfo *) NULL;
9084 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00009085 image_colors=(int) image->colors;
cristyb0a657e2012-08-29 00:45:37 +00009086 image_matte=image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse;
glennrp26c990a2010-11-23 02:23:20 +00009087
glennrp0fe50b42010-11-16 03:52:51 +00009088 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00009089 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00009090
glennrp52a479c2011-02-26 21:14:38 +00009091 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9092 (image->colors == 0 || image->colormap == NULL))
9093 {
cristy16ea1392012-03-21 20:38:41 +00009094 image_info=DestroyImageInfo(image_info);
9095 image=DestroyImage(image);
9096 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00009097 "Cannot write PNG8 or color-type 3; colormap is NULL",
cristy16ea1392012-03-21 20:38:41 +00009098 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00009099 return(MagickFalse);
9100 }
9101
cristy3ed852e2009-09-05 21:47:34 +00009102 /*
9103 Allocate the PNG structures
9104 */
9105#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00009106 error_info.image=image;
9107 error_info.exception=exception;
9108 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009109 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9110 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00009111
cristy3ed852e2009-09-05 21:47:34 +00009112#else
cristy16ea1392012-03-21 20:38:41 +00009113 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009114 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00009115
cristy3ed852e2009-09-05 21:47:34 +00009116#endif
9117 if (ping == (png_struct *) NULL)
9118 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009119
cristy3ed852e2009-09-05 21:47:34 +00009120 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00009121
cristy3ed852e2009-09-05 21:47:34 +00009122 if (ping_info == (png_info *) NULL)
9123 {
9124 png_destroy_write_struct(&ping,(png_info **) NULL);
9125 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9126 }
glennrp0fe50b42010-11-16 03:52:51 +00009127
cristy3ed852e2009-09-05 21:47:34 +00009128 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00009129 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00009130
glennrp5af765f2010-03-30 11:12:18 +00009131 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009132 {
9133 /*
9134 PNG write failed.
9135 */
9136#ifdef PNG_DEBUG
9137 if (image_info->verbose)
9138 (void) printf("PNG write has failed.\n");
9139#endif
9140 png_destroy_write_struct(&ping,&ping_info);
glennrpedaa0382012-04-12 14:16:21 +00009141#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00009142 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009143#endif
glennrpedaa0382012-04-12 14:16:21 +00009144
9145 if (ping_pixels != (unsigned char *) NULL)
9146 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9147
9148 if (quantum_info != (QuantumInfo *) NULL)
9149 quantum_info=DestroyQuantumInfo(quantum_info);
9150
cristy16ea1392012-03-21 20:38:41 +00009151 if (ping_have_blob != MagickFalse)
9152 (void) CloseBlob(image);
9153 image_info=DestroyImageInfo(image_info);
9154 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009155 return(MagickFalse);
9156 }
glennrpedaa0382012-04-12 14:16:21 +00009157
9158 /* { For navigation to end of SETJMP-protected block. Within this
9159 * block, use png_error() instead of Throwing an Exception, to ensure
9160 * that libpng is able to clean up, and that the semaphore is unlocked.
9161 */
9162
9163#ifdef PNG_SETJMP_NOT_THREAD_SAFE
9164 LockSemaphoreInfo(ping_semaphore);
9165#endif
9166
cristy3ed852e2009-09-05 21:47:34 +00009167 /*
9168 Prepare PNG for writing.
9169 */
glennrp9bf97b62012-06-06 21:03:14 +00009170
cristy3ed852e2009-09-05 21:47:34 +00009171#if defined(PNG_MNG_FEATURES_SUPPORTED)
9172 if (mng_info->write_mng)
glennrp25024a62012-06-07 11:38:34 +00009173 {
cristy3ed852e2009-09-05 21:47:34 +00009174 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp25024a62012-06-07 11:38:34 +00009175# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9176 /* Disable new libpng-1.5.10 feature when writing a MNG because
9177 * zero-length PLTE is OK
9178 */
9179 png_set_check_for_invalid_index (ping, 0);
9180# endif
9181 }
glennrp2b013e42010-11-24 16:55:50 +00009182
cristy3ed852e2009-09-05 21:47:34 +00009183#else
9184# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9185 if (mng_info->write_mng)
9186 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00009187
cristy3ed852e2009-09-05 21:47:34 +00009188# endif
9189#endif
glennrp2b013e42010-11-24 16:55:50 +00009190
cristy3ed852e2009-09-05 21:47:34 +00009191 x=0;
glennrp2b013e42010-11-24 16:55:50 +00009192
cristy4e5bc842010-06-09 13:56:01 +00009193 ping_width=(png_uint_32) image->columns;
9194 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00009195
cristy3ed852e2009-09-05 21:47:34 +00009196 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9197 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009198
glennrpfd164d22013-01-26 21:10:22 +00009199 if (mng_info->write_png48 || mng_info->write_png64)
9200 image_depth=16;
9201
cristy3ed852e2009-09-05 21:47:34 +00009202 if (mng_info->write_png_depth != 0)
9203 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009204
cristy3ed852e2009-09-05 21:47:34 +00009205 /* Adjust requested depth to next higher valid depth if necessary */
9206 if (image_depth > 8)
9207 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009208
cristy3ed852e2009-09-05 21:47:34 +00009209 if ((image_depth > 4) && (image_depth < 8))
9210 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009211
cristy3ed852e2009-09-05 21:47:34 +00009212 if (image_depth == 3)
9213 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00009214
cristy3ed852e2009-09-05 21:47:34 +00009215 if (logging != MagickFalse)
9216 {
9217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009218 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00009219 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009220 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00009221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy8a46d822012-08-28 23:32:39 +00009222 " image_matte=%.20g",(double) image->alpha_trait);
cristy3ed852e2009-09-05 21:47:34 +00009223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009224 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00009225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009226 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00009227 }
glennrp8640fb52010-11-23 15:48:26 +00009228
cristy3ed852e2009-09-05 21:47:34 +00009229 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00009230 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00009231
glennrp26f37912010-12-23 16:22:42 +00009232
cristy3ed852e2009-09-05 21:47:34 +00009233#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00009234 if (ping_exclude_pHYs == MagickFalse)
9235 {
cristy16ea1392012-03-21 20:38:41 +00009236 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00009237 (!mng_info->write_mng || !mng_info->equal_physs))
9238 {
glennrp0fe50b42010-11-16 03:52:51 +00009239 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9241 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00009242
9243 if (image->units == PixelsPerInchResolution)
9244 {
glennrpdfd70802010-11-14 01:23:35 +00009245 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009246 ping_pHYs_x_resolution=
cristy16ea1392012-03-21 20:38:41 +00009247 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00009248 ping_pHYs_y_resolution=
cristy16ea1392012-03-21 20:38:41 +00009249 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009250 }
glennrpdfd70802010-11-14 01:23:35 +00009251
cristy3ed852e2009-09-05 21:47:34 +00009252 else if (image->units == PixelsPerCentimeterResolution)
9253 {
glennrpdfd70802010-11-14 01:23:35 +00009254 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy16ea1392012-03-21 20:38:41 +00009255 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9256 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009257 }
glennrp991d11d2010-11-12 21:55:28 +00009258
cristy3ed852e2009-09-05 21:47:34 +00009259 else
9260 {
glennrpdfd70802010-11-14 01:23:35 +00009261 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy16ea1392012-03-21 20:38:41 +00009262 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9263 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009264 }
glennrp991d11d2010-11-12 21:55:28 +00009265
glennrp823b55c2011-03-14 18:46:46 +00009266 if (logging != MagickFalse)
9267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9268 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9269 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9270 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009271 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009272 }
glennrp26f37912010-12-23 16:22:42 +00009273 }
cristy3ed852e2009-09-05 21:47:34 +00009274#endif
glennrpa521b2f2010-10-29 04:11:03 +00009275
glennrp26f37912010-12-23 16:22:42 +00009276 if (ping_exclude_bKGD == MagickFalse)
9277 {
glennrpa521b2f2010-10-29 04:11:03 +00009278 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009279 {
glennrpa521b2f2010-10-29 04:11:03 +00009280 unsigned int
9281 mask;
cristy3ed852e2009-09-05 21:47:34 +00009282
glennrpa521b2f2010-10-29 04:11:03 +00009283 mask=0xffff;
9284 if (ping_bit_depth == 8)
9285 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009286
glennrpa521b2f2010-10-29 04:11:03 +00009287 if (ping_bit_depth == 4)
9288 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009289
glennrpa521b2f2010-10-29 04:11:03 +00009290 if (ping_bit_depth == 2)
9291 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009292
glennrpa521b2f2010-10-29 04:11:03 +00009293 if (ping_bit_depth == 1)
9294 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009295
glennrpa521b2f2010-10-29 04:11:03 +00009296 ping_background.red=(png_uint_16)
9297 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009298
glennrpa521b2f2010-10-29 04:11:03 +00009299 ping_background.green=(png_uint_16)
9300 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009301
glennrpa521b2f2010-10-29 04:11:03 +00009302 ping_background.blue=(png_uint_16)
9303 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009304
9305 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009306 }
cristy3ed852e2009-09-05 21:47:34 +00009307
glennrp0fe50b42010-11-16 03:52:51 +00009308 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009309 {
9310 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9311 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9313 " background_color index is %d",
9314 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009315
9316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9317 " ping_bit_depth=%d",ping_bit_depth);
9318 }
glennrp0fe50b42010-11-16 03:52:51 +00009319
9320 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009321 }
glennrp0fe50b42010-11-16 03:52:51 +00009322
cristy3ed852e2009-09-05 21:47:34 +00009323 /*
9324 Select the color type.
9325 */
9326 matte=image_matte;
9327 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009328
glennrp1273f7b2011-02-24 03:20:30 +00009329 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009330 {
glennrp0fe50b42010-11-16 03:52:51 +00009331
glennrpfd05d622011-02-25 04:10:33 +00009332 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009333 for reducing the sample depth from 8. */
9334
glennrp0fe50b42010-11-16 03:52:51 +00009335 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009336
glennrp8bb3a022010-12-13 20:40:04 +00009337 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009338
9339 /*
9340 Set image palette.
9341 */
9342 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9343
glennrp0fe50b42010-11-16 03:52:51 +00009344 if (logging != MagickFalse)
9345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9346 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009347 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009348
9349 for (i=0; i < (ssize_t) number_colors; i++)
9350 {
9351 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9352 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9353 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9354 if (logging != MagickFalse)
9355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009356#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009357 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009358#else
9359 " %5ld (%5d,%5d,%5d)",
9360#endif
glennrp0fe50b42010-11-16 03:52:51 +00009361 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9362
9363 }
glennrp2b013e42010-11-24 16:55:50 +00009364
glennrp8bb3a022010-12-13 20:40:04 +00009365 ping_have_PLTE=MagickTrue;
9366 image_depth=ping_bit_depth;
9367 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009368
glennrp58e01762011-01-07 15:28:54 +00009369 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009370 {
glennrp0fe50b42010-11-16 03:52:51 +00009371 /*
9372 Identify which colormap entry is transparent.
9373 */
9374 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009375 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009376
glennrp8bb3a022010-12-13 20:40:04 +00009377 for (i=0; i < (ssize_t) number_transparent; i++)
9378 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009379
glennrp0fe50b42010-11-16 03:52:51 +00009380
glennrp2cc891a2010-12-24 13:44:32 +00009381 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009382 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009383
9384 if (ping_num_trans == 0)
9385 ping_have_tRNS=MagickFalse;
9386
glennrp8bb3a022010-12-13 20:40:04 +00009387 else
9388 ping_have_tRNS=MagickTrue;
9389 }
glennrp0fe50b42010-11-16 03:52:51 +00009390
glennrp1273f7b2011-02-24 03:20:30 +00009391 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009392 {
glennrp1273f7b2011-02-24 03:20:30 +00009393 /*
9394 * Identify which colormap entry is the background color.
9395 */
9396
glennrp4f25bd02011-01-01 18:51:28 +00009397 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9398 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9399 break;
glennrp0fe50b42010-11-16 03:52:51 +00009400
glennrp4f25bd02011-01-01 18:51:28 +00009401 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009402
9403 if (logging != MagickFalse)
9404 {
9405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9406 " background_color index is %d",
9407 (int) ping_background.index);
9408 }
glennrp4f25bd02011-01-01 18:51:28 +00009409 }
cristy3ed852e2009-09-05 21:47:34 +00009410 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009411
glennrpfd164d22013-01-26 21:10:22 +00009412 else if (mng_info->write_png_colortype == 1)
9413 {
9414 image_matte=MagickFalse;
9415 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9416 }
9417
9418 else if (mng_info->write_png24 || mng_info->write_png48 ||
9419 mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009420 {
9421 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009422 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009423 }
glennrp0fe50b42010-11-16 03:52:51 +00009424
glennrpfd164d22013-01-26 21:10:22 +00009425 else if (mng_info->write_png32 || mng_info->write_png64 ||
9426 mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009427 {
9428 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009429 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009430 }
glennrp0fe50b42010-11-16 03:52:51 +00009431
glennrp8bb3a022010-12-13 20:40:04 +00009432 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009433 {
glennrp5af765f2010-03-30 11:12:18 +00009434 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009435
glennrp8bb3a022010-12-13 20:40:04 +00009436 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009437 {
glennrp5af765f2010-03-30 11:12:18 +00009438 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009439
glennrp5af765f2010-03-30 11:12:18 +00009440 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9441 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009442 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009443
glennrp8bb3a022010-12-13 20:40:04 +00009444 else
9445 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009446
9447 if (logging != MagickFalse)
9448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9449 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009450 }
glennrp0fe50b42010-11-16 03:52:51 +00009451
glennrp7c4c9e62011-03-21 20:23:32 +00009452 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009453 {
9454 if (logging != MagickFalse)
9455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009456 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009457
glennrpd6bf1612010-12-17 17:28:54 +00009458 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009459 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009460
glennrpd6bf1612010-12-17 17:28:54 +00009461 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009462 {
glennrp5af765f2010-03-30 11:12:18 +00009463 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009464 image_matte=MagickFalse;
9465 }
glennrp0fe50b42010-11-16 03:52:51 +00009466
glennrpd6bf1612010-12-17 17:28:54 +00009467 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009468 {
glennrp5af765f2010-03-30 11:12:18 +00009469 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009470 image_matte=MagickTrue;
9471 }
glennrp0fe50b42010-11-16 03:52:51 +00009472
glennrp5aa37f62011-01-02 03:07:57 +00009473 if (image_info->type == PaletteType ||
9474 image_info->type == PaletteMatteType)
9475 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9476
glennrp7c4c9e62011-03-21 20:23:32 +00009477 if (mng_info->write_png_colortype == 0 &&
9478 (image_info->type == UndefinedType ||
9479 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009480 {
glennrp5aa37f62011-01-02 03:07:57 +00009481 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009482 {
glennrp5aa37f62011-01-02 03:07:57 +00009483 if (image_matte == MagickFalse)
9484 {
9485 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9486 image_matte=MagickFalse;
9487 }
glennrp0fe50b42010-11-16 03:52:51 +00009488
glennrp0b206f52011-01-07 04:55:32 +00009489 else
glennrp5aa37f62011-01-02 03:07:57 +00009490 {
9491 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9492 image_matte=MagickTrue;
9493 }
9494 }
9495 else
glennrp8bb3a022010-12-13 20:40:04 +00009496 {
glennrp5aa37f62011-01-02 03:07:57 +00009497 if (image_matte == MagickFalse)
9498 {
9499 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9500 image_matte=MagickFalse;
9501 }
glennrp8bb3a022010-12-13 20:40:04 +00009502
glennrp0b206f52011-01-07 04:55:32 +00009503 else
glennrp5aa37f62011-01-02 03:07:57 +00009504 {
9505 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9506 image_matte=MagickTrue;
9507 }
9508 }
glennrp0fe50b42010-11-16 03:52:51 +00009509 }
glennrp5aa37f62011-01-02 03:07:57 +00009510
cristy3ed852e2009-09-05 21:47:34 +00009511 }
glennrp0fe50b42010-11-16 03:52:51 +00009512
cristy3ed852e2009-09-05 21:47:34 +00009513 if (logging != MagickFalse)
9514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009515 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009516
glennrp5af765f2010-03-30 11:12:18 +00009517 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009518 {
9519 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9520 ping_color_type == PNG_COLOR_TYPE_RGB ||
9521 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9522 ping_bit_depth=8;
9523 }
cristy3ed852e2009-09-05 21:47:34 +00009524
glennrpd6bf1612010-12-17 17:28:54 +00009525 old_bit_depth=ping_bit_depth;
9526
glennrp5af765f2010-03-30 11:12:18 +00009527 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009528 {
cristy8a46d822012-08-28 23:32:39 +00009529 if (image->alpha_trait != BlendPixelTrait && ping_have_non_bw == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00009530 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009531 }
glennrp8640fb52010-11-23 15:48:26 +00009532
glennrp5af765f2010-03-30 11:12:18 +00009533 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009534 {
cristy35ef8242010-06-03 16:24:13 +00009535 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009536 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009537
9538 if (image->colors == 0)
9539 {
glennrp0fe50b42010-11-16 03:52:51 +00009540 /* DO SOMETHING */
glennrpedaa0382012-04-12 14:16:21 +00009541 png_error(ping,"image has 0 colors");
glennrp0f111982010-07-07 20:18:33 +00009542 }
9543
cristy35ef8242010-06-03 16:24:13 +00009544 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009545 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009546 }
glennrp2b013e42010-11-24 16:55:50 +00009547
glennrpd6bf1612010-12-17 17:28:54 +00009548 if (logging != MagickFalse)
9549 {
9550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9551 " Number of colors: %.20g",(double) image_colors);
9552
9553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9554 " Tentative PNG bit depth: %d",ping_bit_depth);
9555 }
9556
9557 if (ping_bit_depth < (int) mng_info->write_png_depth)
9558 ping_bit_depth = mng_info->write_png_depth;
9559 }
glennrp2cc891a2010-12-24 13:44:32 +00009560
glennrp5af765f2010-03-30 11:12:18 +00009561 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009562
cristy3ed852e2009-09-05 21:47:34 +00009563 if (logging != MagickFalse)
9564 {
9565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +00009566 " Tentative PNG color type: %s (%.20g)",
9567 PngColorTypeToString(ping_color_type),
9568 (double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009569
cristy3ed852e2009-09-05 21:47:34 +00009570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009571 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009572
cristy3ed852e2009-09-05 21:47:34 +00009573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009574 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009575
cristy3ed852e2009-09-05 21:47:34 +00009576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009577
glennrp8640fb52010-11-23 15:48:26 +00009578 " image->depth: %.20g",(double) image->depth);
9579
9580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009581 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009582 }
9583
glennrp58e01762011-01-07 15:28:54 +00009584 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009585 {
glennrp4f25bd02011-01-01 18:51:28 +00009586 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009587 {
glennrp7c4c9e62011-03-21 20:23:32 +00009588 if (mng_info->write_png_colortype == 0)
9589 {
9590 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009591
glennrp7c4c9e62011-03-21 20:23:32 +00009592 if (ping_have_color != MagickFalse)
9593 ping_color_type=PNG_COLOR_TYPE_RGBA;
9594 }
glennrp4f25bd02011-01-01 18:51:28 +00009595
9596 /*
9597 * Determine if there is any transparent color.
9598 */
9599 if (number_transparent + number_semitransparent == 0)
9600 {
9601 /*
9602 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9603 */
glennrpa6a06632011-01-19 15:15:34 +00009604
glennrp4f25bd02011-01-01 18:51:28 +00009605 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009606
9607 if (mng_info->write_png_colortype == 0)
9608 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009609 }
9610
9611 else
9612 {
9613 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009614 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009615
9616 mask=0xffff;
9617
9618 if (ping_bit_depth == 8)
9619 mask=0x00ff;
9620
9621 if (ping_bit_depth == 4)
9622 mask=0x000f;
9623
9624 if (ping_bit_depth == 2)
9625 mask=0x0003;
9626
9627 if (ping_bit_depth == 1)
9628 mask=0x0001;
9629
9630 ping_trans_color.red=(png_uint_16)
9631 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9632
9633 ping_trans_color.green=(png_uint_16)
9634 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9635
9636 ping_trans_color.blue=(png_uint_16)
9637 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9638
9639 ping_trans_color.gray=(png_uint_16)
cristy16ea1392012-03-21 20:38:41 +00009640 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009641 image->colormap)) & mask);
9642
9643 ping_trans_color.index=(png_byte) 0;
9644
9645 ping_have_tRNS=MagickTrue;
9646 }
9647
9648 if (ping_have_tRNS != MagickFalse)
9649 {
9650 /*
glennrpfd05d622011-02-25 04:10:33 +00009651 * Determine if there is one and only one transparent color
9652 * and if so if it is fully transparent.
9653 */
9654 if (ping_have_cheap_transparency == MagickFalse)
9655 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009656 }
9657
9658 if (ping_have_tRNS != MagickFalse)
9659 {
glennrp7c4c9e62011-03-21 20:23:32 +00009660 if (mng_info->write_png_colortype == 0)
9661 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009662
9663 if (image_depth == 8)
9664 {
9665 ping_trans_color.red&=0xff;
9666 ping_trans_color.green&=0xff;
9667 ping_trans_color.blue&=0xff;
9668 ping_trans_color.gray&=0xff;
9669 }
9670 }
9671 }
cristy3ed852e2009-09-05 21:47:34 +00009672 else
9673 {
cristy3ed852e2009-09-05 21:47:34 +00009674 if (image_depth == 8)
9675 {
glennrp5af765f2010-03-30 11:12:18 +00009676 ping_trans_color.red&=0xff;
9677 ping_trans_color.green&=0xff;
9678 ping_trans_color.blue&=0xff;
9679 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009680 }
9681 }
9682 }
glennrp8640fb52010-11-23 15:48:26 +00009683
cristy3ed852e2009-09-05 21:47:34 +00009684 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009685
glennrp2e09f552010-11-14 00:38:48 +00009686 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009687 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009688
glennrp39992b42010-11-14 00:03:43 +00009689 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009690 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009691 ping_have_color == MagickFalse &&
9692 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009693 {
cristy35ef8242010-06-03 16:24:13 +00009694 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009695
cristy3ed852e2009-09-05 21:47:34 +00009696 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009697 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009698
glennrp7c4c9e62011-03-21 20:23:32 +00009699 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009700 {
glennrp5af765f2010-03-30 11:12:18 +00009701 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009702
cristy3ed852e2009-09-05 21:47:34 +00009703 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009704 {
9705 if (logging != MagickFalse)
9706 {
9707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9708 " Scaling ping_trans_color (0)");
9709 }
9710 ping_trans_color.gray*=0x0101;
9711 }
cristy3ed852e2009-09-05 21:47:34 +00009712 }
glennrp0fe50b42010-11-16 03:52:51 +00009713
cristy3ed852e2009-09-05 21:47:34 +00009714 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9715 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009716
glennrp136ee3a2011-04-27 15:47:45 +00009717 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009718 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009719 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009720
cristy3ed852e2009-09-05 21:47:34 +00009721 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009722 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009723
cristy3ed852e2009-09-05 21:47:34 +00009724 else
9725 {
glennrp5af765f2010-03-30 11:12:18 +00009726 ping_bit_depth=8;
9727 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009728 {
9729 if(!mng_info->write_png_depth)
9730 {
glennrp5af765f2010-03-30 11:12:18 +00009731 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009732
cristy35ef8242010-06-03 16:24:13 +00009733 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009734 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009735 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009736 }
9737 }
glennrp2b013e42010-11-24 16:55:50 +00009738
glennrp0fe50b42010-11-16 03:52:51 +00009739 else if (ping_color_type ==
9740 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009741 mng_info->IsPalette)
9742 {
cristy3ed852e2009-09-05 21:47:34 +00009743 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009744
cristy3ed852e2009-09-05 21:47:34 +00009745 int
9746 depth_4_ok=MagickTrue,
9747 depth_2_ok=MagickTrue,
9748 depth_1_ok=MagickTrue;
9749
cristybb503372010-05-27 20:51:26 +00009750 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009751 {
9752 unsigned char
9753 intensity;
9754
9755 intensity=ScaleQuantumToChar(image->colormap[i].red);
9756
9757 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9758 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9759 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9760 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009761 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009762 depth_1_ok=MagickFalse;
9763 }
glennrp2b013e42010-11-24 16:55:50 +00009764
cristy3ed852e2009-09-05 21:47:34 +00009765 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009766 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009767
cristy3ed852e2009-09-05 21:47:34 +00009768 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009769 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009770
cristy3ed852e2009-09-05 21:47:34 +00009771 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009772 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009773 }
9774 }
glennrp2b013e42010-11-24 16:55:50 +00009775
glennrp5af765f2010-03-30 11:12:18 +00009776 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009777 }
glennrp0fe50b42010-11-16 03:52:51 +00009778
cristy3ed852e2009-09-05 21:47:34 +00009779 else
glennrp0fe50b42010-11-16 03:52:51 +00009780
cristy3ed852e2009-09-05 21:47:34 +00009781 if (mng_info->IsPalette)
9782 {
glennrp17a14852010-05-10 03:01:59 +00009783 number_colors=image_colors;
9784
cristy3ed852e2009-09-05 21:47:34 +00009785 if (image_depth <= 8)
9786 {
cristy3ed852e2009-09-05 21:47:34 +00009787 /*
9788 Set image palette.
9789 */
glennrp5af765f2010-03-30 11:12:18 +00009790 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009791
glennrp58e01762011-01-07 15:28:54 +00009792 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009793 {
glennrp9c1eb072010-06-06 22:19:15 +00009794 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009795
glennrp3b51f0e2010-11-27 18:14:08 +00009796 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9798 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009799 }
glennrp0fe50b42010-11-16 03:52:51 +00009800
cristy3ed852e2009-09-05 21:47:34 +00009801 else
9802 {
cristybb503372010-05-27 20:51:26 +00009803 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009804 {
9805 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9806 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9807 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9808 }
glennrp0fe50b42010-11-16 03:52:51 +00009809
glennrp3b51f0e2010-11-27 18:14:08 +00009810 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009812 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009813 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009814
glennrp39992b42010-11-14 00:03:43 +00009815 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009816 }
glennrp0fe50b42010-11-16 03:52:51 +00009817
cristy3ed852e2009-09-05 21:47:34 +00009818 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009819 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009820 {
cristybefe4d22010-06-07 01:18:58 +00009821 size_t
9822 one;
9823
glennrp5af765f2010-03-30 11:12:18 +00009824 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009825 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009826
cristy16ea1392012-03-21 20:38:41 +00009827 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009828 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009829 }
glennrp0fe50b42010-11-16 03:52:51 +00009830
glennrp5af765f2010-03-30 11:12:18 +00009831 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009832
glennrp58e01762011-01-07 15:28:54 +00009833 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009834 {
glennrp0fe50b42010-11-16 03:52:51 +00009835 /*
glennrpd6bf1612010-12-17 17:28:54 +00009836 * Set up trans_colors array.
9837 */
glennrp0fe50b42010-11-16 03:52:51 +00009838 assert(number_colors <= 256);
9839
glennrpd6bf1612010-12-17 17:28:54 +00009840 ping_num_trans=(unsigned short) (number_transparent +
9841 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009842
9843 if (ping_num_trans == 0)
9844 ping_have_tRNS=MagickFalse;
9845
glennrpd6bf1612010-12-17 17:28:54 +00009846 else
glennrp0fe50b42010-11-16 03:52:51 +00009847 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009848 if (logging != MagickFalse)
9849 {
9850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9851 " Scaling ping_trans_color (1)");
9852 }
glennrpd6bf1612010-12-17 17:28:54 +00009853 ping_have_tRNS=MagickTrue;
9854
9855 for (i=0; i < ping_num_trans; i++)
9856 {
glennrp750105b2012-04-25 16:20:45 +00009857 ping_trans_alpha[i]= (png_byte)
cristy16ea1392012-03-21 20:38:41 +00009858 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009859 }
glennrp0fe50b42010-11-16 03:52:51 +00009860 }
9861 }
cristy3ed852e2009-09-05 21:47:34 +00009862 }
9863 }
glennrp0fe50b42010-11-16 03:52:51 +00009864
cristy3ed852e2009-09-05 21:47:34 +00009865 else
9866 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009867
cristy3ed852e2009-09-05 21:47:34 +00009868 if (image_depth < 8)
9869 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009870
cristy3ed852e2009-09-05 21:47:34 +00009871 if ((save_image_depth == 16) && (image_depth == 8))
9872 {
glennrp4f25bd02011-01-01 18:51:28 +00009873 if (logging != MagickFalse)
9874 {
9875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9876 " Scaling ping_trans_color from (%d,%d,%d)",
9877 (int) ping_trans_color.red,
9878 (int) ping_trans_color.green,
9879 (int) ping_trans_color.blue);
9880 }
9881
glennrp5af765f2010-03-30 11:12:18 +00009882 ping_trans_color.red*=0x0101;
9883 ping_trans_color.green*=0x0101;
9884 ping_trans_color.blue*=0x0101;
9885 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009886
9887 if (logging != MagickFalse)
9888 {
9889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9890 " to (%d,%d,%d)",
9891 (int) ping_trans_color.red,
9892 (int) ping_trans_color.green,
9893 (int) ping_trans_color.blue);
9894 }
cristy3ed852e2009-09-05 21:47:34 +00009895 }
9896 }
9897
cristy4383ec82011-01-05 15:42:32 +00009898 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9899 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009900
cristy3ed852e2009-09-05 21:47:34 +00009901 /*
9902 Adjust background and transparency samples in sub-8-bit grayscale files.
9903 */
glennrp5af765f2010-03-30 11:12:18 +00009904 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009905 PNG_COLOR_TYPE_GRAY)
9906 {
9907 png_uint_16
9908 maxval;
9909
cristy35ef8242010-06-03 16:24:13 +00009910 size_t
9911 one=1;
9912
cristy22ffd972010-06-03 16:51:47 +00009913 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009914
glennrp4f25bd02011-01-01 18:51:28 +00009915 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009916 {
cristy3ed852e2009-09-05 21:47:34 +00009917
cristy16ea1392012-03-21 20:38:41 +00009918 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9919 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9920 &image->background_color))) +.5)));
9921
cristy3ed852e2009-09-05 21:47:34 +00009922 if (logging != MagickFalse)
9923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00009924 " Setting up bKGD chunk (2)");
9925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9926 " background_color index is %d",
9927 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009928
glennrp991d11d2010-11-12 21:55:28 +00009929 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009930 }
cristy3ed852e2009-09-05 21:47:34 +00009931
glennrp3e3e20f2011-06-09 04:21:43 +00009932 if (logging != MagickFalse)
9933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9934 " Scaling ping_trans_color.gray from %d",
9935 (int)ping_trans_color.gray);
9936
glennrp9be9b1c2011-06-09 12:21:45 +00009937 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009938 ping_trans_color.gray)+.5);
9939
9940 if (logging != MagickFalse)
9941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9942 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009943 }
glennrp17a14852010-05-10 03:01:59 +00009944
glennrp26f37912010-12-23 16:22:42 +00009945 if (ping_exclude_bKGD == MagickFalse)
9946 {
glennrp1273f7b2011-02-24 03:20:30 +00009947 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009948 {
9949 /*
9950 Identify which colormap entry is the background color.
9951 */
9952
glennrp17a14852010-05-10 03:01:59 +00009953 number_colors=image_colors;
9954
glennrpa521b2f2010-10-29 04:11:03 +00009955 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9956 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009957 break;
9958
9959 ping_background.index=(png_byte) i;
9960
glennrp3b51f0e2010-11-27 18:14:08 +00009961 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009962 {
9963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009964 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009965 }
glennrp0fe50b42010-11-16 03:52:51 +00009966
cristy13d07042010-11-21 20:56:18 +00009967 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009968 {
9969 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009970
9971 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009972 {
9973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9974 " background =(%d,%d,%d)",
9975 (int) ping_background.red,
9976 (int) ping_background.green,
9977 (int) ping_background.blue);
9978 }
9979 }
glennrpa521b2f2010-10-29 04:11:03 +00009980
glennrpd6bf1612010-12-17 17:28:54 +00009981 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009982 {
glennrp3b51f0e2010-11-27 18:14:08 +00009983 if (logging != MagickFalse)
9984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9985 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009986 ping_have_bKGD = MagickFalse;
9987 }
glennrp17a14852010-05-10 03:01:59 +00009988 }
glennrp26f37912010-12-23 16:22:42 +00009989 }
glennrp17a14852010-05-10 03:01:59 +00009990
cristy3ed852e2009-09-05 21:47:34 +00009991 if (logging != MagickFalse)
9992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +00009993 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
9994 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009995 /*
9996 Initialize compression level and filtering.
9997 */
9998 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009999 {
10000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10001 " Setting up deflate compression");
10002
10003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10004 " Compression buffer size: 32768");
10005 }
10006
cristy3ed852e2009-09-05 21:47:34 +000010007 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +000010008
cristy3ed852e2009-09-05 21:47:34 +000010009 if (logging != MagickFalse)
10010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10011 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +000010012
cristy4054bfb2011-08-29 23:41:39 +000010013 png_set_compression_mem_level(ping, 9);
10014
glennrp10d739e2011-06-29 18:00:52 +000010015 /* Untangle the "-quality" setting:
10016
10017 Undefined is 0; the default is used.
10018 Default is 75
10019
10020 10's digit:
10021
10022 0: Use Z_HUFFMAN_ONLY strategy with the
10023 zlib default compression level
10024
10025 1-9: the zlib compression level
10026
10027 1's digit:
10028
10029 0-4: the PNG filter method
10030
10031 5: libpng adaptive filtering if compression level > 5
10032 libpng filter type "none" if compression level <= 5
10033 or if image is grayscale or palette
glennrp750105b2012-04-25 16:20:45 +000010034
glennrp10d739e2011-06-29 18:00:52 +000010035 6: libpng adaptive filtering
10036
10037 7: "LOCO" filtering (intrapixel differing) if writing
10038 a MNG, othewise "none". Did not work in IM-6.7.0-9
10039 and earlier because of a missing "else".
10040
10041 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +000010042 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +000010043
10044 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +000010045 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +000010046
10047 Note that using the -quality option, not all combinations of
10048 PNG filter type, zlib compression level, and zlib compression
cristy16ea1392012-03-21 20:38:41 +000010049 strategy are possible. This will be addressed soon in a
10050 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +000010051
10052 */
10053
cristy3ed852e2009-09-05 21:47:34 +000010054 quality=image->quality == UndefinedCompressionQuality ? 75UL :
10055 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +000010056
glennrp18682582011-06-30 18:11:47 +000010057 if (quality <= 9)
10058 {
10059 if (mng_info->write_png_compression_strategy == 0)
10060 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10061 }
glennrp750105b2012-04-25 16:20:45 +000010062
glennrp18682582011-06-30 18:11:47 +000010063 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +000010064 {
10065 int
10066 level;
10067
cristybb503372010-05-27 20:51:26 +000010068 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +000010069
glennrp18682582011-06-30 18:11:47 +000010070 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +000010071 }
glennrp0fe50b42010-11-16 03:52:51 +000010072
glennrp18682582011-06-30 18:11:47 +000010073 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +000010074 {
glennrp18682582011-06-30 18:11:47 +000010075 if ((quality %10) == 8 || (quality %10) == 9)
glennrpa24b2452012-06-27 11:38:38 +000010076#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10077 mng_info->write_png_compression_strategy=Z_RLE+1;
10078#else
10079 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10080#endif
cristy3ed852e2009-09-05 21:47:34 +000010081 }
glennrp0fe50b42010-11-16 03:52:51 +000010082
glennrp18682582011-06-30 18:11:47 +000010083 if (mng_info->write_png_compression_filter == 0)
10084 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10085
cristy3ed852e2009-09-05 21:47:34 +000010086 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010087 {
glennrp18682582011-06-30 18:11:47 +000010088 if (mng_info->write_png_compression_level)
10089 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10090 " Compression level: %d",
10091 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +000010092
glennrp18682582011-06-30 18:11:47 +000010093 if (mng_info->write_png_compression_strategy)
10094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10095 " Compression strategy: %d",
10096 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +000010097
glennrp18682582011-06-30 18:11:47 +000010098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10099 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +000010100
cristy4054bfb2011-08-29 23:41:39 +000010101 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +000010102 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10103 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +000010104 else if (mng_info->write_png_compression_filter == 0 ||
10105 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +000010106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10107 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +000010108 else
10109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10110 " Base filter method: %d",
10111 (int) mng_info->write_png_compression_filter-1);
10112 }
glennrp2b013e42010-11-24 16:55:50 +000010113
glennrp18682582011-06-30 18:11:47 +000010114 if (mng_info->write_png_compression_level != 0)
10115 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10116
10117 if (mng_info->write_png_compression_filter == 6)
10118 {
10119 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10120 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10121 (quality < 50))
10122 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10123 else
10124 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10125 }
cristy4054bfb2011-08-29 23:41:39 +000010126 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +000010127 mng_info->write_png_compression_filter == 10)
10128 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10129
10130 else if (mng_info->write_png_compression_filter == 8)
10131 {
10132#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10133 if (mng_info->write_mng)
10134 {
10135 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10136 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10137 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10138 }
10139#endif
cristy4054bfb2011-08-29 23:41:39 +000010140 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +000010141 }
10142
10143 else if (mng_info->write_png_compression_filter == 9)
10144 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10145
10146 else if (mng_info->write_png_compression_filter != 0)
10147 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10148 mng_info->write_png_compression_filter-1);
10149
10150 if (mng_info->write_png_compression_strategy != 0)
10151 png_set_compression_strategy(ping,
10152 mng_info->write_png_compression_strategy-1);
10153
cristy0d57eec2011-09-04 22:13:56 +000010154 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10155 if (ping_exclude_sRGB != MagickFalse ||
10156 (image->rendering_intent == UndefinedIntent))
10157 {
10158 if ((ping_exclude_tEXt == MagickFalse ||
10159 ping_exclude_zTXt == MagickFalse) &&
10160 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +000010161 {
10162 ResetImageProfileIterator(image);
10163 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +000010164 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010165 profile=GetImageProfile(image,name);
10166
10167 if (profile != (StringInfo *) NULL)
10168 {
glennrp5af765f2010-03-30 11:12:18 +000010169#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +000010170 if ((LocaleCompare(name,"ICC") == 0) ||
10171 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +000010172 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010173
10174 if (ping_exclude_iCCP == MagickFalse)
10175 {
cristy16ea1392012-03-21 20:38:41 +000010176 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +000010177#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +000010178 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +000010179#else
10180 (png_const_bytep) GetStringInfoDatum(profile),
10181#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010182 (png_uint_32) GetStringInfoLength(profile));
10183 }
glennrp26f37912010-12-23 16:22:42 +000010184 }
glennrp0fe50b42010-11-16 03:52:51 +000010185
glennrpc8cbc5d2011-01-01 00:12:34 +000010186 else
cristy3ed852e2009-09-05 21:47:34 +000010187#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010188 if (ping_exclude_zCCP == MagickFalse)
10189 {
glennrpcf002022011-01-30 02:38:15 +000010190 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +000010191 (unsigned char *) name,(unsigned char *) name,
10192 GetStringInfoDatum(profile),
10193 (png_uint_32) GetStringInfoLength(profile));
10194 }
10195 }
glennrp0b206f52011-01-07 04:55:32 +000010196
glennrpc8cbc5d2011-01-01 00:12:34 +000010197 if (logging != MagickFalse)
10198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10199 " Setting up text chunk with %s profile",name);
10200
10201 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +000010202 }
cristy0d57eec2011-09-04 22:13:56 +000010203 }
cristy3ed852e2009-09-05 21:47:34 +000010204 }
10205
10206#if defined(PNG_WRITE_sRGB_SUPPORTED)
10207 if ((mng_info->have_write_global_srgb == 0) &&
glennrp5f11bf92012-05-05 03:21:03 +000010208 (image->rendering_intent != UndefinedIntent))
cristy3ed852e2009-09-05 21:47:34 +000010209 {
glennrp26f37912010-12-23 16:22:42 +000010210 if (ping_exclude_sRGB == MagickFalse)
10211 {
10212 /*
10213 Note image rendering intent.
10214 */
10215 if (logging != MagickFalse)
10216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10217 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +000010218
glennrp26f37912010-12-23 16:22:42 +000010219 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +000010220 Magick_RenderingIntent_to_PNG_RenderingIntent(
10221 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +000010222 }
cristy3ed852e2009-09-05 21:47:34 +000010223 }
glennrp26f37912010-12-23 16:22:42 +000010224
glennrp5af765f2010-03-30 11:12:18 +000010225 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +000010226#endif
10227 {
glennrp2cc891a2010-12-24 13:44:32 +000010228 if (ping_exclude_gAMA == MagickFalse &&
10229 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +000010230 (image->gamma < .45 || image->gamma > .46)))
10231 {
cristy3ed852e2009-09-05 21:47:34 +000010232 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10233 {
10234 /*
10235 Note image gamma.
10236 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10237 */
10238 if (logging != MagickFalse)
10239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10240 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010241
cristy3ed852e2009-09-05 21:47:34 +000010242 png_set_gAMA(ping,ping_info,image->gamma);
10243 }
glennrp26f37912010-12-23 16:22:42 +000010244 }
glennrp2b013e42010-11-24 16:55:50 +000010245
glennrp26f37912010-12-23 16:22:42 +000010246 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010247 {
glennrp26f37912010-12-23 16:22:42 +000010248 if ((mng_info->have_write_global_chrm == 0) &&
10249 (image->chromaticity.red_primary.x != 0.0))
10250 {
10251 /*
10252 Note image chromaticity.
10253 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10254 */
10255 PrimaryInfo
10256 bp,
10257 gp,
10258 rp,
10259 wp;
cristy3ed852e2009-09-05 21:47:34 +000010260
glennrp26f37912010-12-23 16:22:42 +000010261 wp=image->chromaticity.white_point;
10262 rp=image->chromaticity.red_primary;
10263 gp=image->chromaticity.green_primary;
10264 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010265
glennrp26f37912010-12-23 16:22:42 +000010266 if (logging != MagickFalse)
10267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10268 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010269
glennrp26f37912010-12-23 16:22:42 +000010270 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10271 bp.x,bp.y);
10272 }
10273 }
cristy3ed852e2009-09-05 21:47:34 +000010274 }
glennrpdfd70802010-11-14 01:23:35 +000010275
glennrp5af765f2010-03-30 11:12:18 +000010276 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010277
10278 if (mng_info->write_mng)
10279 png_set_sig_bytes(ping,8);
10280
cristy5d6fc9c2011-12-27 03:10:42 +000010281 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
cristy3ed852e2009-09-05 21:47:34 +000010282
glennrpd6bf1612010-12-17 17:28:54 +000010283 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010284 {
10285 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010286 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010287 {
glennrp5af765f2010-03-30 11:12:18 +000010288 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010289
glennrp5af765f2010-03-30 11:12:18 +000010290 if (ping_bit_depth < 8)
10291 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010292 }
glennrp0fe50b42010-11-16 03:52:51 +000010293
cristy3ed852e2009-09-05 21:47:34 +000010294 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010295 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010296 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010297 }
10298
glennrp0e8ea192010-12-24 18:00:33 +000010299 if (ping_need_colortype_warning != MagickFalse ||
10300 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010301 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010302 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010303 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010304 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010305 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010306 {
10307 if (logging != MagickFalse)
10308 {
glennrp0e8ea192010-12-24 18:00:33 +000010309 if (ping_need_colortype_warning != MagickFalse)
10310 {
10311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10312 " Image has transparency but tRNS chunk was excluded");
10313 }
10314
cristy3ed852e2009-09-05 21:47:34 +000010315 if (mng_info->write_png_depth)
10316 {
10317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010318 " Defined png:bit-depth=%u, Computed depth=%u",
cristy3ed852e2009-09-05 21:47:34 +000010319 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010320 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010321 }
glennrp0e8ea192010-12-24 18:00:33 +000010322
cristy3ed852e2009-09-05 21:47:34 +000010323 if (mng_info->write_png_colortype)
10324 {
10325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010326 " Defined png:color-type=%u, Computed color type=%u",
cristy3ed852e2009-09-05 21:47:34 +000010327 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010328 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010329 }
10330 }
glennrp0e8ea192010-12-24 18:00:33 +000010331
glennrp3bd2e412010-08-10 13:34:52 +000010332 png_warning(ping,
cristy5d6fc9c2011-12-27 03:10:42 +000010333 "Cannot write image with defined png:bit-depth or png:color-type.");
cristy3ed852e2009-09-05 21:47:34 +000010334 }
10335
cristy8a46d822012-08-28 23:32:39 +000010336 if (image_matte != MagickFalse && image->alpha_trait != BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000010337 {
10338 /* Add an opaque matte channel */
cristyb0a657e2012-08-29 00:45:37 +000010339 image->alpha_trait = BlendPixelTrait;
cristy16ea1392012-03-21 20:38:41 +000010340 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010341
glennrpb4a13412010-05-05 12:47:19 +000010342 if (logging != MagickFalse)
10343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10344 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010345 }
10346
glennrp0e319732011-01-25 21:53:13 +000010347 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010348 {
glennrp991d11d2010-11-12 21:55:28 +000010349 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010350 {
glennrp991d11d2010-11-12 21:55:28 +000010351 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010352 if (logging != MagickFalse)
10353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10354 " Setting ping_have_tRNS=MagickTrue.");
10355 }
glennrpe9c26dc2010-05-30 01:56:35 +000010356 }
10357
cristy3ed852e2009-09-05 21:47:34 +000010358 if (logging != MagickFalse)
10359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10360 " Writing PNG header chunks");
10361
glennrp5af765f2010-03-30 11:12:18 +000010362 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10363 ping_bit_depth,ping_color_type,
10364 ping_interlace_method,ping_compression_method,
10365 ping_filter_method);
10366
glennrp39992b42010-11-14 00:03:43 +000010367 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10368 {
glennrpf09bded2011-01-08 01:15:59 +000010369 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010370
glennrp3b51f0e2010-11-27 18:14:08 +000010371 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010372 {
glennrp8640fb52010-11-23 15:48:26 +000010373 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010374 {
glennrpd6bf1612010-12-17 17:28:54 +000010375 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010376 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010377 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10378 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010379 (int) palette[i].red,
10380 (int) palette[i].green,
10381 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010382 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010383 (int) ping_trans_alpha[i]);
10384 else
10385 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010386 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010387 (int) i,
10388 (int) palette[i].red,
10389 (int) palette[i].green,
10390 (int) palette[i].blue);
10391 }
glennrp39992b42010-11-14 00:03:43 +000010392 }
glennrp39992b42010-11-14 00:03:43 +000010393 }
10394
glennrp26f37912010-12-23 16:22:42 +000010395 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010396 {
glennrp26f37912010-12-23 16:22:42 +000010397 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010398 {
glennrp26f37912010-12-23 16:22:42 +000010399 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010400 if (logging)
10401 {
10402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10403 " Setting up bKGD chunk");
10404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10405 " background color = (%d,%d,%d)",
10406 (int) ping_background.red,
10407 (int) ping_background.green,
10408 (int) ping_background.blue);
10409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10410 " index = %d, gray=%d",
10411 (int) ping_background.index,
10412 (int) ping_background.gray);
10413 }
10414 }
glennrp26f37912010-12-23 16:22:42 +000010415 }
10416
10417 if (ping_exclude_pHYs == MagickFalse)
10418 {
10419 if (ping_have_pHYs != MagickFalse)
10420 {
10421 png_set_pHYs(ping,ping_info,
10422 ping_pHYs_x_resolution,
10423 ping_pHYs_y_resolution,
10424 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010425
10426 if (logging)
10427 {
10428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10429 " Setting up pHYs chunk");
10430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10431 " x_resolution=%lu",
10432 (unsigned long) ping_pHYs_x_resolution);
10433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10434 " y_resolution=%lu",
10435 (unsigned long) ping_pHYs_y_resolution);
10436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10437 " unit_type=%lu",
10438 (unsigned long) ping_pHYs_unit_type);
10439 }
glennrp26f37912010-12-23 16:22:42 +000010440 }
glennrpdfd70802010-11-14 01:23:35 +000010441 }
10442
10443#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010444 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010445 {
glennrp26f37912010-12-23 16:22:42 +000010446 if (image->page.x || image->page.y)
10447 {
10448 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10449 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010450
glennrp26f37912010-12-23 16:22:42 +000010451 if (logging != MagickFalse)
10452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10453 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10454 (int) image->page.x, (int) image->page.y);
10455 }
glennrpdfd70802010-11-14 01:23:35 +000010456 }
10457#endif
10458
glennrpda8f3a72011-02-27 23:54:12 +000010459 if (mng_info->need_blob != MagickFalse)
10460 {
cristy16ea1392012-03-21 20:38:41 +000010461 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010462 MagickFalse)
10463 png_error(ping,"WriteBlob Failed");
10464
10465 ping_have_blob=MagickTrue;
10466 }
10467
cristy3ed852e2009-09-05 21:47:34 +000010468 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010469
glennrp39992b42010-11-14 00:03:43 +000010470 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010471 {
glennrp3b51f0e2010-11-27 18:14:08 +000010472 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010473 {
10474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10475 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10476 }
10477
10478 if (ping_color_type == 3)
10479 (void) png_set_tRNS(ping, ping_info,
10480 ping_trans_alpha,
10481 ping_num_trans,
10482 NULL);
10483
10484 else
10485 {
10486 (void) png_set_tRNS(ping, ping_info,
10487 NULL,
10488 0,
10489 &ping_trans_color);
10490
glennrp3b51f0e2010-11-27 18:14:08 +000010491 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010492 {
10493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010494 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010495 (int) ping_trans_color.red,
10496 (int) ping_trans_color.green,
10497 (int) ping_trans_color.blue);
10498 }
10499 }
glennrp991d11d2010-11-12 21:55:28 +000010500 }
10501
cristy3ed852e2009-09-05 21:47:34 +000010502 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010503 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010504
cristy3ed852e2009-09-05 21:47:34 +000010505 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010506
cristy3ed852e2009-09-05 21:47:34 +000010507 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010508 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010509
glennrp26f37912010-12-23 16:22:42 +000010510 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010511 {
glennrp4f25bd02011-01-01 18:51:28 +000010512 if ((image->page.width != 0 && image->page.width != image->columns) ||
10513 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010514 {
10515 unsigned char
10516 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010517
glennrp26f37912010-12-23 16:22:42 +000010518 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10519 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010520 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010521 PNGLong(chunk+4,(png_uint_32) image->page.width);
10522 PNGLong(chunk+8,(png_uint_32) image->page.height);
10523 chunk[12]=0; /* unit = pixels */
10524 (void) WriteBlob(image,13,chunk);
10525 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10526 }
cristy3ed852e2009-09-05 21:47:34 +000010527 }
10528
10529#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010530 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010531#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010532 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010533#undef PNG_HAVE_IDAT
10534#endif
10535
10536 png_set_packing(ping);
10537 /*
10538 Allocate memory.
10539 */
10540 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010541 if (image_depth > 8)
10542 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010543 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010544 {
glennrpb4a13412010-05-05 12:47:19 +000010545 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010546 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010547 break;
glennrp0fe50b42010-11-16 03:52:51 +000010548
glennrpb4a13412010-05-05 12:47:19 +000010549 case PNG_COLOR_TYPE_GRAY_ALPHA:
10550 rowbytes*=2;
10551 break;
glennrp0fe50b42010-11-16 03:52:51 +000010552
glennrpb4a13412010-05-05 12:47:19 +000010553 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010554 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010555 break;
glennrp0fe50b42010-11-16 03:52:51 +000010556
glennrpb4a13412010-05-05 12:47:19 +000010557 default:
10558 break;
cristy3ed852e2009-09-05 21:47:34 +000010559 }
glennrp3b51f0e2010-11-27 18:14:08 +000010560
10561 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010562 {
10563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10564 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010565
glennrpb4a13412010-05-05 12:47:19 +000010566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010567 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010568 }
glennrpcf002022011-01-30 02:38:15 +000010569 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10570 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010571
glennrpcf002022011-01-30 02:38:15 +000010572 if (ping_pixels == (unsigned char *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010573 png_error(ping,"Allocation of memory for pixels failed");
glennrp0fe50b42010-11-16 03:52:51 +000010574
cristy3ed852e2009-09-05 21:47:34 +000010575 /*
10576 Initialize image scanlines.
10577 */
cristyed552522009-10-16 14:04:35 +000010578 quantum_info=AcquireQuantumInfo(image_info,image);
10579 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010580 png_error(ping,"Memory allocation for quantum_info failed");
cristy3ed852e2009-09-05 21:47:34 +000010581 quantum_info->format=UndefinedQuantumFormat;
10582 quantum_info->depth=image_depth;
glennrp4b840d72012-11-22 16:01:16 +000010583 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
cristy3ed852e2009-09-05 21:47:34 +000010584 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010585
cristy3ed852e2009-09-05 21:47:34 +000010586 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrpfd164d22013-01-26 21:10:22 +000010587 !mng_info->write_png48 && !mng_info->write_png64 &&
glennrp8bb3a022010-12-13 20:40:04 +000010588 !mng_info->write_png32) &&
10589 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010590 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010591 image_matte == MagickFalse &&
10592 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010593 {
glennrp8bb3a022010-12-13 20:40:04 +000010594 /* Palette, Bilevel, or Opaque Monochrome */
cristy16ea1392012-03-21 20:38:41 +000010595 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010596 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010597
cristy3ed852e2009-09-05 21:47:34 +000010598 quantum_info->depth=8;
10599 for (pass=0; pass < num_passes; pass++)
10600 {
10601 /*
10602 Convert PseudoClass image to a PNG monochrome image.
10603 */
cristybb503372010-05-27 20:51:26 +000010604 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010605 {
glennrpd71e86a2011-02-24 01:28:37 +000010606 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10608 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010609
cristy16ea1392012-03-21 20:38:41 +000010610 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010611
cristy16ea1392012-03-21 20:38:41 +000010612 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010613 break;
glennrp0fe50b42010-11-16 03:52:51 +000010614
cristy3ed852e2009-09-05 21:47:34 +000010615 if (mng_info->IsPalette)
10616 {
cristy16ea1392012-03-21 20:38:41 +000010617 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10618 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010619 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10620 mng_info->write_png_depth &&
10621 mng_info->write_png_depth != old_bit_depth)
10622 {
10623 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010624 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010625 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010626 >> (8-old_bit_depth));
10627 }
10628 }
glennrp0fe50b42010-11-16 03:52:51 +000010629
cristy3ed852e2009-09-05 21:47:34 +000010630 else
10631 {
cristy16ea1392012-03-21 20:38:41 +000010632 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10633 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010634 }
glennrp0fe50b42010-11-16 03:52:51 +000010635
cristy3ed852e2009-09-05 21:47:34 +000010636 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010637 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010638 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010639 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010640
glennrp3b51f0e2010-11-27 18:14:08 +000010641 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10643 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010644
glennrpcf002022011-01-30 02:38:15 +000010645 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010646 }
10647 if (image->previous == (Image *) NULL)
10648 {
10649 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10650 if (status == MagickFalse)
10651 break;
10652 }
10653 }
10654 }
glennrp0fe50b42010-11-16 03:52:51 +000010655
glennrp8bb3a022010-12-13 20:40:04 +000010656 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010657 {
glennrp0fe50b42010-11-16 03:52:51 +000010658 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrpfd164d22013-01-26 21:10:22 +000010659 !mng_info->write_png48 && !mng_info->write_png64 &&
10660 !mng_info->write_png32) && (image_matte != MagickFalse ||
10661 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10662 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010663 {
cristy16ea1392012-03-21 20:38:41 +000010664 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010665 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010666
glennrp8bb3a022010-12-13 20:40:04 +000010667 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010668 {
glennrp8bb3a022010-12-13 20:40:04 +000010669
cristybb503372010-05-27 20:51:26 +000010670 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010671 {
cristy16ea1392012-03-21 20:38:41 +000010672 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010673
cristy16ea1392012-03-21 20:38:41 +000010674 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010675 break;
glennrp2cc891a2010-12-24 13:44:32 +000010676
glennrp5af765f2010-03-30 11:12:18 +000010677 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010678 {
glennrp8bb3a022010-12-13 20:40:04 +000010679 if (mng_info->IsPalette)
cristy16ea1392012-03-21 20:38:41 +000010680 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10681 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010682
glennrp8bb3a022010-12-13 20:40:04 +000010683 else
cristy16ea1392012-03-21 20:38:41 +000010684 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10685 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010686
glennrp3b51f0e2010-11-27 18:14:08 +000010687 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010689 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010690 }
glennrp2cc891a2010-12-24 13:44:32 +000010691
glennrp8bb3a022010-12-13 20:40:04 +000010692 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10693 {
10694 if (logging != MagickFalse && y == 0)
10695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10696 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010697
cristy16ea1392012-03-21 20:38:41 +000010698 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10699 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010700 }
glennrp2cc891a2010-12-24 13:44:32 +000010701
glennrp3b51f0e2010-11-27 18:14:08 +000010702 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010704 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010705
glennrpcf002022011-01-30 02:38:15 +000010706 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010707 }
glennrp2cc891a2010-12-24 13:44:32 +000010708
glennrp8bb3a022010-12-13 20:40:04 +000010709 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010710 {
glennrp8bb3a022010-12-13 20:40:04 +000010711 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10712 if (status == MagickFalse)
10713 break;
cristy3ed852e2009-09-05 21:47:34 +000010714 }
cristy3ed852e2009-09-05 21:47:34 +000010715 }
10716 }
glennrp8bb3a022010-12-13 20:40:04 +000010717
10718 else
10719 {
cristy16ea1392012-03-21 20:38:41 +000010720 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010721 *p;
10722
10723 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010724 {
glennrpfd164d22013-01-26 21:10:22 +000010725 if ((image_depth > 8) ||
10726 mng_info->write_png24 ||
glennrp8bb3a022010-12-13 20:40:04 +000010727 mng_info->write_png32 ||
glennrpfd164d22013-01-26 21:10:22 +000010728 mng_info->write_png48 ||
10729 mng_info->write_png64 ||
10730 (!mng_info->write_png8 && !mng_info->IsPalette))
glennrp8bb3a022010-12-13 20:40:04 +000010731 {
10732 for (y=0; y < (ssize_t) image->rows; y++)
10733 {
cristy862a33c2012-05-17 22:49:37 +000010734 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010735
cristy16ea1392012-03-21 20:38:41 +000010736 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010737 break;
glennrp2cc891a2010-12-24 13:44:32 +000010738
glennrp8bb3a022010-12-13 20:40:04 +000010739 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10740 {
10741 if (image->storage_class == DirectClass)
cristy16ea1392012-03-21 20:38:41 +000010742 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10743 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010744
glennrp8bb3a022010-12-13 20:40:04 +000010745 else
cristy16ea1392012-03-21 20:38:41 +000010746 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10747 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010748 }
glennrp2cc891a2010-12-24 13:44:32 +000010749
glennrp8bb3a022010-12-13 20:40:04 +000010750 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10751 {
cristy16ea1392012-03-21 20:38:41 +000010752 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010753 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000010754 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010755
glennrp8bb3a022010-12-13 20:40:04 +000010756 if (logging != MagickFalse && y == 0)
10757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10758 " Writing GRAY_ALPHA PNG pixels (3)");
10759 }
glennrp2cc891a2010-12-24 13:44:32 +000010760
glennrp8bb3a022010-12-13 20:40:04 +000010761 else if (image_matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +000010762 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10763 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010764
glennrp8bb3a022010-12-13 20:40:04 +000010765 else
cristy16ea1392012-03-21 20:38:41 +000010766 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10767 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010768
glennrp8bb3a022010-12-13 20:40:04 +000010769 if (logging != MagickFalse && y == 0)
10770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10771 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010772
glennrpcf002022011-01-30 02:38:15 +000010773 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010774 }
10775 }
glennrp2cc891a2010-12-24 13:44:32 +000010776
glennrp8bb3a022010-12-13 20:40:04 +000010777 else
glennrpfd164d22013-01-26 21:10:22 +000010778 /* not ((image_depth > 8) ||
10779 mng_info->write_png24 || mng_info->write_png32 ||
10780 mng_info->write_png48 || mng_info->write_png64 ||
10781 (!mng_info->write_png8 && !mng_info->IsPalette))
10782 */
glennrp8bb3a022010-12-13 20:40:04 +000010783 {
10784 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10785 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10786 {
10787 if (logging != MagickFalse)
10788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10789 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010790
glennrp8bb3a022010-12-13 20:40:04 +000010791 quantum_info->depth=8;
10792 image_depth=8;
10793 }
glennrp2cc891a2010-12-24 13:44:32 +000010794
glennrp8bb3a022010-12-13 20:40:04 +000010795 for (y=0; y < (ssize_t) image->rows; y++)
10796 {
10797 if (logging != MagickFalse && y == 0)
10798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10799 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010800
cristy16ea1392012-03-21 20:38:41 +000010801 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010802
cristy16ea1392012-03-21 20:38:41 +000010803 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010804 break;
glennrp2cc891a2010-12-24 13:44:32 +000010805
glennrp8bb3a022010-12-13 20:40:04 +000010806 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010807 {
glennrp4bf89732011-03-21 13:48:28 +000010808 quantum_info->depth=image->depth;
10809
cristy16ea1392012-03-21 20:38:41 +000010810 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10811 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010812 }
glennrp2cc891a2010-12-24 13:44:32 +000010813
glennrp8bb3a022010-12-13 20:40:04 +000010814 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10815 {
10816 if (logging != MagickFalse && y == 0)
10817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10818 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010819
cristy16ea1392012-03-21 20:38:41 +000010820 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010821 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000010822 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010823 }
glennrp2cc891a2010-12-24 13:44:32 +000010824
glennrp8bb3a022010-12-13 20:40:04 +000010825 else
glennrp8bb3a022010-12-13 20:40:04 +000010826 {
cristy16ea1392012-03-21 20:38:41 +000010827 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10828 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010829
10830 if (logging != MagickFalse && y <= 2)
10831 {
10832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010833 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010834
10835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10836 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10837 (int)ping_pixels[0],(int)ping_pixels[1]);
10838 }
glennrp8bb3a022010-12-13 20:40:04 +000010839 }
glennrpcf002022011-01-30 02:38:15 +000010840 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010841 }
10842 }
glennrp2cc891a2010-12-24 13:44:32 +000010843
glennrp8bb3a022010-12-13 20:40:04 +000010844 if (image->previous == (Image *) NULL)
10845 {
10846 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10847 if (status == MagickFalse)
10848 break;
10849 }
cristy3ed852e2009-09-05 21:47:34 +000010850 }
glennrp8bb3a022010-12-13 20:40:04 +000010851 }
10852 }
10853
cristyb32b90a2009-09-07 21:45:48 +000010854 if (quantum_info != (QuantumInfo *) NULL)
10855 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010856
10857 if (logging != MagickFalse)
10858 {
10859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010860 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010861
cristy3ed852e2009-09-05 21:47:34 +000010862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010863 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010864
cristy3ed852e2009-09-05 21:47:34 +000010865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010866 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010867
cristy3ed852e2009-09-05 21:47:34 +000010868 if (mng_info->write_png_depth)
10869 {
10870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010871 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010872 }
glennrp0fe50b42010-11-16 03:52:51 +000010873
cristy3ed852e2009-09-05 21:47:34 +000010874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010875 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010876
cristy3ed852e2009-09-05 21:47:34 +000010877 if (mng_info->write_png_colortype)
10878 {
10879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010880 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010881 }
glennrp0fe50b42010-11-16 03:52:51 +000010882
cristy3ed852e2009-09-05 21:47:34 +000010883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010884 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010885
cristy3ed852e2009-09-05 21:47:34 +000010886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010887 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010888 }
10889 /*
glennrpa0ed0092011-04-18 16:36:29 +000010890 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010891 */
glennrp823b55c2011-03-14 18:46:46 +000010892 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010893 {
glennrp26f37912010-12-23 16:22:42 +000010894 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010895 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010896 while (property != (const char *) NULL)
10897 {
10898 png_textp
10899 text;
glennrp2cc891a2010-12-24 13:44:32 +000010900
cristy16ea1392012-03-21 20:38:41 +000010901 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010902
10903 /* Don't write any "png:" properties; those are just for "identify" */
10904 if (LocaleNCompare(property,"png:",4) != 0 &&
10905
10906 /* Suppress density and units if we wrote a pHYs chunk */
10907 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010908 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010909 LocaleCompare(property,"units") != 0) &&
10910
10911 /* Suppress the IM-generated Date:create and Date:modify */
10912 (ping_exclude_date == MagickFalse ||
10913 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010914 {
glennrpc70af4a2011-03-07 00:08:23 +000010915 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010916 {
cristya865ccd2012-07-28 00:33:10 +000010917
10918#if PNG_LIBPNG_VER >= 14000
10919 text=(png_textp) png_malloc(ping,
10920 (png_alloc_size_t) sizeof(png_text));
10921#else
10922 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
10923#endif
glennrpc70af4a2011-03-07 00:08:23 +000010924 text[0].key=(char *) property;
10925 text[0].text=(char *) value;
10926 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010927
glennrpc70af4a2011-03-07 00:08:23 +000010928 if (ping_exclude_tEXt != MagickFalse)
10929 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10930
10931 else if (ping_exclude_zTXt != MagickFalse)
10932 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10933
10934 else
glennrp26f37912010-12-23 16:22:42 +000010935 {
glennrpc70af4a2011-03-07 00:08:23 +000010936 text[0].compression=image_info->compression == NoCompression ||
10937 (image_info->compression == UndefinedCompression &&
10938 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10939 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010940 }
glennrp2cc891a2010-12-24 13:44:32 +000010941
glennrpc70af4a2011-03-07 00:08:23 +000010942 if (logging != MagickFalse)
10943 {
10944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10945 " Setting up text chunk");
10946
10947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpcbc92152013-02-04 15:46:22 +000010948 " keyword: '%s'",text[0].key);
glennrpc70af4a2011-03-07 00:08:23 +000010949 }
10950
10951 png_set_text(ping,ping_info,text,1);
10952 png_free(ping,text);
10953 }
glennrp26f37912010-12-23 16:22:42 +000010954 }
10955 property=GetNextImageProperty(image);
10956 }
cristy3ed852e2009-09-05 21:47:34 +000010957 }
10958
10959 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010960 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010961
10962 if (logging != MagickFalse)
10963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10964 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010965
cristy3ed852e2009-09-05 21:47:34 +000010966 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010967
cristy3ed852e2009-09-05 21:47:34 +000010968 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10969 {
10970 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010971 (ping_width != mng_info->page.width) ||
10972 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010973 {
10974 unsigned char
10975 chunk[32];
10976
10977 /*
10978 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10979 */
10980 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10981 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010982 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010983 chunk[4]=4;
10984 chunk[5]=0; /* frame name separator (no name) */
10985 chunk[6]=1; /* flag for changing delay, for next frame only */
10986 chunk[7]=0; /* flag for changing frame timeout */
10987 chunk[8]=1; /* flag for changing frame clipping for next frame */
10988 chunk[9]=0; /* flag for changing frame sync_id */
10989 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10990 chunk[14]=0; /* clipping boundaries delta type */
10991 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10992 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010993 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010994 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10995 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010996 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010997 (void) WriteBlob(image,31,chunk);
10998 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10999 mng_info->old_framing_mode=4;
11000 mng_info->framing_mode=1;
11001 }
glennrp0fe50b42010-11-16 03:52:51 +000011002
cristy3ed852e2009-09-05 21:47:34 +000011003 else
11004 mng_info->framing_mode=3;
11005 }
11006 if (mng_info->write_mng && !mng_info->need_fram &&
11007 ((int) image->dispose == 3))
glennrpedaa0382012-04-12 14:16:21 +000011008 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
glennrp0fe50b42010-11-16 03:52:51 +000011009
cristy3ed852e2009-09-05 21:47:34 +000011010 /*
11011 Free PNG resources.
11012 */
glennrp5af765f2010-03-30 11:12:18 +000011013
cristy3ed852e2009-09-05 21:47:34 +000011014 png_destroy_write_struct(&ping,&ping_info);
11015
glennrpcf002022011-01-30 02:38:15 +000011016 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000011017
cristy16ea1392012-03-21 20:38:41 +000011018 if (ping_have_blob != MagickFalse)
11019 (void) CloseBlob(image);
11020
11021 image_info=DestroyImageInfo(image_info);
11022 image=DestroyImage(image);
11023
glennrpb9cfe272010-12-21 15:08:06 +000011024 /* Store bit depth actually written */
11025 s[0]=(char) ping_bit_depth;
11026 s[1]='\0';
11027
cristy16ea1392012-03-21 20:38:41 +000011028 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000011029
cristy3ed852e2009-09-05 21:47:34 +000011030 if (logging != MagickFalse)
11031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11032 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011033
glennrpedaa0382012-04-12 14:16:21 +000011034#ifdef PNG_SETJMP_NOT_THREAD_SAFE
11035 UnlockSemaphoreInfo(ping_semaphore);
11036#endif
11037
11038 /* } for navigation to beginning of SETJMP-protected block. Revert to
11039 * Throwing an Exception when an error occurs.
11040 */
11041
cristy3ed852e2009-09-05 21:47:34 +000011042 return(MagickTrue);
11043/* End write one PNG image */
glennrpedaa0382012-04-12 14:16:21 +000011044
cristy3ed852e2009-09-05 21:47:34 +000011045}
11046
11047/*
11048%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11049% %
11050% %
11051% %
11052% W r i t e P N G I m a g e %
11053% %
11054% %
11055% %
11056%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11057%
11058% WritePNGImage() writes a Portable Network Graphics (PNG) or
11059% Multiple-image Network Graphics (MNG) image file.
11060%
11061% MNG support written by Glenn Randers-Pehrson, glennrp@image...
11062%
11063% The format of the WritePNGImage method is:
11064%
cristy16ea1392012-03-21 20:38:41 +000011065% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11066% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011067%
11068% A description of each parameter follows:
11069%
11070% o image_info: the image info.
11071%
11072% o image: The image.
11073%
cristy16ea1392012-03-21 20:38:41 +000011074% o exception: return any errors or warnings in this structure.
11075%
cristy3ed852e2009-09-05 21:47:34 +000011076% Returns MagickTrue on success, MagickFalse on failure.
11077%
11078% Communicating with the PNG encoder:
11079%
11080% While the datastream written is always in PNG format and normally would
11081% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000011082% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000011083%
glennrp5a39f372011-02-25 04:52:16 +000011084% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11085% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000011086% is present, the tRNS chunk must only have values 0 and 255
11087% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000011088% transparent). If other values are present they will be
11089% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000011090% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000011091% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11092% of any resulting fully-transparent pixels is changed to
11093% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000011094%
11095% If you want better quantization or dithering of the colors
11096% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000011097% PNG encoder. The pixels contain 8-bit indices even if
11098% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000011099% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000011100% PNG grayscale type might be slightly more efficient. Please
11101% note that writing to the PNG8 format may result in loss
11102% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000011103%
11104% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11105% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000011106% one of the colors as transparent. The only loss incurred
11107% is reduction of sample depth to 8. If the image has more
11108% than one transparent color, has semitransparent pixels, or
11109% has an opaque pixel with the same RGB components as the
11110% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000011111%
11112% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11113% transparency is permitted, i.e., the alpha sample for
11114% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000011115% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000011116% The only loss in data is the reduction of the sample depth
11117% to 8.
cristy3ed852e2009-09-05 21:47:34 +000011118%
glennrpfd164d22013-01-26 21:10:22 +000011119% o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11120% chunk can be present to convey binary transparency by naming
11121% one of the colors as transparent. If the image has more
11122% than one transparent color, has semitransparent pixels, or
11123% has an opaque pixel with the same RGB components as the
11124% transparent color, an image is not written.
11125%
11126% o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11127% transparency is permitted, i.e., the alpha sample for
11128% each pixel can have any value from 0 to 65535. The alpha
11129% channel is present even if the image is fully opaque.
11130%
glennrp5830fbc2013-01-27 06:11:45 +000011131% o PNG00: A PNG that inherits its colortype and bit-depth from the input
11132% image, if the input was a PNG, is written. If these values
11133% cannot be found, then "PNG00" falls back to the regular "PNG"
11134% format.
11135%
cristy3ed852e2009-09-05 21:47:34 +000011136% o -define: For more precise control of the PNG output, you can use the
11137% Image options "png:bit-depth" and "png:color-type". These
11138% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000011139% from the application programming interfaces. The options
11140% are case-independent and are converted to lowercase before
11141% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000011142%
11143% png:color-type can be 0, 2, 3, 4, or 6.
11144%
11145% When png:color-type is 0 (Grayscale), png:bit-depth can
11146% be 1, 2, 4, 8, or 16.
11147%
11148% When png:color-type is 2 (RGB), png:bit-depth can
11149% be 8 or 16.
11150%
11151% When png:color-type is 3 (Indexed), png:bit-depth can
11152% be 1, 2, 4, or 8. This refers to the number of bits
11153% used to store the index. The color samples always have
11154% bit-depth 8 in indexed PNG files.
11155%
11156% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11157% png:bit-depth can be 8 or 16.
11158%
glennrpfd164d22013-01-26 21:10:22 +000011159% If the image cannot be written without loss with the
11160% requested bit-depth and color-type, a PNG file will not
11161% be written, a warning will be issued, and the encoder will
11162% return MagickFalse.
glennrp5a39f372011-02-25 04:52:16 +000011163%
cristy3ed852e2009-09-05 21:47:34 +000011164% Since image encoders should not be responsible for the "heavy lifting",
11165% the user should make sure that ImageMagick has already reduced the
11166% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000011167% transparency prior to attempting to write the image with depth, color,
cristy16ea1392012-03-21 20:38:41 +000011168% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000011169%
cristy3ed852e2009-09-05 21:47:34 +000011170% Note that another definition, "png:bit-depth-written" exists, but it
11171% is not intended for external use. It is only used internally by the
11172% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11173%
11174% It is possible to request that the PNG encoder write previously-formatted
11175% ancillary chunks in the output PNG file, using the "-profile" commandline
11176% option as shown below or by setting the profile via a programming
11177% interface:
11178%
11179% -profile PNG-chunk-x:<file>
11180%
11181% where x is a location flag and <file> is a file containing the chunk
11182% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000011183% This encoder will compute the chunk length and CRC, so those must not
11184% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000011185%
11186% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11187% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11188% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000011189% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000011190%
glennrpbb8a7332010-11-13 15:17:35 +000011191% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000011192%
glennrp3241bd02010-12-12 04:36:28 +000011193% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000011194%
glennrpd6afd542010-11-19 01:53:05 +000011195% o 32-bit depth is reduced to 16.
11196% o 16-bit depth is reduced to 8 if all pixels contain samples whose
11197% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000011198% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000011199% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000011200% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000011201% o Grayscale images are reduced to 1, 2, or 4 bit depth if
11202% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000011203% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000011204% o If matte channel is present but only one transparent color is
11205% present, RGB+tRNS is written instead of RGBA
11206% o Opaque matte channel is removed (or added, if color-type 4 or 6
11207% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000011208%
cristy3ed852e2009-09-05 21:47:34 +000011209%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11210*/
11211static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy16ea1392012-03-21 20:38:41 +000011212 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011213{
11214 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011215 excluding,
11216 logging,
11217 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011218 status;
11219
11220 MngInfo
11221 *mng_info;
11222
11223 const char
11224 *value;
11225
11226 int
glennrp21f0e622011-01-07 16:20:57 +000011227 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000011228 source;
11229
cristy3ed852e2009-09-05 21:47:34 +000011230 /*
11231 Open image file.
11232 */
11233 assert(image_info != (const ImageInfo *) NULL);
11234 assert(image_info->signature == MagickSignature);
11235 assert(image != (Image *) NULL);
11236 assert(image->signature == MagickSignature);
11237 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011238 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011239 /*
11240 Allocate a MngInfo structure.
11241 */
11242 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011243 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000011244
cristy3ed852e2009-09-05 21:47:34 +000011245 if (mng_info == (MngInfo *) NULL)
11246 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011247
cristy3ed852e2009-09-05 21:47:34 +000011248 /*
11249 Initialize members of the MngInfo structure.
11250 */
11251 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11252 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000011253 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011254 have_mng_structure=MagickTrue;
11255
11256 /* See if user has requested a specific PNG subformat */
11257
11258 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11259 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11260 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
glennrpfd164d22013-01-26 21:10:22 +000011261 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11262 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
cristy3ed852e2009-09-05 21:47:34 +000011263
glennrpb381a262012-02-11 17:49:49 +000011264 value=GetImageOption(image_info,"png:format");
11265
11266 if (value != (char *) NULL)
11267 {
glennrpfd164d22013-01-26 21:10:22 +000011268 mng_info->write_png8 = MagickFalse;
11269 mng_info->write_png24 = MagickFalse;
11270 mng_info->write_png32 = MagickFalse;
11271 mng_info->write_png48 = MagickFalse;
11272 mng_info->write_png64 = MagickFalse;
11273
glennrpb381a262012-02-11 17:49:49 +000011274 if (LocaleCompare(value,"png8") == 0)
glennrpb381a262012-02-11 17:49:49 +000011275 mng_info->write_png8 = MagickTrue;
glennrpb381a262012-02-11 17:49:49 +000011276
11277 else if (LocaleCompare(value,"png24") == 0)
glennrpb381a262012-02-11 17:49:49 +000011278 mng_info->write_png24 = MagickTrue;
glennrpb381a262012-02-11 17:49:49 +000011279
11280 else if (LocaleCompare(value,"png32") == 0)
glennrpb381a262012-02-11 17:49:49 +000011281 mng_info->write_png32 = MagickTrue;
glennrpfd164d22013-01-26 21:10:22 +000011282
11283 else if (LocaleCompare(value,"png48") == 0)
11284 mng_info->write_png48 = MagickTrue;
11285
11286 else if (LocaleCompare(value,"png64") == 0)
11287 mng_info->write_png64 = MagickTrue;
glennrpb381a262012-02-11 17:49:49 +000011288 }
glennrp5830fbc2013-01-27 06:11:45 +000011289
11290 if (LocaleCompare(value,"png00") == 0)
11291 {
11292 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig
11293 Note that whitespace at the end of the property names must match
11294 that in the corresponding SetImageProperty() calls.
11295 */
11296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11297 " Format=%s",value);
11298
11299 value=GetImageProperty(image,"png:IHDR.bit-depth-orig ",exception);
11300
11301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11302 " png00 inherited bit depth=%s",value);
11303
11304 if (value != (char *) NULL)
11305 {
11306 if (LocaleCompare(value,"1") == 0)
11307 mng_info->write_png_depth = 1;
11308
11309 else if (LocaleCompare(value,"1") == 0)
11310 mng_info->write_png_depth = 2;
11311
11312 else if (LocaleCompare(value,"2") == 0)
11313 mng_info->write_png_depth = 4;
11314
11315 else if (LocaleCompare(value,"8") == 0)
11316 mng_info->write_png_depth = 8;
11317
11318 else if (LocaleCompare(value,"16") == 0)
11319 mng_info->write_png_depth = 16;
11320 }
11321
11322 value=GetImageProperty(image,"png:IHDR.color-type-orig ",exception);
11323
11324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11325 " png00 inherited color type=%s",value);
11326
11327 if (value != (char *) NULL)
11328 {
11329 if (LocaleCompare(value,"0") == 0)
11330 mng_info->write_png_colortype = 1;
11331
11332 else if (LocaleCompare(value,"2") == 0)
11333 mng_info->write_png_colortype = 3;
11334
11335 else if (LocaleCompare(value,"3") == 0)
11336 mng_info->write_png_colortype = 4;
11337
11338 else if (LocaleCompare(value,"4") == 0)
11339 mng_info->write_png_colortype = 5;
11340
11341 else if (LocaleCompare(value,"6") == 0)
11342 mng_info->write_png_colortype = 7;
11343 }
11344 }
11345
cristy3ed852e2009-09-05 21:47:34 +000011346 if (mng_info->write_png8)
11347 {
glennrp9c1eb072010-06-06 22:19:15 +000011348 mng_info->write_png_colortype = /* 3 */ 4;
11349 mng_info->write_png_depth = 8;
11350 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011351 }
11352
11353 if (mng_info->write_png24)
11354 {
glennrp9c1eb072010-06-06 22:19:15 +000011355 mng_info->write_png_colortype = /* 2 */ 3;
11356 mng_info->write_png_depth = 8;
11357 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011358
cristy8a46d822012-08-28 23:32:39 +000011359 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +000011360 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011361
glennrp9c1eb072010-06-06 22:19:15 +000011362 else
cristy16ea1392012-03-21 20:38:41 +000011363 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011364
cristy16ea1392012-03-21 20:38:41 +000011365 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011366 }
11367
11368 if (mng_info->write_png32)
11369 {
glennrp9c1eb072010-06-06 22:19:15 +000011370 mng_info->write_png_colortype = /* 6 */ 7;
11371 mng_info->write_png_depth = 8;
11372 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011373
cristy8a46d822012-08-28 23:32:39 +000011374 if (image->alpha_trait == BlendPixelTrait)
cristy16ea1392012-03-21 20:38:41 +000011375 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011376
glennrp9c1eb072010-06-06 22:19:15 +000011377 else
cristy16ea1392012-03-21 20:38:41 +000011378 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011379
cristy16ea1392012-03-21 20:38:41 +000011380 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011381 }
11382
glennrpfd164d22013-01-26 21:10:22 +000011383 if (mng_info->write_png48)
11384 {
11385 mng_info->write_png_colortype = /* 2 */ 3;
11386 mng_info->write_png_depth = 16;
11387 image->depth = 16;
11388
glennrp4dda64f2013-01-26 21:20:24 +000011389 if (image->alpha_trait == BlendPixelTrait)
11390 (void) SetImageType(image,TrueColorMatteType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011391
11392 else
glennrp4dda64f2013-01-26 21:20:24 +000011393 (void) SetImageType(image,TrueColorType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011394
glennrp4dda64f2013-01-26 21:20:24 +000011395 (void) SyncImage(image,exception);
glennrpfd164d22013-01-26 21:10:22 +000011396 }
11397
11398 if (mng_info->write_png64)
11399 {
11400 mng_info->write_png_colortype = /* 6 */ 7;
11401 mng_info->write_png_depth = 16;
11402 image->depth = 16;
11403
glennrp4dda64f2013-01-26 21:20:24 +000011404 if (image->alpha_trait == BlendPixelTrait)
11405 (void) SetImageType(image,TrueColorMatteType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011406
11407 else
glennrp4dda64f2013-01-26 21:20:24 +000011408 (void) SetImageType(image,TrueColorType,exception);
glennrpfd164d22013-01-26 21:10:22 +000011409
glennrp4dda64f2013-01-26 21:20:24 +000011410 (void) SyncImage(image,exception);
glennrpfd164d22013-01-26 21:10:22 +000011411 }
11412
cristy3ed852e2009-09-05 21:47:34 +000011413 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011414
cristy3ed852e2009-09-05 21:47:34 +000011415 if (value != (char *) NULL)
11416 {
11417 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011418 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011419
cristy3ed852e2009-09-05 21:47:34 +000011420 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011421 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011422
cristy3ed852e2009-09-05 21:47:34 +000011423 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011424 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011425
cristy3ed852e2009-09-05 21:47:34 +000011426 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011427 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011428
cristy3ed852e2009-09-05 21:47:34 +000011429 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011430 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011431
glennrpbb8a7332010-11-13 15:17:35 +000011432 else
cristy16ea1392012-03-21 20:38:41 +000011433 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011434 GetMagickModule(),CoderWarning,
11435 "ignoring invalid defined png:bit-depth",
11436 "=%s",value);
11437
cristy3ed852e2009-09-05 21:47:34 +000011438 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011440 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011441 }
glennrp0fe50b42010-11-16 03:52:51 +000011442
cristy3ed852e2009-09-05 21:47:34 +000011443 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011444
cristy3ed852e2009-09-05 21:47:34 +000011445 if (value != (char *) NULL)
11446 {
11447 /* We must store colortype+1 because 0 is a valid colortype */
11448 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011449 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011450
cristy16ea1392012-03-21 20:38:41 +000011451 else if (LocaleCompare(value,"1") == 0)
11452 mng_info->write_png_colortype = 2;
11453
cristy3ed852e2009-09-05 21:47:34 +000011454 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011455 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011456
cristy3ed852e2009-09-05 21:47:34 +000011457 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011458 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011459
cristy3ed852e2009-09-05 21:47:34 +000011460 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011461 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011462
cristy3ed852e2009-09-05 21:47:34 +000011463 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011464 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011465
glennrpbb8a7332010-11-13 15:17:35 +000011466 else
cristy16ea1392012-03-21 20:38:41 +000011467 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011468 GetMagickModule(),CoderWarning,
11469 "ignoring invalid defined png:color-type",
11470 "=%s",value);
11471
cristy3ed852e2009-09-05 21:47:34 +000011472 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011474 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011475 }
11476
glennrp0e8ea192010-12-24 18:00:33 +000011477 /* Check for chunks to be excluded:
11478 *
glennrp0dff56c2011-01-29 19:10:02 +000011479 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011480 * listed in the "unused_chunks" array, above.
11481 *
cristy5d6fc9c2011-12-27 03:10:42 +000011482 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011483 * define (in the image properties or in the image artifacts)
11484 * or via a mng_info member. For convenience, in addition
11485 * to or instead of a comma-separated list of chunks, the
11486 * "exclude-chunk" string can be simply "all" or "none".
11487 *
11488 * The exclude-chunk define takes priority over the mng_info.
11489 *
cristy5d6fc9c2011-12-27 03:10:42 +000011490 * A "png:include-chunk" define takes priority over both the
11491 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011492 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011493 * well as a comma-separated list. Chunks that are unknown to
11494 * ImageMagick are always excluded, regardless of their "copy-safe"
11495 * status according to the PNG specification, and even if they
glennrpaa192b12012-01-17 21:35:21 +000011496 * appear in the "include-chunk" list. Such defines appearing among
11497 * the image options take priority over those found among the image
11498 * artifacts.
glennrp0e8ea192010-12-24 18:00:33 +000011499 *
11500 * Finally, all chunks listed in the "unused_chunks" array are
11501 * automatically excluded, regardless of the other instructions
11502 * or lack thereof.
11503 *
11504 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11505 * will not be written and the gAMA chunk will only be written if it
11506 * is not between .45 and .46, or approximately (1.0/2.2).
11507 *
11508 * If you exclude tRNS and the image has transparency, the colortype
11509 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11510 *
11511 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011512 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011513 */
11514
glennrp26f37912010-12-23 16:22:42 +000011515 mng_info->ping_exclude_bKGD=MagickFalse;
11516 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011517 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011518 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11519 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011520 mng_info->ping_exclude_iCCP=MagickFalse;
11521 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11522 mng_info->ping_exclude_oFFs=MagickFalse;
11523 mng_info->ping_exclude_pHYs=MagickFalse;
11524 mng_info->ping_exclude_sRGB=MagickFalse;
11525 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011526 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011527 mng_info->ping_exclude_vpAg=MagickFalse;
11528 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11529 mng_info->ping_exclude_zTXt=MagickFalse;
11530
glennrp8d3d6e52011-04-19 04:39:51 +000011531 mng_info->ping_preserve_colormap=MagickFalse;
11532
11533 value=GetImageArtifact(image,"png:preserve-colormap");
11534 if (value == NULL)
11535 value=GetImageOption(image_info,"png:preserve-colormap");
11536 if (value != NULL)
11537 mng_info->ping_preserve_colormap=MagickTrue;
11538
glennrp18682582011-06-30 18:11:47 +000011539 /* Thes compression-level, compression-strategy, and compression-filter
11540 * defines take precedence over values from the -quality option.
11541 */
11542 value=GetImageArtifact(image,"png:compression-level");
11543 if (value == NULL)
11544 value=GetImageOption(image_info,"png:compression-level");
11545 if (value != NULL)
11546 {
glennrp18682582011-06-30 18:11:47 +000011547 /* We have to add 1 to everything because 0 is a valid input,
11548 * and we want to use 0 (the default) to mean undefined.
11549 */
11550 if (LocaleCompare(value,"0") == 0)
11551 mng_info->write_png_compression_level = 1;
11552
glennrp0ffb95c2012-01-30 21:16:22 +000011553 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011554 mng_info->write_png_compression_level = 2;
11555
11556 else if (LocaleCompare(value,"2") == 0)
11557 mng_info->write_png_compression_level = 3;
11558
11559 else if (LocaleCompare(value,"3") == 0)
11560 mng_info->write_png_compression_level = 4;
11561
11562 else if (LocaleCompare(value,"4") == 0)
11563 mng_info->write_png_compression_level = 5;
11564
11565 else if (LocaleCompare(value,"5") == 0)
11566 mng_info->write_png_compression_level = 6;
11567
11568 else if (LocaleCompare(value,"6") == 0)
11569 mng_info->write_png_compression_level = 7;
11570
11571 else if (LocaleCompare(value,"7") == 0)
11572 mng_info->write_png_compression_level = 8;
11573
11574 else if (LocaleCompare(value,"8") == 0)
11575 mng_info->write_png_compression_level = 9;
11576
11577 else if (LocaleCompare(value,"9") == 0)
11578 mng_info->write_png_compression_level = 10;
11579
11580 else
cristy16ea1392012-03-21 20:38:41 +000011581 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011582 GetMagickModule(),CoderWarning,
11583 "ignoring invalid defined png:compression-level",
11584 "=%s",value);
11585 }
11586
11587 value=GetImageArtifact(image,"png:compression-strategy");
11588 if (value == NULL)
11589 value=GetImageOption(image_info,"png:compression-strategy");
11590 if (value != NULL)
11591 {
11592
11593 if (LocaleCompare(value,"0") == 0)
11594 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11595
11596 else if (LocaleCompare(value,"1") == 0)
11597 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11598
11599 else if (LocaleCompare(value,"2") == 0)
11600 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11601
11602 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011603#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011604 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011605#else
11606 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11607#endif
glennrp18682582011-06-30 18:11:47 +000011608
11609 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011610#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011611 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011612#else
11613 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11614#endif
glennrp18682582011-06-30 18:11:47 +000011615
11616 else
cristy16ea1392012-03-21 20:38:41 +000011617 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011618 GetMagickModule(),CoderWarning,
11619 "ignoring invalid defined png:compression-strategy",
11620 "=%s",value);
11621 }
11622
11623 value=GetImageArtifact(image,"png:compression-filter");
11624 if (value == NULL)
11625 value=GetImageOption(image_info,"png:compression-filter");
11626 if (value != NULL)
11627 {
11628
11629 /* To do: combinations of filters allowed by libpng
11630 * masks 0x08 through 0xf8
11631 *
11632 * Implement this as a comma-separated list of 0,1,2,3,4,5
11633 * where 5 is a special case meaning PNG_ALL_FILTERS.
11634 */
11635
11636 if (LocaleCompare(value,"0") == 0)
11637 mng_info->write_png_compression_filter = 1;
11638
cristyb19b8122012-10-22 11:03:30 +000011639 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011640 mng_info->write_png_compression_filter = 2;
11641
11642 else if (LocaleCompare(value,"2") == 0)
11643 mng_info->write_png_compression_filter = 3;
11644
11645 else if (LocaleCompare(value,"3") == 0)
11646 mng_info->write_png_compression_filter = 4;
11647
11648 else if (LocaleCompare(value,"4") == 0)
11649 mng_info->write_png_compression_filter = 5;
11650
11651 else if (LocaleCompare(value,"5") == 0)
11652 mng_info->write_png_compression_filter = 6;
11653
glennrp18682582011-06-30 18:11:47 +000011654 else
cristy16ea1392012-03-21 20:38:41 +000011655 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011656 GetMagickModule(),CoderWarning,
11657 "ignoring invalid defined png:compression-filter",
11658 "=%s",value);
11659 }
11660
glennrp03812ae2010-12-24 01:31:34 +000011661 excluding=MagickFalse;
11662
glennrp5c7cf4e2010-12-24 00:30:00 +000011663 for (source=0; source<1; source++)
11664 {
11665 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011666 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011667 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011668
11669 if (value == NULL)
11670 value=GetImageArtifact(image,"png:exclude-chunks");
11671 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011672 else
glennrpacba0042010-12-24 14:27:26 +000011673 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011674 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011675
glennrpacba0042010-12-24 14:27:26 +000011676 if (value == NULL)
11677 value=GetImageOption(image_info,"png:exclude-chunks");
11678 }
11679
glennrp03812ae2010-12-24 01:31:34 +000011680 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011681 {
glennrp03812ae2010-12-24 01:31:34 +000011682
11683 size_t
11684 last;
11685
11686 excluding=MagickTrue;
11687
11688 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011689 {
11690 if (source == 0)
11691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11692 " png:exclude-chunk=%s found in image artifacts.\n", value);
11693 else
11694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11695 " png:exclude-chunk=%s found in image properties.\n", value);
11696 }
glennrp03812ae2010-12-24 01:31:34 +000011697
11698 last=strlen(value);
11699
11700 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011701 {
glennrp03812ae2010-12-24 01:31:34 +000011702
11703 if (LocaleNCompare(value+i,"all",3) == 0)
11704 {
11705 mng_info->ping_exclude_bKGD=MagickTrue;
11706 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011707 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011708 mng_info->ping_exclude_EXIF=MagickTrue;
11709 mng_info->ping_exclude_gAMA=MagickTrue;
11710 mng_info->ping_exclude_iCCP=MagickTrue;
11711 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11712 mng_info->ping_exclude_oFFs=MagickTrue;
11713 mng_info->ping_exclude_pHYs=MagickTrue;
11714 mng_info->ping_exclude_sRGB=MagickTrue;
11715 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011716 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011717 mng_info->ping_exclude_vpAg=MagickTrue;
11718 mng_info->ping_exclude_zCCP=MagickTrue;
11719 mng_info->ping_exclude_zTXt=MagickTrue;
11720 i--;
11721 }
glennrp2cc891a2010-12-24 13:44:32 +000011722
glennrp03812ae2010-12-24 01:31:34 +000011723 if (LocaleNCompare(value+i,"none",4) == 0)
11724 {
11725 mng_info->ping_exclude_bKGD=MagickFalse;
11726 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011727 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011728 mng_info->ping_exclude_EXIF=MagickFalse;
11729 mng_info->ping_exclude_gAMA=MagickFalse;
11730 mng_info->ping_exclude_iCCP=MagickFalse;
11731 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11732 mng_info->ping_exclude_oFFs=MagickFalse;
11733 mng_info->ping_exclude_pHYs=MagickFalse;
11734 mng_info->ping_exclude_sRGB=MagickFalse;
11735 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011736 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011737 mng_info->ping_exclude_vpAg=MagickFalse;
11738 mng_info->ping_exclude_zCCP=MagickFalse;
11739 mng_info->ping_exclude_zTXt=MagickFalse;
11740 }
glennrp2cc891a2010-12-24 13:44:32 +000011741
glennrp03812ae2010-12-24 01:31:34 +000011742 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11743 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011744
glennrp03812ae2010-12-24 01:31:34 +000011745 if (LocaleNCompare(value+i,"chrm",4) == 0)
11746 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011747
glennrpa0ed0092011-04-18 16:36:29 +000011748 if (LocaleNCompare(value+i,"date",4) == 0)
11749 mng_info->ping_exclude_date=MagickTrue;
11750
glennrp03812ae2010-12-24 01:31:34 +000011751 if (LocaleNCompare(value+i,"exif",4) == 0)
11752 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011753
glennrp03812ae2010-12-24 01:31:34 +000011754 if (LocaleNCompare(value+i,"gama",4) == 0)
11755 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011756
glennrp03812ae2010-12-24 01:31:34 +000011757 if (LocaleNCompare(value+i,"iccp",4) == 0)
11758 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011759
glennrp03812ae2010-12-24 01:31:34 +000011760 /*
11761 if (LocaleNCompare(value+i,"itxt",4) == 0)
11762 mng_info->ping_exclude_iTXt=MagickTrue;
11763 */
glennrp2cc891a2010-12-24 13:44:32 +000011764
glennrp03812ae2010-12-24 01:31:34 +000011765 if (LocaleNCompare(value+i,"gama",4) == 0)
11766 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011767
glennrp03812ae2010-12-24 01:31:34 +000011768 if (LocaleNCompare(value+i,"offs",4) == 0)
11769 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011770
glennrp03812ae2010-12-24 01:31:34 +000011771 if (LocaleNCompare(value+i,"phys",4) == 0)
11772 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011773
glennrpa1e3b7b2010-12-24 16:37:33 +000011774 if (LocaleNCompare(value+i,"srgb",4) == 0)
11775 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011776
glennrp03812ae2010-12-24 01:31:34 +000011777 if (LocaleNCompare(value+i,"text",4) == 0)
11778 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011779
glennrpa1e3b7b2010-12-24 16:37:33 +000011780 if (LocaleNCompare(value+i,"trns",4) == 0)
11781 mng_info->ping_exclude_tRNS=MagickTrue;
11782
glennrp03812ae2010-12-24 01:31:34 +000011783 if (LocaleNCompare(value+i,"vpag",4) == 0)
11784 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011785
glennrp03812ae2010-12-24 01:31:34 +000011786 if (LocaleNCompare(value+i,"zccp",4) == 0)
11787 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011788
glennrp03812ae2010-12-24 01:31:34 +000011789 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11790 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011791
glennrp03812ae2010-12-24 01:31:34 +000011792 }
glennrpce91ed52010-12-23 22:37:49 +000011793 }
glennrp26f37912010-12-23 16:22:42 +000011794 }
11795
glennrp5c7cf4e2010-12-24 00:30:00 +000011796 for (source=0; source<1; source++)
11797 {
11798 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011799 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011800 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011801
11802 if (value == NULL)
11803 value=GetImageArtifact(image,"png:include-chunks");
11804 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011805 else
glennrpacba0042010-12-24 14:27:26 +000011806 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011807 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011808
glennrpacba0042010-12-24 14:27:26 +000011809 if (value == NULL)
11810 value=GetImageOption(image_info,"png:include-chunks");
11811 }
11812
glennrp03812ae2010-12-24 01:31:34 +000011813 if (value != NULL)
11814 {
11815 size_t
11816 last;
glennrp26f37912010-12-23 16:22:42 +000011817
glennrp03812ae2010-12-24 01:31:34 +000011818 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011819
glennrp03812ae2010-12-24 01:31:34 +000011820 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011821 {
11822 if (source == 0)
11823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11824 " png:include-chunk=%s found in image artifacts.\n", value);
11825 else
11826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11827 " png:include-chunk=%s found in image properties.\n", value);
11828 }
glennrp03812ae2010-12-24 01:31:34 +000011829
11830 last=strlen(value);
11831
11832 for (i=0; i<(int) last; i+=5)
11833 {
11834 if (LocaleNCompare(value+i,"all",3) == 0)
11835 {
11836 mng_info->ping_exclude_bKGD=MagickFalse;
11837 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011838 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011839 mng_info->ping_exclude_EXIF=MagickFalse;
11840 mng_info->ping_exclude_gAMA=MagickFalse;
11841 mng_info->ping_exclude_iCCP=MagickFalse;
11842 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11843 mng_info->ping_exclude_oFFs=MagickFalse;
11844 mng_info->ping_exclude_pHYs=MagickFalse;
11845 mng_info->ping_exclude_sRGB=MagickFalse;
11846 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011847 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011848 mng_info->ping_exclude_vpAg=MagickFalse;
11849 mng_info->ping_exclude_zCCP=MagickFalse;
11850 mng_info->ping_exclude_zTXt=MagickFalse;
11851 i--;
11852 }
glennrp2cc891a2010-12-24 13:44:32 +000011853
glennrp03812ae2010-12-24 01:31:34 +000011854 if (LocaleNCompare(value+i,"none",4) == 0)
11855 {
11856 mng_info->ping_exclude_bKGD=MagickTrue;
11857 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011858 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011859 mng_info->ping_exclude_EXIF=MagickTrue;
11860 mng_info->ping_exclude_gAMA=MagickTrue;
11861 mng_info->ping_exclude_iCCP=MagickTrue;
11862 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11863 mng_info->ping_exclude_oFFs=MagickTrue;
11864 mng_info->ping_exclude_pHYs=MagickTrue;
11865 mng_info->ping_exclude_sRGB=MagickTrue;
11866 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011867 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011868 mng_info->ping_exclude_vpAg=MagickTrue;
11869 mng_info->ping_exclude_zCCP=MagickTrue;
11870 mng_info->ping_exclude_zTXt=MagickTrue;
11871 }
glennrp2cc891a2010-12-24 13:44:32 +000011872
glennrp03812ae2010-12-24 01:31:34 +000011873 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11874 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011875
glennrp03812ae2010-12-24 01:31:34 +000011876 if (LocaleNCompare(value+i,"chrm",4) == 0)
11877 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011878
glennrpa0ed0092011-04-18 16:36:29 +000011879 if (LocaleNCompare(value+i,"date",4) == 0)
11880 mng_info->ping_exclude_date=MagickFalse;
11881
glennrp03812ae2010-12-24 01:31:34 +000011882 if (LocaleNCompare(value+i,"exif",4) == 0)
11883 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011884
glennrp03812ae2010-12-24 01:31:34 +000011885 if (LocaleNCompare(value+i,"gama",4) == 0)
11886 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011887
glennrp03812ae2010-12-24 01:31:34 +000011888 if (LocaleNCompare(value+i,"iccp",4) == 0)
11889 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011890
glennrp03812ae2010-12-24 01:31:34 +000011891 /*
11892 if (LocaleNCompare(value+i,"itxt",4) == 0)
11893 mng_info->ping_exclude_iTXt=MagickFalse;
11894 */
glennrp2cc891a2010-12-24 13:44:32 +000011895
glennrp03812ae2010-12-24 01:31:34 +000011896 if (LocaleNCompare(value+i,"gama",4) == 0)
11897 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011898
glennrp03812ae2010-12-24 01:31:34 +000011899 if (LocaleNCompare(value+i,"offs",4) == 0)
11900 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011901
glennrp03812ae2010-12-24 01:31:34 +000011902 if (LocaleNCompare(value+i,"phys",4) == 0)
11903 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011904
glennrpa1e3b7b2010-12-24 16:37:33 +000011905 if (LocaleNCompare(value+i,"srgb",4) == 0)
11906 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011907
glennrp03812ae2010-12-24 01:31:34 +000011908 if (LocaleNCompare(value+i,"text",4) == 0)
11909 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011910
glennrpa1e3b7b2010-12-24 16:37:33 +000011911 if (LocaleNCompare(value+i,"trns",4) == 0)
11912 mng_info->ping_exclude_tRNS=MagickFalse;
11913
glennrp03812ae2010-12-24 01:31:34 +000011914 if (LocaleNCompare(value+i,"vpag",4) == 0)
11915 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011916
glennrp03812ae2010-12-24 01:31:34 +000011917 if (LocaleNCompare(value+i,"zccp",4) == 0)
11918 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011919
glennrp03812ae2010-12-24 01:31:34 +000011920 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11921 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011922
glennrp03812ae2010-12-24 01:31:34 +000011923 }
glennrpce91ed52010-12-23 22:37:49 +000011924 }
glennrp26f37912010-12-23 16:22:42 +000011925 }
11926
glennrp03812ae2010-12-24 01:31:34 +000011927 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011928 {
11929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011930 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011931 if (mng_info->ping_exclude_bKGD != MagickFalse)
11932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11933 " bKGD");
11934 if (mng_info->ping_exclude_cHRM != MagickFalse)
11935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11936 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011937 if (mng_info->ping_exclude_date != MagickFalse)
11938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11939 " date");
glennrp26f37912010-12-23 16:22:42 +000011940 if (mng_info->ping_exclude_EXIF != MagickFalse)
11941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11942 " EXIF");
11943 if (mng_info->ping_exclude_gAMA != MagickFalse)
11944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11945 " gAMA");
11946 if (mng_info->ping_exclude_iCCP != MagickFalse)
11947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11948 " iCCP");
11949/*
11950 if (mng_info->ping_exclude_iTXt != MagickFalse)
11951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11952 " iTXt");
11953*/
11954 if (mng_info->ping_exclude_oFFs != MagickFalse)
11955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11956 " oFFs");
11957 if (mng_info->ping_exclude_pHYs != MagickFalse)
11958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11959 " pHYs");
11960 if (mng_info->ping_exclude_sRGB != MagickFalse)
11961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11962 " sRGB");
11963 if (mng_info->ping_exclude_tEXt != MagickFalse)
11964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11965 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011966 if (mng_info->ping_exclude_tRNS != MagickFalse)
11967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11968 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011969 if (mng_info->ping_exclude_vpAg != MagickFalse)
11970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11971 " vpAg");
11972 if (mng_info->ping_exclude_zCCP != MagickFalse)
11973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11974 " zCCP");
11975 if (mng_info->ping_exclude_zTXt != MagickFalse)
11976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11977 " zTXt");
11978 }
11979
glennrpb9cfe272010-12-21 15:08:06 +000011980 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011981
cristy16ea1392012-03-21 20:38:41 +000011982 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011983
11984 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011985
cristy3ed852e2009-09-05 21:47:34 +000011986 if (logging != MagickFalse)
11987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011988
cristy3ed852e2009-09-05 21:47:34 +000011989 return(status);
11990}
11991
11992#if defined(JNG_SUPPORTED)
11993
11994/* Write one JNG image */
11995static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +000011996 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011997{
11998 Image
11999 *jpeg_image;
12000
12001 ImageInfo
12002 *jpeg_image_info;
12003
12004 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000012005 logging,
cristy3ed852e2009-09-05 21:47:34 +000012006 status;
12007
12008 size_t
12009 length;
12010
12011 unsigned char
12012 *blob,
12013 chunk[80],
12014 *p;
12015
12016 unsigned int
12017 jng_alpha_compression_method,
12018 jng_alpha_sample_depth,
12019 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000012020 transparent;
12021
cristybb503372010-05-27 20:51:26 +000012022 size_t
glennrp59575fa2011-12-31 21:31:39 +000012023 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000012024 jng_quality;
12025
12026 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000012027 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012028
12029 blob=(unsigned char *) NULL;
12030 jpeg_image=(Image *) NULL;
12031 jpeg_image_info=(ImageInfo *) NULL;
12032
12033 status=MagickTrue;
12034 transparent=image_info->type==GrayscaleMatteType ||
cristy8a46d822012-08-28 23:32:39 +000012035 image_info->type==TrueColorMatteType || image->alpha_trait == BlendPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +000012036
glennrp59575fa2011-12-31 21:31:39 +000012037 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12038
12039 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12040
glennrp750105b2012-04-25 16:20:45 +000012041 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
glennrp59575fa2011-12-31 21:31:39 +000012042 image_info->quality;
12043
12044 if (jng_alpha_quality >= 1000)
12045 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000012046
12047 if (transparent)
12048 {
12049 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000012050
cristy3ed852e2009-09-05 21:47:34 +000012051 /* Create JPEG blob, image, and image_info */
12052 if (logging != MagickFalse)
12053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +000012054 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000012055
cristy3ed852e2009-09-05 21:47:34 +000012056 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000012057
cristy3ed852e2009-09-05 21:47:34 +000012058 if (jpeg_image_info == (ImageInfo *) NULL)
12059 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000012060
cristy3ed852e2009-09-05 21:47:34 +000012061 if (logging != MagickFalse)
12062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12063 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000012064
cristy16ea1392012-03-21 20:38:41 +000012065 jpeg_image=SeparateImage(image,AlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +000012066 if (jpeg_image == (Image *) NULL)
12067 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12068 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy8a46d822012-08-28 23:32:39 +000012069 jpeg_image->alpha_trait=UndefinedPixelTrait;
glennrp8f77fdc2012-03-21 15:16:37 +000012070 jpeg_image->quality=jng_alpha_quality;
cristy16ea1392012-03-21 20:38:41 +000012071 jpeg_image_info->type=GrayscaleType;
12072 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000012073 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012074 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000012075 "%s",jpeg_image->filename);
12076 }
glennrp59575fa2011-12-31 21:31:39 +000012077 else
12078 {
12079 jng_alpha_compression_method=0;
12080 jng_color_type=10;
12081 jng_alpha_sample_depth=0;
12082 }
cristy3ed852e2009-09-05 21:47:34 +000012083
12084 /* To do: check bit depth of PNG alpha channel */
12085
12086 /* Check if image is grayscale. */
12087 if (image_info->type != TrueColorMatteType && image_info->type !=
cristy7fb26522012-06-21 13:02:48 +000012088 TrueColorType && IsImageGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000012089 jng_color_type-=2;
12090
glennrp59575fa2011-12-31 21:31:39 +000012091 if (logging != MagickFalse)
12092 {
12093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12094 " JNG Quality = %d",(int) jng_quality);
12095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12096 " JNG Color Type = %d",jng_color_type);
12097 if (transparent)
12098 {
12099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12100 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12102 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12104 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12105 }
12106 }
12107
cristy3ed852e2009-09-05 21:47:34 +000012108 if (transparent)
12109 {
12110 if (jng_alpha_compression_method==0)
12111 {
12112 const char
12113 *value;
12114
cristy16ea1392012-03-21 20:38:41 +000012115 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000012116 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012117 exception);
cristy3ed852e2009-09-05 21:47:34 +000012118 if (logging != MagickFalse)
12119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12120 " Creating PNG blob.");
12121 length=0;
12122
12123 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12124 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12125 jpeg_image_info->interlace=NoInterlace;
12126
glennrpcc5d45b2012-01-06 04:06:10 +000012127 /* Exclude all ancillary chunks */
12128 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12129
cristy3ed852e2009-09-05 21:47:34 +000012130 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000012131 exception);
cristy3ed852e2009-09-05 21:47:34 +000012132
12133 /* Retrieve sample depth used */
cristy16ea1392012-03-21 20:38:41 +000012134 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000012135 if (value != (char *) NULL)
12136 jng_alpha_sample_depth= (unsigned int) value[0];
12137 }
12138 else
12139 {
cristy16ea1392012-03-21 20:38:41 +000012140 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000012141
12142 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012143 exception);
cristy3ed852e2009-09-05 21:47:34 +000012144
12145 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12146 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12147 jpeg_image_info->interlace=NoInterlace;
12148 if (logging != MagickFalse)
12149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12150 " Creating blob.");
12151 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000012152 exception);
cristy3ed852e2009-09-05 21:47:34 +000012153 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000012154
cristy3ed852e2009-09-05 21:47:34 +000012155 if (logging != MagickFalse)
12156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012157 " Successfully read jpeg_image into a blob, length=%.20g.",
12158 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012159
12160 }
12161 /* Destroy JPEG image and image_info */
12162 jpeg_image=DestroyImage(jpeg_image);
12163 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12164 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12165 }
12166
12167 /* Write JHDR chunk */
12168 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12169 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000012170 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000012171 PNGLong(chunk+4,(png_uint_32) image->columns);
12172 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012173 chunk[12]=jng_color_type;
12174 chunk[13]=8; /* sample depth */
12175 chunk[14]=8; /*jng_image_compression_method */
12176 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12177 chunk[16]=jng_alpha_sample_depth;
12178 chunk[17]=jng_alpha_compression_method;
12179 chunk[18]=0; /*jng_alpha_filter_method */
12180 chunk[19]=0; /*jng_alpha_interlace_method */
12181 (void) WriteBlob(image,20,chunk);
12182 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12183 if (logging != MagickFalse)
12184 {
12185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000012186 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000012187
cristy3ed852e2009-09-05 21:47:34 +000012188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000012189 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000012190
cristy3ed852e2009-09-05 21:47:34 +000012191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12192 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000012193
cristy3ed852e2009-09-05 21:47:34 +000012194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12195 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000012196
cristy3ed852e2009-09-05 21:47:34 +000012197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12198 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000012199
cristy3ed852e2009-09-05 21:47:34 +000012200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12201 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000012202
cristy3ed852e2009-09-05 21:47:34 +000012203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12204 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000012205
cristy3ed852e2009-09-05 21:47:34 +000012206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12207 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000012208
cristy3ed852e2009-09-05 21:47:34 +000012209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12210 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000012211
cristy3ed852e2009-09-05 21:47:34 +000012212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12213 " JNG alpha interlace:%5d",0);
12214 }
12215
glennrp0fe50b42010-11-16 03:52:51 +000012216 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000012217 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000012218
12219 /*
12220 Write leading ancillary chunks
12221 */
12222
12223 if (transparent)
12224 {
12225 /*
12226 Write JNG bKGD chunk
12227 */
12228
12229 unsigned char
12230 blue,
12231 green,
12232 red;
12233
cristybb503372010-05-27 20:51:26 +000012234 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012235 num_bytes;
12236
12237 if (jng_color_type == 8 || jng_color_type == 12)
12238 num_bytes=6L;
12239 else
12240 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000012241 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012242 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012243 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012244 red=ScaleQuantumToChar(image->background_color.red);
12245 green=ScaleQuantumToChar(image->background_color.green);
12246 blue=ScaleQuantumToChar(image->background_color.blue);
12247 *(chunk+4)=0;
12248 *(chunk+5)=red;
12249 *(chunk+6)=0;
12250 *(chunk+7)=green;
12251 *(chunk+8)=0;
12252 *(chunk+9)=blue;
12253 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12254 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12255 }
12256
12257 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12258 {
12259 /*
12260 Write JNG sRGB chunk
12261 */
12262 (void) WriteBlobMSBULong(image,1L);
12263 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012264 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012265
cristy3ed852e2009-09-05 21:47:34 +000012266 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012267 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012268 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012269 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012270
cristy3ed852e2009-09-05 21:47:34 +000012271 else
glennrpe610a072010-08-05 17:08:46 +000012272 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012273 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012274 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012275
cristy3ed852e2009-09-05 21:47:34 +000012276 (void) WriteBlob(image,5,chunk);
12277 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12278 }
12279 else
12280 {
12281 if (image->gamma != 0.0)
12282 {
12283 /*
12284 Write JNG gAMA chunk
12285 */
12286 (void) WriteBlobMSBULong(image,4L);
12287 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012288 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012289 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012290 (void) WriteBlob(image,8,chunk);
12291 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12292 }
glennrp0fe50b42010-11-16 03:52:51 +000012293
cristy3ed852e2009-09-05 21:47:34 +000012294 if ((mng_info->equal_chrms == MagickFalse) &&
12295 (image->chromaticity.red_primary.x != 0.0))
12296 {
12297 PrimaryInfo
12298 primary;
12299
12300 /*
12301 Write JNG cHRM chunk
12302 */
12303 (void) WriteBlobMSBULong(image,32L);
12304 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012305 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012306 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012307 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12308 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012309 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012310 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12311 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012312 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012313 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12314 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012315 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012316 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12317 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012318 (void) WriteBlob(image,36,chunk);
12319 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12320 }
12321 }
glennrp0fe50b42010-11-16 03:52:51 +000012322
cristy16ea1392012-03-21 20:38:41 +000012323 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012324 {
12325 /*
12326 Write JNG pHYs chunk
12327 */
12328 (void) WriteBlobMSBULong(image,9L);
12329 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012330 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000012331 if (image->units == PixelsPerInchResolution)
12332 {
cristy35ef8242010-06-03 16:24:13 +000012333 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012334 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012335
cristy35ef8242010-06-03 16:24:13 +000012336 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012337 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012338
cristy3ed852e2009-09-05 21:47:34 +000012339 chunk[12]=1;
12340 }
glennrp0fe50b42010-11-16 03:52:51 +000012341
cristy3ed852e2009-09-05 21:47:34 +000012342 else
12343 {
12344 if (image->units == PixelsPerCentimeterResolution)
12345 {
cristy35ef8242010-06-03 16:24:13 +000012346 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012347 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012348
cristy35ef8242010-06-03 16:24:13 +000012349 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012350 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012351
cristy3ed852e2009-09-05 21:47:34 +000012352 chunk[12]=1;
12353 }
glennrp0fe50b42010-11-16 03:52:51 +000012354
cristy3ed852e2009-09-05 21:47:34 +000012355 else
12356 {
cristy16ea1392012-03-21 20:38:41 +000012357 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12358 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012359 chunk[12]=0;
12360 }
12361 }
12362 (void) WriteBlob(image,13,chunk);
12363 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12364 }
12365
12366 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12367 {
12368 /*
12369 Write JNG oFFs chunk
12370 */
12371 (void) WriteBlobMSBULong(image,9L);
12372 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000012373 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000012374 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12375 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000012376 chunk[12]=0;
12377 (void) WriteBlob(image,13,chunk);
12378 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12379 }
12380 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12381 {
12382 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12383 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000012384 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012385 PNGLong(chunk+4,(png_uint_32) image->page.width);
12386 PNGLong(chunk+8,(png_uint_32) image->page.height);
12387 chunk[12]=0; /* unit = pixels */
12388 (void) WriteBlob(image,13,chunk);
12389 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12390 }
12391
12392
12393 if (transparent)
12394 {
12395 if (jng_alpha_compression_method==0)
12396 {
cristybb503372010-05-27 20:51:26 +000012397 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012398 i;
12399
cristybb503372010-05-27 20:51:26 +000012400 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012401 len;
12402
12403 /* Write IDAT chunk header */
12404 if (logging != MagickFalse)
12405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012406 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012407 length);
cristy3ed852e2009-09-05 21:47:34 +000012408
12409 /* Copy IDAT chunks */
12410 len=0;
12411 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012412 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012413 {
12414 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12415 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012416
cristy3ed852e2009-09-05 21:47:34 +000012417 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12418 {
12419 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012420 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012421 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012422 (void) WriteBlob(image,(size_t) len+4,p);
12423 (void) WriteBlobMSBULong(image,
12424 crc32(0,p,(uInt) len+4));
12425 }
glennrp0fe50b42010-11-16 03:52:51 +000012426
cristy3ed852e2009-09-05 21:47:34 +000012427 else
12428 {
12429 if (logging != MagickFalse)
12430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012431 " Skipping %c%c%c%c chunk, length=%.20g.",
12432 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012433 }
12434 p+=(8+len);
12435 }
12436 }
12437 else
12438 {
12439 /* Write JDAA chunk header */
12440 if (logging != MagickFalse)
12441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012442 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012443 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012444 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012445 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012446 /* Write JDAT chunk(s) data */
12447 (void) WriteBlob(image,4,chunk);
12448 (void) WriteBlob(image,length,blob);
12449 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12450 (uInt) length));
12451 }
12452 blob=(unsigned char *) RelinquishMagickMemory(blob);
12453 }
12454
12455 /* Encode image as a JPEG blob */
12456 if (logging != MagickFalse)
12457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12458 " Creating jpeg_image_info.");
12459 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12460 if (jpeg_image_info == (ImageInfo *) NULL)
12461 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12462
12463 if (logging != MagickFalse)
12464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12465 " Creating jpeg_image.");
12466
cristy16ea1392012-03-21 20:38:41 +000012467 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012468 if (jpeg_image == (Image *) NULL)
12469 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12470 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12471
12472 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012473 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012474 jpeg_image->filename);
12475
12476 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012477 exception);
cristy3ed852e2009-09-05 21:47:34 +000012478
12479 if (logging != MagickFalse)
12480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012481 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12482 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012483
12484 if (jng_color_type == 8 || jng_color_type == 12)
12485 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012486
glennrp59575fa2011-12-31 21:31:39 +000012487 jpeg_image_info->quality=jng_quality;
12488 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012489 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12490 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012491
cristy3ed852e2009-09-05 21:47:34 +000012492 if (logging != MagickFalse)
12493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12494 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012495
cristy16ea1392012-03-21 20:38:41 +000012496 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012497
cristy3ed852e2009-09-05 21:47:34 +000012498 if (logging != MagickFalse)
12499 {
12500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012501 " Successfully read jpeg_image into a blob, length=%.20g.",
12502 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012503
12504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012505 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012506 }
glennrp0fe50b42010-11-16 03:52:51 +000012507
cristy3ed852e2009-09-05 21:47:34 +000012508 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012509 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012510 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012511 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012512 (void) WriteBlob(image,4,chunk);
12513 (void) WriteBlob(image,length,blob);
12514 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12515
12516 jpeg_image=DestroyImage(jpeg_image);
12517 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12518 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12519 blob=(unsigned char *) RelinquishMagickMemory(blob);
12520
12521 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012522 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012523
12524 /* Write IEND chunk */
12525 (void) WriteBlobMSBULong(image,0L);
12526 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012527 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012528 (void) WriteBlob(image,4,chunk);
12529 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12530
12531 if (logging != MagickFalse)
12532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12533 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012534
cristy3ed852e2009-09-05 21:47:34 +000012535 return(status);
12536}
12537
12538
12539/*
12540%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12541% %
12542% %
12543% %
12544% W r i t e J N G I m a g e %
12545% %
12546% %
12547% %
12548%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12549%
12550% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12551%
12552% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12553%
12554% The format of the WriteJNGImage method is:
12555%
cristy16ea1392012-03-21 20:38:41 +000012556% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12557% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012558%
12559% A description of each parameter follows:
12560%
12561% o image_info: the image info.
12562%
12563% o image: The image.
12564%
cristy16ea1392012-03-21 20:38:41 +000012565% o exception: return any errors or warnings in this structure.
12566%
cristy3ed852e2009-09-05 21:47:34 +000012567%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12568*/
cristy16ea1392012-03-21 20:38:41 +000012569static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12570 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012571{
12572 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012573 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012574 logging,
cristy3ed852e2009-09-05 21:47:34 +000012575 status;
12576
12577 MngInfo
12578 *mng_info;
12579
cristy3ed852e2009-09-05 21:47:34 +000012580 /*
12581 Open image file.
12582 */
12583 assert(image_info != (const ImageInfo *) NULL);
12584 assert(image_info->signature == MagickSignature);
12585 assert(image != (Image *) NULL);
12586 assert(image->signature == MagickSignature);
12587 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012588 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012589 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012590 if (status == MagickFalse)
12591 return(status);
12592
12593 /*
12594 Allocate a MngInfo structure.
12595 */
12596 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012597 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012598 if (mng_info == (MngInfo *) NULL)
12599 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12600 /*
12601 Initialize members of the MngInfo structure.
12602 */
12603 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12604 mng_info->image=image;
12605 have_mng_structure=MagickTrue;
12606
12607 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12608
cristy16ea1392012-03-21 20:38:41 +000012609 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012610 (void) CloseBlob(image);
12611
12612 (void) CatchImageException(image);
12613 MngInfoFreeStruct(mng_info,&have_mng_structure);
12614 if (logging != MagickFalse)
12615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12616 return(status);
12617}
12618#endif
12619
cristy16ea1392012-03-21 20:38:41 +000012620static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12621 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012622{
12623 const char
12624 *option;
12625
12626 Image
12627 *next_image;
12628
12629 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012630 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012631 status;
12632
glennrp03812ae2010-12-24 01:31:34 +000012633 volatile MagickBooleanType
12634 logging;
12635
cristy3ed852e2009-09-05 21:47:34 +000012636 MngInfo
12637 *mng_info;
12638
12639 int
cristy3ed852e2009-09-05 21:47:34 +000012640 image_count,
12641 need_iterations,
12642 need_matte;
12643
12644 volatile int
12645#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12646 defined(PNG_MNG_FEATURES_SUPPORTED)
12647 need_local_plte,
12648#endif
12649 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012650 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012651 use_global_plte;
12652
cristybb503372010-05-27 20:51:26 +000012653 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012654 i;
12655
12656 unsigned char
12657 chunk[800];
12658
12659 volatile unsigned int
12660 write_jng,
12661 write_mng;
12662
cristybb503372010-05-27 20:51:26 +000012663 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012664 scene;
12665
cristybb503372010-05-27 20:51:26 +000012666 size_t
cristy3ed852e2009-09-05 21:47:34 +000012667 final_delay=0,
12668 initial_delay;
12669
glennrpd5045b42010-03-24 12:40:35 +000012670#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012671 if (image_info->verbose)
12672 printf("Your PNG library (libpng-%s) is rather old.\n",
12673 PNG_LIBPNG_VER_STRING);
12674#endif
12675
12676 /*
12677 Open image file.
12678 */
12679 assert(image_info != (const ImageInfo *) NULL);
12680 assert(image_info->signature == MagickSignature);
12681 assert(image != (Image *) NULL);
12682 assert(image->signature == MagickSignature);
12683 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012684 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012685 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012686 if (status == MagickFalse)
12687 return(status);
12688
12689 /*
12690 Allocate a MngInfo structure.
12691 */
12692 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012693 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012694 if (mng_info == (MngInfo *) NULL)
12695 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12696 /*
12697 Initialize members of the MngInfo structure.
12698 */
12699 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12700 mng_info->image=image;
12701 have_mng_structure=MagickTrue;
12702 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12703
12704 /*
12705 * See if user has requested a specific PNG subformat to be used
12706 * for all of the PNGs in the MNG being written, e.g.,
12707 *
12708 * convert *.png png8:animation.mng
12709 *
12710 * To do: check -define png:bit_depth and png:color_type as well,
12711 * or perhaps use mng:bit_depth and mng:color_type instead for
12712 * global settings.
12713 */
12714
12715 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12716 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12717 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12718
12719 write_jng=MagickFalse;
12720 if (image_info->compression == JPEGCompression)
12721 write_jng=MagickTrue;
12722
12723 mng_info->adjoin=image_info->adjoin &&
12724 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12725
cristy3ed852e2009-09-05 21:47:34 +000012726 if (logging != MagickFalse)
12727 {
12728 /* Log some info about the input */
12729 Image
12730 *p;
12731
12732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12733 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012734
cristy3ed852e2009-09-05 21:47:34 +000012735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012736 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012737
cristy3ed852e2009-09-05 21:47:34 +000012738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12739 " Type: %d",image_info->type);
12740
12741 scene=0;
12742 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12743 {
12744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012745 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012746
cristy3ed852e2009-09-05 21:47:34 +000012747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012748 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012749
cristydc2d3272013-02-12 14:00:44 +000012750 if (p->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000012751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12752 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012753
cristy3ed852e2009-09-05 21:47:34 +000012754 else
12755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12756 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012757
cristy3ed852e2009-09-05 21:47:34 +000012758 if (p->storage_class == PseudoClass)
12759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12760 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012761
cristy3ed852e2009-09-05 21:47:34 +000012762 else
12763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12764 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012765
cristy3ed852e2009-09-05 21:47:34 +000012766 if (p->colors)
12767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012768 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012769
cristy3ed852e2009-09-05 21:47:34 +000012770 else
12771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12772 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012773
cristy3ed852e2009-09-05 21:47:34 +000012774 if (mng_info->adjoin == MagickFalse)
12775 break;
12776 }
12777 }
12778
cristy3ed852e2009-09-05 21:47:34 +000012779 use_global_plte=MagickFalse;
12780 all_images_are_gray=MagickFalse;
12781#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12782 need_local_plte=MagickTrue;
12783#endif
12784 need_defi=MagickFalse;
12785 need_matte=MagickFalse;
12786 mng_info->framing_mode=1;
12787 mng_info->old_framing_mode=1;
12788
12789 if (write_mng)
12790 if (image_info->page != (char *) NULL)
12791 {
12792 /*
12793 Determine image bounding box.
12794 */
12795 SetGeometry(image,&mng_info->page);
12796 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12797 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12798 }
12799 if (write_mng)
12800 {
12801 unsigned int
12802 need_geom;
12803
12804 unsigned short
12805 red,
12806 green,
12807 blue;
12808
12809 mng_info->page=image->page;
12810 need_geom=MagickTrue;
12811 if (mng_info->page.width || mng_info->page.height)
12812 need_geom=MagickFalse;
12813 /*
12814 Check all the scenes.
12815 */
12816 initial_delay=image->delay;
12817 need_iterations=MagickFalse;
12818 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12819 mng_info->equal_physs=MagickTrue,
12820 mng_info->equal_gammas=MagickTrue;
12821 mng_info->equal_srgbs=MagickTrue;
12822 mng_info->equal_backgrounds=MagickTrue;
12823 image_count=0;
12824#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12825 defined(PNG_MNG_FEATURES_SUPPORTED)
12826 all_images_are_gray=MagickTrue;
12827 mng_info->equal_palettes=MagickFalse;
12828 need_local_plte=MagickFalse;
12829#endif
12830 for (next_image=image; next_image != (Image *) NULL; )
12831 {
12832 if (need_geom)
12833 {
12834 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12835 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012836
cristy3ed852e2009-09-05 21:47:34 +000012837 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12838 mng_info->page.height=next_image->rows+next_image->page.y;
12839 }
glennrp0fe50b42010-11-16 03:52:51 +000012840
cristy3ed852e2009-09-05 21:47:34 +000012841 if (next_image->page.x || next_image->page.y)
12842 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012843
cristydc2d3272013-02-12 14:00:44 +000012844 if (next_image->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000012845 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012846
cristy3ed852e2009-09-05 21:47:34 +000012847 if ((int) next_image->dispose >= BackgroundDispose)
cristydc2d3272013-02-12 14:00:44 +000012848 if ((next_image->alpha_trait == BlendPixelTrait) ||
12849 next_image->page.x || next_image->page.y ||
cristy3ed852e2009-09-05 21:47:34 +000012850 ((next_image->columns < mng_info->page.width) &&
12851 (next_image->rows < mng_info->page.height)))
12852 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012853
cristy3ed852e2009-09-05 21:47:34 +000012854 if (next_image->iterations)
12855 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012856
cristy3ed852e2009-09-05 21:47:34 +000012857 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012858
cristy3ed852e2009-09-05 21:47:34 +000012859 if (final_delay != initial_delay || final_delay > 1UL*
12860 next_image->ticks_per_second)
12861 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012862
cristy3ed852e2009-09-05 21:47:34 +000012863#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12864 defined(PNG_MNG_FEATURES_SUPPORTED)
12865 /*
12866 check for global palette possibility.
12867 */
cristy8a46d822012-08-28 23:32:39 +000012868 if (image->alpha_trait == BlendPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +000012869 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012870
cristy3ed852e2009-09-05 21:47:34 +000012871 if (need_local_plte == 0)
12872 {
cristy7fb26522012-06-21 13:02:48 +000012873 if (IsImageGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012874 all_images_are_gray=MagickFalse;
12875 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12876 if (use_global_plte == 0)
12877 use_global_plte=mng_info->equal_palettes;
12878 need_local_plte=!mng_info->equal_palettes;
12879 }
12880#endif
12881 if (GetNextImageInList(next_image) != (Image *) NULL)
12882 {
12883 if (next_image->background_color.red !=
12884 next_image->next->background_color.red ||
12885 next_image->background_color.green !=
12886 next_image->next->background_color.green ||
12887 next_image->background_color.blue !=
12888 next_image->next->background_color.blue)
12889 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012890
cristy3ed852e2009-09-05 21:47:34 +000012891 if (next_image->gamma != next_image->next->gamma)
12892 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012893
cristy3ed852e2009-09-05 21:47:34 +000012894 if (next_image->rendering_intent !=
12895 next_image->next->rendering_intent)
12896 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012897
cristy3ed852e2009-09-05 21:47:34 +000012898 if ((next_image->units != next_image->next->units) ||
cristy16ea1392012-03-21 20:38:41 +000012899 (next_image->resolution.x != next_image->next->resolution.x) ||
12900 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012901 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012902
cristy3ed852e2009-09-05 21:47:34 +000012903 if (mng_info->equal_chrms)
12904 {
12905 if (next_image->chromaticity.red_primary.x !=
12906 next_image->next->chromaticity.red_primary.x ||
12907 next_image->chromaticity.red_primary.y !=
12908 next_image->next->chromaticity.red_primary.y ||
12909 next_image->chromaticity.green_primary.x !=
12910 next_image->next->chromaticity.green_primary.x ||
12911 next_image->chromaticity.green_primary.y !=
12912 next_image->next->chromaticity.green_primary.y ||
12913 next_image->chromaticity.blue_primary.x !=
12914 next_image->next->chromaticity.blue_primary.x ||
12915 next_image->chromaticity.blue_primary.y !=
12916 next_image->next->chromaticity.blue_primary.y ||
12917 next_image->chromaticity.white_point.x !=
12918 next_image->next->chromaticity.white_point.x ||
12919 next_image->chromaticity.white_point.y !=
12920 next_image->next->chromaticity.white_point.y)
12921 mng_info->equal_chrms=MagickFalse;
12922 }
12923 }
12924 image_count++;
12925 next_image=GetNextImageInList(next_image);
12926 }
12927 if (image_count < 2)
12928 {
12929 mng_info->equal_backgrounds=MagickFalse;
12930 mng_info->equal_chrms=MagickFalse;
12931 mng_info->equal_gammas=MagickFalse;
12932 mng_info->equal_srgbs=MagickFalse;
12933 mng_info->equal_physs=MagickFalse;
12934 use_global_plte=MagickFalse;
12935#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12936 need_local_plte=MagickTrue;
12937#endif
12938 need_iterations=MagickFalse;
12939 }
glennrp0fe50b42010-11-16 03:52:51 +000012940
cristy3ed852e2009-09-05 21:47:34 +000012941 if (mng_info->need_fram == MagickFalse)
12942 {
12943 /*
12944 Only certain framing rates 100/n are exactly representable without
12945 the FRAM chunk but we'll allow some slop in VLC files
12946 */
12947 if (final_delay == 0)
12948 {
12949 if (need_iterations != MagickFalse)
12950 {
12951 /*
12952 It's probably a GIF with loop; don't run it *too* fast.
12953 */
glennrp02617122010-07-28 13:07:35 +000012954 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012955 {
12956 final_delay=10;
cristy16ea1392012-03-21 20:38:41 +000012957 (void) ThrowMagickException(exception,GetMagickModule(),
12958 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012959 "input has zero delay between all frames; assuming",
12960 " 10 cs `%s'","");
12961 }
cristy3ed852e2009-09-05 21:47:34 +000012962 }
12963 else
12964 mng_info->ticks_per_second=0;
12965 }
12966 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012967 mng_info->ticks_per_second=(png_uint_32)
12968 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012969 if (final_delay > 50)
12970 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012971
cristy3ed852e2009-09-05 21:47:34 +000012972 if (final_delay > 75)
12973 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012974
cristy3ed852e2009-09-05 21:47:34 +000012975 if (final_delay > 125)
12976 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012977
cristy3ed852e2009-09-05 21:47:34 +000012978 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12979 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12980 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12981 1UL*image->ticks_per_second))
12982 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12983 }
glennrp0fe50b42010-11-16 03:52:51 +000012984
cristy3ed852e2009-09-05 21:47:34 +000012985 if (mng_info->need_fram != MagickFalse)
12986 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12987 /*
12988 If pseudocolor, we should also check to see if all the
12989 palettes are identical and write a global PLTE if they are.
12990 ../glennrp Feb 99.
12991 */
12992 /*
12993 Write the MNG version 1.0 signature and MHDR chunk.
12994 */
12995 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12996 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12997 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012998 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012999 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13000 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000013001 PNGLong(chunk+12,mng_info->ticks_per_second);
13002 PNGLong(chunk+16,0L); /* layer count=unknown */
13003 PNGLong(chunk+20,0L); /* frame count=unknown */
13004 PNGLong(chunk+24,0L); /* play time=unknown */
13005 if (write_jng)
13006 {
13007 if (need_matte)
13008 {
13009 if (need_defi || mng_info->need_fram || use_global_plte)
13010 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000013011
cristy3ed852e2009-09-05 21:47:34 +000013012 else
13013 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13014 }
glennrp0fe50b42010-11-16 03:52:51 +000013015
cristy3ed852e2009-09-05 21:47:34 +000013016 else
13017 {
13018 if (need_defi || mng_info->need_fram || use_global_plte)
13019 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000013020
cristy3ed852e2009-09-05 21:47:34 +000013021 else
13022 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13023 }
13024 }
glennrp0fe50b42010-11-16 03:52:51 +000013025
cristy3ed852e2009-09-05 21:47:34 +000013026 else
13027 {
13028 if (need_matte)
13029 {
13030 if (need_defi || mng_info->need_fram || use_global_plte)
13031 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000013032
cristy3ed852e2009-09-05 21:47:34 +000013033 else
13034 PNGLong(chunk+28,9L); /* simplicity=VLC */
13035 }
glennrp0fe50b42010-11-16 03:52:51 +000013036
cristy3ed852e2009-09-05 21:47:34 +000013037 else
13038 {
13039 if (need_defi || mng_info->need_fram || use_global_plte)
13040 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000013041
cristy3ed852e2009-09-05 21:47:34 +000013042 else
13043 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13044 }
13045 }
13046 (void) WriteBlob(image,32,chunk);
13047 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13048 option=GetImageOption(image_info,"mng:need-cacheoff");
13049 if (option != (const char *) NULL)
13050 {
13051 size_t
13052 length;
13053
13054 /*
13055 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13056 */
13057 PNGType(chunk,mng_nEED);
13058 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000013059 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000013060 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000013061 length+=4;
13062 (void) WriteBlob(image,length,chunk);
13063 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13064 }
13065 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13066 (GetNextImageInList(image) != (Image *) NULL) &&
13067 (image->iterations != 1))
13068 {
13069 /*
13070 Write MNG TERM chunk
13071 */
13072 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13073 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000013074 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013075 chunk[4]=3; /* repeat animation */
13076 chunk[5]=0; /* show last frame when done */
13077 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13078 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000013079
cristy3ed852e2009-09-05 21:47:34 +000013080 if (image->iterations == 0)
13081 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000013082
cristy3ed852e2009-09-05 21:47:34 +000013083 else
13084 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000013085
cristy3ed852e2009-09-05 21:47:34 +000013086 if (logging != MagickFalse)
13087 {
13088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013089 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13090 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000013091
cristy3ed852e2009-09-05 21:47:34 +000013092 if (image->iterations == 0)
13093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013094 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000013095
cristy3ed852e2009-09-05 21:47:34 +000013096 else
13097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000013098 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000013099 }
13100 (void) WriteBlob(image,14,chunk);
13101 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13102 }
13103 /*
13104 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13105 */
13106 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13107 mng_info->equal_srgbs)
13108 {
13109 /*
13110 Write MNG sRGB chunk
13111 */
13112 (void) WriteBlobMSBULong(image,1L);
13113 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000013114 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000013115
cristy3ed852e2009-09-05 21:47:34 +000013116 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000013117 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000013118 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000013119 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000013120
cristy3ed852e2009-09-05 21:47:34 +000013121 else
glennrpe610a072010-08-05 17:08:46 +000013122 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000013123 Magick_RenderingIntent_to_PNG_RenderingIntent(
13124 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000013125
cristy3ed852e2009-09-05 21:47:34 +000013126 (void) WriteBlob(image,5,chunk);
13127 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13128 mng_info->have_write_global_srgb=MagickTrue;
13129 }
glennrp0fe50b42010-11-16 03:52:51 +000013130
cristy3ed852e2009-09-05 21:47:34 +000013131 else
13132 {
13133 if (image->gamma && mng_info->equal_gammas)
13134 {
13135 /*
13136 Write MNG gAMA chunk
13137 */
13138 (void) WriteBlobMSBULong(image,4L);
13139 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000013140 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000013141 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013142 (void) WriteBlob(image,8,chunk);
13143 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13144 mng_info->have_write_global_gama=MagickTrue;
13145 }
13146 if (mng_info->equal_chrms)
13147 {
13148 PrimaryInfo
13149 primary;
13150
13151 /*
13152 Write MNG cHRM chunk
13153 */
13154 (void) WriteBlobMSBULong(image,32L);
13155 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000013156 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000013157 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000013158 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13159 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013160 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000013161 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13162 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013163 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000013164 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13165 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013166 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000013167 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13168 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013169 (void) WriteBlob(image,36,chunk);
13170 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13171 mng_info->have_write_global_chrm=MagickTrue;
13172 }
13173 }
cristy16ea1392012-03-21 20:38:41 +000013174 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000013175 {
13176 /*
13177 Write MNG pHYs chunk
13178 */
13179 (void) WriteBlobMSBULong(image,9L);
13180 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000013181 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000013182
cristy3ed852e2009-09-05 21:47:34 +000013183 if (image->units == PixelsPerInchResolution)
13184 {
cristy35ef8242010-06-03 16:24:13 +000013185 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013186 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013187
cristy35ef8242010-06-03 16:24:13 +000013188 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013189 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013190
cristy3ed852e2009-09-05 21:47:34 +000013191 chunk[12]=1;
13192 }
glennrp0fe50b42010-11-16 03:52:51 +000013193
cristy3ed852e2009-09-05 21:47:34 +000013194 else
13195 {
13196 if (image->units == PixelsPerCentimeterResolution)
13197 {
cristy35ef8242010-06-03 16:24:13 +000013198 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013199 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013200
cristy35ef8242010-06-03 16:24:13 +000013201 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000013202 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000013203
cristy3ed852e2009-09-05 21:47:34 +000013204 chunk[12]=1;
13205 }
glennrp0fe50b42010-11-16 03:52:51 +000013206
cristy3ed852e2009-09-05 21:47:34 +000013207 else
13208 {
cristy16ea1392012-03-21 20:38:41 +000013209 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13210 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000013211 chunk[12]=0;
13212 }
13213 }
13214 (void) WriteBlob(image,13,chunk);
13215 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13216 }
13217 /*
13218 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13219 or does not cover the entire frame.
13220 */
cristydc2d3272013-02-12 14:00:44 +000013221 if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
13222 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
cristy3ed852e2009-09-05 21:47:34 +000013223 (image->page.width+image->page.x < mng_info->page.width))
13224 || (image->page.height && (image->page.height+image->page.y
13225 < mng_info->page.height))))
13226 {
13227 (void) WriteBlobMSBULong(image,6L);
13228 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000013229 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000013230 red=ScaleQuantumToShort(image->background_color.red);
13231 green=ScaleQuantumToShort(image->background_color.green);
13232 blue=ScaleQuantumToShort(image->background_color.blue);
13233 PNGShort(chunk+4,red);
13234 PNGShort(chunk+6,green);
13235 PNGShort(chunk+8,blue);
13236 (void) WriteBlob(image,10,chunk);
13237 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13238 if (mng_info->equal_backgrounds)
13239 {
13240 (void) WriteBlobMSBULong(image,6L);
13241 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000013242 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000013243 (void) WriteBlob(image,10,chunk);
13244 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13245 }
13246 }
13247
13248#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13249 if ((need_local_plte == MagickFalse) &&
13250 (image->storage_class == PseudoClass) &&
13251 (all_images_are_gray == MagickFalse))
13252 {
cristybb503372010-05-27 20:51:26 +000013253 size_t
cristy3ed852e2009-09-05 21:47:34 +000013254 data_length;
13255
13256 /*
13257 Write MNG PLTE chunk
13258 */
13259 data_length=3*image->colors;
13260 (void) WriteBlobMSBULong(image,data_length);
13261 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013262 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013263
cristybb503372010-05-27 20:51:26 +000013264 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013265 {
cristy16ea1392012-03-21 20:38:41 +000013266 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13267 image->colormap[i].red) & 0xff);
13268 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13269 image->colormap[i].green) & 0xff);
13270 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13271 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000013272 }
glennrp0fe50b42010-11-16 03:52:51 +000013273
cristy3ed852e2009-09-05 21:47:34 +000013274 (void) WriteBlob(image,data_length+4,chunk);
13275 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13276 mng_info->have_write_global_plte=MagickTrue;
13277 }
13278#endif
13279 }
13280 scene=0;
13281 mng_info->delay=0;
13282#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13283 defined(PNG_MNG_FEATURES_SUPPORTED)
13284 mng_info->equal_palettes=MagickFalse;
13285#endif
13286 do
13287 {
13288 if (mng_info->adjoin)
13289 {
13290#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13291 defined(PNG_MNG_FEATURES_SUPPORTED)
13292 /*
13293 If we aren't using a global palette for the entire MNG, check to
13294 see if we can use one for two or more consecutive images.
13295 */
13296 if (need_local_plte && use_global_plte && !all_images_are_gray)
13297 {
13298 if (mng_info->IsPalette)
13299 {
13300 /*
13301 When equal_palettes is true, this image has the same palette
13302 as the previous PseudoClass image
13303 */
13304 mng_info->have_write_global_plte=mng_info->equal_palettes;
13305 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13306 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13307 {
13308 /*
13309 Write MNG PLTE chunk
13310 */
cristybb503372010-05-27 20:51:26 +000013311 size_t
cristy3ed852e2009-09-05 21:47:34 +000013312 data_length;
13313
13314 data_length=3*image->colors;
13315 (void) WriteBlobMSBULong(image,data_length);
13316 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013317 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013318
cristybb503372010-05-27 20:51:26 +000013319 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013320 {
13321 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13322 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13323 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13324 }
glennrp0fe50b42010-11-16 03:52:51 +000013325
cristy3ed852e2009-09-05 21:47:34 +000013326 (void) WriteBlob(image,data_length+4,chunk);
13327 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13328 (uInt) (data_length+4)));
13329 mng_info->have_write_global_plte=MagickTrue;
13330 }
13331 }
13332 else
13333 mng_info->have_write_global_plte=MagickFalse;
13334 }
13335#endif
13336 if (need_defi)
13337 {
cristybb503372010-05-27 20:51:26 +000013338 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000013339 previous_x,
13340 previous_y;
13341
13342 if (scene)
13343 {
13344 previous_x=mng_info->page.x;
13345 previous_y=mng_info->page.y;
13346 }
13347 else
13348 {
13349 previous_x=0;
13350 previous_y=0;
13351 }
13352 mng_info->page=image->page;
13353 if ((mng_info->page.x != previous_x) ||
13354 (mng_info->page.y != previous_y))
13355 {
13356 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13357 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000013358 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000013359 chunk[4]=0; /* object 0 MSB */
13360 chunk[5]=0; /* object 0 LSB */
13361 chunk[6]=0; /* visible */
13362 chunk[7]=0; /* abstract */
13363 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13364 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13365 (void) WriteBlob(image,16,chunk);
13366 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13367 }
13368 }
13369 }
13370
13371 mng_info->write_mng=write_mng;
13372
13373 if ((int) image->dispose >= 3)
13374 mng_info->framing_mode=3;
13375
13376 if (mng_info->need_fram && mng_info->adjoin &&
13377 ((image->delay != mng_info->delay) ||
13378 (mng_info->framing_mode != mng_info->old_framing_mode)))
13379 {
13380 if (image->delay == mng_info->delay)
13381 {
13382 /*
13383 Write a MNG FRAM chunk with the new framing mode.
13384 */
13385 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13386 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013387 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013388 chunk[4]=(unsigned char) mng_info->framing_mode;
13389 (void) WriteBlob(image,5,chunk);
13390 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13391 }
13392 else
13393 {
13394 /*
13395 Write a MNG FRAM chunk with the delay.
13396 */
13397 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13398 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013399 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013400 chunk[4]=(unsigned char) mng_info->framing_mode;
13401 chunk[5]=0; /* frame name separator (no name) */
13402 chunk[6]=2; /* flag for changing default delay */
13403 chunk[7]=0; /* flag for changing frame timeout */
13404 chunk[8]=0; /* flag for changing frame clipping */
13405 chunk[9]=0; /* flag for changing frame sync_id */
13406 PNGLong(chunk+10,(png_uint_32)
13407 ((mng_info->ticks_per_second*
13408 image->delay)/MagickMax(image->ticks_per_second,1)));
13409 (void) WriteBlob(image,14,chunk);
13410 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013411 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013412 }
13413 mng_info->old_framing_mode=mng_info->framing_mode;
13414 }
13415
13416#if defined(JNG_SUPPORTED)
13417 if (image_info->compression == JPEGCompression)
13418 {
13419 ImageInfo
13420 *write_info;
13421
13422 if (logging != MagickFalse)
13423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13424 " Writing JNG object.");
13425 /* To do: specify the desired alpha compression method. */
13426 write_info=CloneImageInfo(image_info);
13427 write_info->compression=UndefinedCompression;
cristy16ea1392012-03-21 20:38:41 +000013428 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013429 write_info=DestroyImageInfo(write_info);
13430 }
13431 else
13432#endif
13433 {
13434 if (logging != MagickFalse)
13435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13436 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013437
glennrpb9cfe272010-12-21 15:08:06 +000013438 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013439 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013440
13441 /* We don't want any ancillary chunks written */
13442 mng_info->ping_exclude_bKGD=MagickTrue;
13443 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013444 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013445 mng_info->ping_exclude_EXIF=MagickTrue;
13446 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013447 mng_info->ping_exclude_iCCP=MagickTrue;
13448 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13449 mng_info->ping_exclude_oFFs=MagickTrue;
13450 mng_info->ping_exclude_pHYs=MagickTrue;
13451 mng_info->ping_exclude_sRGB=MagickTrue;
13452 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013453 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013454 mng_info->ping_exclude_vpAg=MagickTrue;
13455 mng_info->ping_exclude_zCCP=MagickTrue;
13456 mng_info->ping_exclude_zTXt=MagickTrue;
13457
cristy16ea1392012-03-21 20:38:41 +000013458 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013459 }
13460
13461 if (status == MagickFalse)
13462 {
13463 MngInfoFreeStruct(mng_info,&have_mng_structure);
13464 (void) CloseBlob(image);
13465 return(MagickFalse);
13466 }
13467 (void) CatchImageException(image);
13468 if (GetNextImageInList(image) == (Image *) NULL)
13469 break;
13470 image=SyncNextImageInList(image);
13471 status=SetImageProgress(image,SaveImagesTag,scene++,
13472 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013473
cristy3ed852e2009-09-05 21:47:34 +000013474 if (status == MagickFalse)
13475 break;
glennrp0fe50b42010-11-16 03:52:51 +000013476
cristy3ed852e2009-09-05 21:47:34 +000013477 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013478
cristy3ed852e2009-09-05 21:47:34 +000013479 if (write_mng)
13480 {
13481 while (GetPreviousImageInList(image) != (Image *) NULL)
13482 image=GetPreviousImageInList(image);
13483 /*
13484 Write the MEND chunk.
13485 */
13486 (void) WriteBlobMSBULong(image,0x00000000L);
13487 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013488 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013489 (void) WriteBlob(image,4,chunk);
13490 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13491 }
13492 /*
13493 Relinquish resources.
13494 */
13495 (void) CloseBlob(image);
13496 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013497
cristy3ed852e2009-09-05 21:47:34 +000013498 if (logging != MagickFalse)
13499 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013500
cristy3ed852e2009-09-05 21:47:34 +000013501 return(MagickTrue);
13502}
glennrpd5045b42010-03-24 12:40:35 +000013503#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013504
cristy3ed852e2009-09-05 21:47:34 +000013505static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13506{
glennrp3bd393f2011-12-21 18:54:53 +000013507 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013508 printf("Your PNG library is too old: You have libpng-%s\n",
13509 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013510
cristy3ed852e2009-09-05 21:47:34 +000013511 ThrowBinaryException(CoderError,"PNG library is too old",
13512 image_info->filename);
13513}
glennrp39992b42010-11-14 00:03:43 +000013514
cristy3ed852e2009-09-05 21:47:34 +000013515static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13516{
13517 return(WritePNGImage(image_info,image));
13518}
glennrpd5045b42010-03-24 12:40:35 +000013519#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013520#endif