blob: 8d92d1ffb265ea5c59bfc0a2c512b70baf103d98 [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% %
cristy1454be72011-12-19 01:52:48 +000021% Copyright 1999-2012 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) \
cristy16ea1392012-03-21 20:38:41 +0000170 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000171 0 : QuantumRange);
172
glennrp54cf7972011-08-06 14:28:09 +0000173#define LBR01PixelGreen(pixel) \
cristy16ea1392012-03-21 20:38:41 +0000174 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000175 0 : QuantumRange);
176
glennrp54cf7972011-08-06 14:28:09 +0000177#define LBR01PixelBlue(pixel) \
cristy16ea1392012-03-21 20:38:41 +0000178 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000179 0 : QuantumRange);
180
cristy16ea1392012-03-21 20:38:41 +0000181#define LBR01PixelAlpha(pixel) \
182 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000183 0 : QuantumRange);
184
glennrp54cf7972011-08-06 14:28:09 +0000185#define LBR01PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000186 { \
cristyef618312011-06-25 12:26:44 +0000187 LBR01PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000188 LBR01PixelGreen((pixel)); \
189 LBR01PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000190 }
glennrp8e58efd2011-05-20 12:16:29 +0000191
cristy16ea1392012-03-21 20:38:41 +0000192#define LBR01PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000193 { \
glennrp54cf7972011-08-06 14:28:09 +0000194 LBR01PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000195 LBR01PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000196 }
glennrp8e58efd2011-05-20 12:16:29 +0000197
198/* LBR02: Replicate top 2 bits */
199
glennrp05001c32011-08-06 13:04:16 +0000200#define LBR02PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000201 { \
202 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
203 (pixelpacket).red=ScaleCharToQuantum( \
204 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
205 }
glennrp91d99252011-06-25 14:30:13 +0000206#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000207 { \
208 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
209 (pixelpacket).green=ScaleCharToQuantum( \
210 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
211 }
glennrp91d99252011-06-25 14:30:13 +0000212#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000213 { \
214 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
215 (pixelpacket).blue=ScaleCharToQuantum( \
216 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
217 }
cristy16ea1392012-03-21 20:38:41 +0000218#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000219 { \
cristy16ea1392012-03-21 20:38:41 +0000220 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
221 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000222 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
223 }
224
glennrp91d99252011-06-25 14:30:13 +0000225#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000226 { \
glennrp05001c32011-08-06 13:04:16 +0000227 LBR02PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000228 LBR02PacketGreen((pixelpacket)); \
229 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000230 }
glennrp8e58efd2011-05-20 12:16:29 +0000231
glennrp91d99252011-06-25 14:30:13 +0000232#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000233 { \
glennrp91d99252011-06-25 14:30:13 +0000234 LBR02PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000235 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000236 }
glennrp8e58efd2011-05-20 12:16:29 +0000237
cristyef618312011-06-25 12:26:44 +0000238#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000239 { \
cristy16ea1392012-03-21 20:38:41 +0000240 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000241 & 0xc0; \
cristy16ea1392012-03-21 20:38:41 +0000242 SetPixelRed(image, ScaleCharToQuantum( \
243 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
244 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000245 }
glennrp54cf7972011-08-06 14:28:09 +0000246#define LBR02PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000247 { \
cristy16ea1392012-03-21 20:38:41 +0000248 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000249 & 0xc0; \
cristy16ea1392012-03-21 20:38:41 +0000250 SetPixelGreen(image, ScaleCharToQuantum( \
251 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
252 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000253 }
glennrp54cf7972011-08-06 14:28:09 +0000254#define LBR02PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000255 { \
256 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000257 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
258 SetPixelBlue(image, ScaleCharToQuantum( \
259 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
260 (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000261 }
cristy16ea1392012-03-21 20:38:41 +0000262#define LBR02PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000263 { \
264 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000265 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
266 SetPixelAlpha(image, ScaleCharToQuantum( \
267 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
268 (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000269 }
270
glennrp54cf7972011-08-06 14:28:09 +0000271#define LBR02PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000272 { \
cristyef618312011-06-25 12:26:44 +0000273 LBR02PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000274 LBR02PixelGreen((pixel)); \
275 LBR02PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000276 }
glennrp8e58efd2011-05-20 12:16:29 +0000277
cristy16ea1392012-03-21 20:38:41 +0000278#define LBR02PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000279 { \
glennrp54cf7972011-08-06 14:28:09 +0000280 LBR02PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000281 LBR02PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000282 }
glennrp8e58efd2011-05-20 12:16:29 +0000283
284/* LBR03: Replicate top 3 bits (only used with opaque pixels during
285 PNG8 quantization) */
286
glennrp05001c32011-08-06 13:04:16 +0000287#define LBR03PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000288 { \
289 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
290 (pixelpacket).red=ScaleCharToQuantum( \
291 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
292 }
glennrp91d99252011-06-25 14:30:13 +0000293#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000294 { \
295 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
296 (pixelpacket).green=ScaleCharToQuantum( \
297 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
298 }
glennrp91d99252011-06-25 14:30:13 +0000299#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000300 { \
301 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
302 (pixelpacket).blue=ScaleCharToQuantum( \
303 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
304 }
305
glennrp91d99252011-06-25 14:30:13 +0000306#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000307 { \
glennrp05001c32011-08-06 13:04:16 +0000308 LBR03PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000309 LBR03PacketGreen((pixelpacket)); \
310 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000311 }
glennrp8e58efd2011-05-20 12:16:29 +0000312
cristyef618312011-06-25 12:26:44 +0000313#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000314 { \
cristy16ea1392012-03-21 20:38:41 +0000315 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000316 & 0xe0; \
cristy16ea1392012-03-21 20:38:41 +0000317 SetPixelRed(image, ScaleCharToQuantum( \
318 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000319 }
cristy16ea1392012-03-21 20:38:41 +0000320#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000321 { \
cristy16ea1392012-03-21 20:38:41 +0000322 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000323 & 0xe0; \
cristy16ea1392012-03-21 20:38:41 +0000324 SetPixelGreen(image, ScaleCharToQuantum( \
325 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000326 }
cristy16ea1392012-03-21 20:38:41 +0000327#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000328 { \
cristy16ea1392012-03-21 20:38:41 +0000329 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000330 & 0xe0; \
cristy16ea1392012-03-21 20:38:41 +0000331 SetPixelBlue(image, ScaleCharToQuantum( \
332 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000333 }
334
cristy16ea1392012-03-21 20:38:41 +0000335#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000336 { \
cristyef618312011-06-25 12:26:44 +0000337 LBR03PixelRed((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000338 LBR03Green((pixel)); \
339 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000340 }
glennrp8e58efd2011-05-20 12:16:29 +0000341
342/* LBR04: Replicate top 4 bits */
343
glennrp05001c32011-08-06 13:04:16 +0000344#define LBR04PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000345 { \
346 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
347 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
348 }
glennrp91d99252011-06-25 14:30:13 +0000349#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000350 { \
351 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
352 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
353 }
glennrp91d99252011-06-25 14:30:13 +0000354#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000355 { \
356 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
357 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
358 }
cristy16ea1392012-03-21 20:38:41 +0000359#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000360 { \
cristy16ea1392012-03-21 20:38:41 +0000361 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
362 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000363 }
364
glennrp91d99252011-06-25 14:30:13 +0000365#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000366 { \
glennrp05001c32011-08-06 13:04:16 +0000367 LBR04PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000368 LBR04PacketGreen((pixelpacket)); \
369 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000370 }
glennrp8e58efd2011-05-20 12:16:29 +0000371
glennrp91d99252011-06-25 14:30:13 +0000372#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000373 { \
glennrp91d99252011-06-25 14:30:13 +0000374 LBR04PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000375 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000376 }
glennrp8e58efd2011-05-20 12:16:29 +0000377
cristyef618312011-06-25 12:26:44 +0000378#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000379 { \
cristy16ea1392012-03-21 20:38:41 +0000380 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000381 & 0xf0; \
cristy16ea1392012-03-21 20:38:41 +0000382 SetPixelRed(image,\
383 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000384 }
glennrp54cf7972011-08-06 14:28:09 +0000385#define LBR04PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000386 { \
cristy16ea1392012-03-21 20:38:41 +0000387 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000388 & 0xf0; \
cristy16ea1392012-03-21 20:38:41 +0000389 SetPixelGreen(image,\
390 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000391 }
glennrp54cf7972011-08-06 14:28:09 +0000392#define LBR04PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000393 { \
394 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000395 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
396 SetPixelBlue(image,\
397 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000398 }
cristy16ea1392012-03-21 20:38:41 +0000399#define LBR04PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000400 { \
401 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000402 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
403 SetPixelAlpha(image,\
404 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000405 }
406
glennrp54cf7972011-08-06 14:28:09 +0000407#define LBR04PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000408 { \
cristyef618312011-06-25 12:26:44 +0000409 LBR04PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000410 LBR04PixelGreen((pixel)); \
411 LBR04PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000412 }
glennrp8e58efd2011-05-20 12:16:29 +0000413
cristy16ea1392012-03-21 20:38:41 +0000414#define LBR04PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000415 { \
glennrp54cf7972011-08-06 14:28:09 +0000416 LBR04PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000417 LBR04PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000418 }
glennrp8e58efd2011-05-20 12:16:29 +0000419
420
421/* LBR08: Replicate top 8 bits */
422
glennrp05001c32011-08-06 13:04:16 +0000423#define LBR08PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000424 { \
425 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
426 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
427 }
glennrp91d99252011-06-25 14:30:13 +0000428#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000429 { \
430 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
431 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
432 }
glennrp91d99252011-06-25 14:30:13 +0000433#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000434 { \
435 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
436 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
437 }
cristy16ea1392012-03-21 20:38:41 +0000438#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000439 { \
cristy16ea1392012-03-21 20:38:41 +0000440 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
441 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000442 }
443
glennrp91d99252011-06-25 14:30:13 +0000444#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000445 { \
glennrp05001c32011-08-06 13:04:16 +0000446 LBR08PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000447 LBR08PacketGreen((pixelpacket)); \
448 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000449 }
glennrp8e58efd2011-05-20 12:16:29 +0000450
glennrp91d99252011-06-25 14:30:13 +0000451#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000452 { \
glennrp91d99252011-06-25 14:30:13 +0000453 LBR08PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000454 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000455 }
glennrp8e58efd2011-05-20 12:16:29 +0000456
cristyef618312011-06-25 12:26:44 +0000457#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000458 { \
459 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000460 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
461 SetPixelRed(image,\
462 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000463 }
glennrp54cf7972011-08-06 14:28:09 +0000464#define LBR08PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000465 { \
466 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000467 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
468 SetPixelGreen(image,\
469 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000470 }
glennrp54cf7972011-08-06 14:28:09 +0000471#define LBR08PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000472 { \
473 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000474 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
475 SetPixelBlue(image,\
476 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000477 }
cristy16ea1392012-03-21 20:38:41 +0000478#define LBR08PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000479 { \
480 unsigned char lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000481 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
482 SetPixelAlpha(image,\
483 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000484 }
485
glennrp54cf7972011-08-06 14:28:09 +0000486#define LBR08PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000487 { \
cristyef618312011-06-25 12:26:44 +0000488 LBR08PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000489 LBR08PixelGreen((pixel)); \
490 LBR08PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000491 }
glennrp8e58efd2011-05-20 12:16:29 +0000492
cristy16ea1392012-03-21 20:38:41 +0000493#define LBR08PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000494 { \
glennrp54cf7972011-08-06 14:28:09 +0000495 LBR08PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000496 LBR08PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000497 }
glennrp8e58efd2011-05-20 12:16:29 +0000498
499
500/* LBR16: Replicate top 16 bits */
501
glennrp05001c32011-08-06 13:04:16 +0000502#define LBR16PacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000503 { \
504 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
505 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
506 }
glennrp91d99252011-06-25 14:30:13 +0000507#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000508 { \
509 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
510 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
511 }
glennrp91d99252011-06-25 14:30:13 +0000512#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000513 { \
514 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
515 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
516 }
cristy16ea1392012-03-21 20:38:41 +0000517#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000518 { \
cristy16ea1392012-03-21 20:38:41 +0000519 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
520 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000521 }
522
glennrp91d99252011-06-25 14:30:13 +0000523#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000524 { \
glennrp05001c32011-08-06 13:04:16 +0000525 LBR16PacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000526 LBR16PacketGreen((pixelpacket)); \
527 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000528 }
glennrp8e58efd2011-05-20 12:16:29 +0000529
glennrp91d99252011-06-25 14:30:13 +0000530#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000531 { \
glennrp91d99252011-06-25 14:30:13 +0000532 LBR16PacketRGB((pixelpacket)); \
cristy16ea1392012-03-21 20:38:41 +0000533 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000534 }
glennrp8e58efd2011-05-20 12:16:29 +0000535
cristyef618312011-06-25 12:26:44 +0000536#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000537 { \
538 unsigned short lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000539 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
540 SetPixelRed(image,\
541 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000542 }
glennrp54cf7972011-08-06 14:28:09 +0000543#define LBR16PixelGreen(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000544 { \
545 unsigned short lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000546 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
547 SetPixelGreen(image,\
548 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000549 }
glennrp54cf7972011-08-06 14:28:09 +0000550#define LBR16PixelBlue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000551 { \
552 unsigned short lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000553 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
554 SetPixelBlue(image,\
555 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000556 }
cristy16ea1392012-03-21 20:38:41 +0000557#define LBR16PixelAlpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000558 { \
559 unsigned short lbr_bits= \
cristy16ea1392012-03-21 20:38:41 +0000560 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
561 SetPixelAlpha(image,\
562 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000563 }
564
glennrp54cf7972011-08-06 14:28:09 +0000565#define LBR16PixelRGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000566 { \
cristyef618312011-06-25 12:26:44 +0000567 LBR16PixelRed((pixel)); \
glennrp54cf7972011-08-06 14:28:09 +0000568 LBR16PixelGreen((pixel)); \
569 LBR16PixelBlue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000570 }
glennrp8e58efd2011-05-20 12:16:29 +0000571
cristy16ea1392012-03-21 20:38:41 +0000572#define LBR16PixelRGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000573 { \
glennrp54cf7972011-08-06 14:28:09 +0000574 LBR16PixelRGB((pixel)); \
cristy16ea1392012-03-21 20:38:41 +0000575 LBR16PixelAlpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000576 }
glennrp8e58efd2011-05-20 12:16:29 +0000577
cristy3ed852e2009-09-05 21:47:34 +0000578/*
579 Establish thread safety.
580 setjmp/longjmp is claimed to be safe on these platforms:
581 setjmp/longjmp is alleged to be unsafe on these platforms:
582*/
583#ifndef SETJMP_IS_THREAD_SAFE
584#define PNG_SETJMP_NOT_THREAD_SAFE
585#endif
586
glennrpedaa0382012-04-12 14:16:21 +0000587#ifdef PNG_SETJMP_NOT_THREAD_SAFE
cristy3ed852e2009-09-05 21:47:34 +0000588static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000589 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000590#endif
591
592/*
593 This temporary until I set up malloc'ed object attributes array.
594 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
595 waste more memory.
596*/
597#define MNG_MAX_OBJECTS 256
598
599/*
600 If this not defined, spec is interpreted strictly. If it is
601 defined, an attempt will be made to recover from some errors,
602 including
603 o global PLTE too short
604*/
605#undef MNG_LOOSE
606
607/*
608 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
609 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
610 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
611 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
612 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
613 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
614 will be enabled by default in libpng-1.2.0.
615*/
cristy3ed852e2009-09-05 21:47:34 +0000616#ifdef PNG_MNG_FEATURES_SUPPORTED
617# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
618# define PNG_READ_EMPTY_PLTE_SUPPORTED
619# endif
620# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
621# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
622# endif
623#endif
624
625/*
cristybb503372010-05-27 20:51:26 +0000626 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000627 This macro is only defined in libpng-1.0.3 and later.
628 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
629*/
630#ifndef PNG_UINT_31_MAX
631#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
632#endif
633
634/*
635 Constant strings for known chunk types. If you need to add a chunk,
636 add a string holding the name here. To make the code more
637 portable, we use ASCII numbers like this, not characters.
638*/
639
glennrp85dcf872011-12-07 02:51:47 +0000640static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
641static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
642static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
643static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
644static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
645static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
646static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
647static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
648static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
649static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
650static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
651static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
652static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
653static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
654static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
655static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
656static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
657static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
658static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
659static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
660static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
661static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
662static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
663static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
664static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
665static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
666static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
667static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
668static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
669static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
670static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
671static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
672static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
673static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000674
675#if defined(JNG_SUPPORTED)
glennrp85dcf872011-12-07 02:51:47 +0000676static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
677static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
678static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
679static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
680static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
681static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000682#endif
683
684/*
685Other known chunks that are not yet supported by ImageMagick:
glennrp85dcf872011-12-07 02:51:47 +0000686static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
687static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
688static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
689static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
690static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
691static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
692static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
693static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
cristy3ed852e2009-09-05 21:47:34 +0000694*/
695
696typedef struct _MngBox
697{
cristy8182b072010-05-30 20:10:53 +0000698 long
cristy3ed852e2009-09-05 21:47:34 +0000699 left,
700 right,
701 top,
702 bottom;
703} MngBox;
704
705typedef struct _MngPair
706{
cristy8182b072010-05-30 20:10:53 +0000707 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000708 a,
709 b;
710} MngPair;
711
712#ifdef MNG_OBJECT_BUFFERS
713typedef struct _MngBuffer
714{
715
cristybb503372010-05-27 20:51:26 +0000716 size_t
cristy3ed852e2009-09-05 21:47:34 +0000717 height,
718 width;
719
720 Image
721 *image;
722
723 png_color
724 plte[256];
725
726 int
727 reference_count;
728
729 unsigned char
730 alpha_sample_depth,
731 compression_method,
732 color_type,
733 concrete,
734 filter_method,
735 frozen,
736 image_type,
737 interlace_method,
738 pixel_sample_depth,
739 plte_length,
740 sample_depth,
741 viewable;
742} MngBuffer;
743#endif
744
745typedef struct _MngInfo
746{
747
748#ifdef MNG_OBJECT_BUFFERS
749 MngBuffer
750 *ob[MNG_MAX_OBJECTS];
751#endif
752
753 Image *
754 image;
755
756 RectangleInfo
757 page;
758
759 int
760 adjoin,
761#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
762 bytes_in_read_buffer,
763 found_empty_plte,
764#endif
765 equal_backgrounds,
766 equal_chrms,
767 equal_gammas,
768#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
769 defined(PNG_MNG_FEATURES_SUPPORTED)
770 equal_palettes,
771#endif
772 equal_physs,
773 equal_srgbs,
774 framing_mode,
775 have_global_bkgd,
776 have_global_chrm,
777 have_global_gama,
778 have_global_phys,
779 have_global_sbit,
780 have_global_srgb,
781 have_saved_bkgd_index,
782 have_write_global_chrm,
783 have_write_global_gama,
784 have_write_global_plte,
785 have_write_global_srgb,
786 need_fram,
787 object_id,
788 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000789 saved_bkgd_index;
790
791 int
792 new_number_colors;
793
cristybb503372010-05-27 20:51:26 +0000794 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000795 image_found,
796 loop_count[256],
797 loop_iteration[256],
798 scenes_found,
799 x_off[MNG_MAX_OBJECTS],
800 y_off[MNG_MAX_OBJECTS];
801
802 MngBox
803 clip,
804 frame,
805 image_box,
806 object_clip[MNG_MAX_OBJECTS];
807
808 unsigned char
809 /* These flags could be combined into one byte */
810 exists[MNG_MAX_OBJECTS],
811 frozen[MNG_MAX_OBJECTS],
812 loop_active[256],
813 invisible[MNG_MAX_OBJECTS],
814 viewable[MNG_MAX_OBJECTS];
815
816 MagickOffsetType
817 loop_jump[256];
818
819 png_colorp
820 global_plte;
821
822 png_color_8
823 global_sbit;
824
825 png_byte
826#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
827 read_buffer[8],
828#endif
829 global_trns[256];
830
831 float
832 global_gamma;
833
834 ChromaticityInfo
835 global_chrm;
836
837 RenderingIntent
838 global_srgb_intent;
839
cristy35ef8242010-06-03 16:24:13 +0000840 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000841 delay,
842 global_plte_length,
843 global_trns_length,
844 global_x_pixels_per_unit,
845 global_y_pixels_per_unit,
846 mng_width,
847 mng_height,
848 ticks_per_second;
849
glennrpb9cfe272010-12-21 15:08:06 +0000850 MagickBooleanType
851 need_blob;
852
cristy3ed852e2009-09-05 21:47:34 +0000853 unsigned int
854 IsPalette,
855 global_phys_unit_type,
856 basi_warning,
857 clon_warning,
858 dhdr_warning,
859 jhdr_warning,
860 magn_warning,
861 past_warning,
862 phyg_warning,
863 phys_warning,
864 sbit_warning,
865 show_warning,
866 mng_type,
867 write_mng,
868 write_png_colortype,
869 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000870 write_png_compression_level,
871 write_png_compression_strategy,
872 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000873 write_png8,
874 write_png24,
875 write_png32;
876
877#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000878 size_t
cristy3ed852e2009-09-05 21:47:34 +0000879 basi_width,
880 basi_height;
881
882 unsigned int
883 basi_depth,
884 basi_color_type,
885 basi_compression_method,
886 basi_filter_type,
887 basi_interlace_method,
888 basi_red,
889 basi_green,
890 basi_blue,
891 basi_alpha,
892 basi_viewable;
893#endif
894
895 png_uint_16
896 magn_first,
897 magn_last,
898 magn_mb,
899 magn_ml,
900 magn_mr,
901 magn_mt,
902 magn_mx,
903 magn_my,
904 magn_methx,
905 magn_methy;
906
cristy16ea1392012-03-21 20:38:41 +0000907 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000908 mng_global_bkgd;
909
glennrp26f37912010-12-23 16:22:42 +0000910 /* Added at version 6.6.6-7 */
911 MagickBooleanType
912 ping_exclude_bKGD,
913 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000914 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000915 ping_exclude_EXIF,
916 ping_exclude_gAMA,
917 ping_exclude_iCCP,
918 /* ping_exclude_iTXt, */
919 ping_exclude_oFFs,
920 ping_exclude_pHYs,
921 ping_exclude_sRGB,
922 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000923 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000924 ping_exclude_vpAg,
925 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000926 ping_exclude_zTXt,
927 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000928
cristy3ed852e2009-09-05 21:47:34 +0000929} MngInfo;
930#endif /* VER */
931
932/*
933 Forward declarations.
934*/
935static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000936 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000937
cristy3ed852e2009-09-05 21:47:34 +0000938static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000939 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
glennrp0c3e06b2010-11-19 13:45:02 +0000940
cristy3ed852e2009-09-05 21:47:34 +0000941#if defined(JNG_SUPPORTED)
942static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000943 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000944#endif
945
glennrp0c3e06b2010-11-19 13:45:02 +0000946#if PNG_LIBPNG_VER > 10011
947
glennrpfd05d622011-02-25 04:10:33 +0000948
glennrp0c3e06b2010-11-19 13:45:02 +0000949#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
950static MagickBooleanType
cristy16ea1392012-03-21 20:38:41 +0000951LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
glennrp0c3e06b2010-11-19 13:45:02 +0000952{
glennrp67b9c1a2011-04-22 18:47:36 +0000953 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
954 *
955 * This is true if the high byte and the next highest byte of
956 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000957 * are equal to each other. We check this by seeing if the samples
958 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000959 *
960 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000961 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000962 */
963
glennrp3faa9a32011-04-23 14:00:25 +0000964#define QuantumToCharToQuantumEqQuantum(quantum) \
965 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
966
glennrp0c3e06b2010-11-19 13:45:02 +0000967 MagickBooleanType
968 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000969
glennrp03e11f62011-04-22 13:30:16 +0000970 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000971 {
972
cristy16ea1392012-03-21 20:38:41 +0000973 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000974 *p;
975
976 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000977 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
978 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
979 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
980 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000981
982 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
983 {
984 int indx;
985
986 for (indx=0; indx < (ssize_t) image->colors; indx++)
987 {
glennrp3faa9a32011-04-23 14:00:25 +0000988 ok_to_reduce=(
989 QuantumToCharToQuantumEqQuantum(
990 image->colormap[indx].red) &&
991 QuantumToCharToQuantumEqQuantum(
992 image->colormap[indx].green) &&
993 QuantumToCharToQuantumEqQuantum(
994 image->colormap[indx].blue)) ?
995 MagickTrue : MagickFalse;
996
glennrp0c3e06b2010-11-19 13:45:02 +0000997 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000998 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000999 }
1000 }
1001
1002 if ((ok_to_reduce != MagickFalse) &&
1003 (image->storage_class != PseudoClass))
1004 {
1005 ssize_t
1006 y;
1007
1008 register ssize_t
1009 x;
1010
1011 for (y=0; y < (ssize_t) image->rows; y++)
1012 {
cristy16ea1392012-03-21 20:38:41 +00001013 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0c3e06b2010-11-19 13:45:02 +00001014
cristy16ea1392012-03-21 20:38:41 +00001015 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001016 {
1017 ok_to_reduce = MagickFalse;
1018 break;
1019 }
1020
1021 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1022 {
glennrp3faa9a32011-04-23 14:00:25 +00001023 ok_to_reduce=
cristy16ea1392012-03-21 20:38:41 +00001024 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1025 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1026 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001027 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001028
1029 if (ok_to_reduce == MagickFalse)
1030 break;
1031
cristy16ea1392012-03-21 20:38:41 +00001032 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001033 }
glennrp8640fb52010-11-23 15:48:26 +00001034 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001035 break;
1036 }
1037 }
1038
1039 if (ok_to_reduce != MagickFalse)
1040 {
glennrp0c3e06b2010-11-19 13:45:02 +00001041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001042 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001043 }
glennrpa6a06632011-01-19 15:15:34 +00001044 else
1045 {
1046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001047 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001048 }
glennrp0c3e06b2010-11-19 13:45:02 +00001049 }
1050
1051 return ok_to_reduce;
1052}
1053#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1054
glennrp1a56e9c2012-04-25 17:06:57 +00001055static const char* PngColorTypeToString(const unsigned int color_type)
1056{
1057 const char
1058 *result = "Unknown";
1059
1060 switch (color_type)
1061 {
1062 case PNG_COLOR_TYPE_GRAY:
1063 result = "Gray";
1064 break;
1065 case PNG_COLOR_TYPE_GRAY_ALPHA:
1066 result = "Gray+Alpha";
1067 break;
1068 case PNG_COLOR_TYPE_PALETTE:
1069 result = "Palette";
1070 break;
1071 case PNG_COLOR_TYPE_RGB:
1072 result = "RGB";
1073 break;
1074 case PNG_COLOR_TYPE_RGB_ALPHA:
1075 result = "RGB+Alpha";
1076 break;
1077 }
1078
1079 return result;
1080}
1081
glennrpe610a072010-08-05 17:08:46 +00001082static int
glennrpcf002022011-01-30 02:38:15 +00001083Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001084{
glennrpe610a072010-08-05 17:08:46 +00001085 switch (intent)
1086 {
1087 case PerceptualIntent:
1088 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001089
glennrpe610a072010-08-05 17:08:46 +00001090 case RelativeIntent:
1091 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001092
glennrpe610a072010-08-05 17:08:46 +00001093 case SaturationIntent:
1094 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001095
glennrpe610a072010-08-05 17:08:46 +00001096 case AbsoluteIntent:
1097 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001098
glennrpe610a072010-08-05 17:08:46 +00001099 default:
1100 return -1;
1101 }
1102}
1103
1104static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001105Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001106{
glennrpcf002022011-01-30 02:38:15 +00001107 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001108 {
1109 case 0:
1110 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001111
glennrpe610a072010-08-05 17:08:46 +00001112 case 1:
1113 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001114
glennrpe610a072010-08-05 17:08:46 +00001115 case 2:
1116 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001117
glennrpe610a072010-08-05 17:08:46 +00001118 case 3:
1119 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001120
glennrpe610a072010-08-05 17:08:46 +00001121 default:
1122 return UndefinedIntent;
1123 }
1124}
1125
cristybb503372010-05-27 20:51:26 +00001126static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001127{
1128 if (x > y)
1129 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001130
cristy3ed852e2009-09-05 21:47:34 +00001131 return(y);
1132}
glennrp0c3e06b2010-11-19 13:45:02 +00001133
cristyd9ecd042012-06-17 18:26:12 +00001134static const char *
glennrp5dff4352012-06-06 22:12:04 +00001135Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1136{
1137 switch (ping_colortype)
1138 {
1139 case 0:
1140 return "Grayscale";
1141
1142 case 2:
1143 return "Truecolor";
1144
1145 case 3:
1146 return "Indexed";
1147
1148 case 4:
1149 return "GrayAlpha";
1150
1151 case 6:
1152 return "RGBA";
1153
1154 default:
1155 return "UndefinedColorType";
1156 }
1157}
1158
1159
cristybb503372010-05-27 20:51:26 +00001160static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001161{
1162 if (x < y)
1163 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001164
cristy3ed852e2009-09-05 21:47:34 +00001165 return(y);
1166}
glennrp0c3e06b2010-11-19 13:45:02 +00001167
cristy3ed852e2009-09-05 21:47:34 +00001168
1169/*
1170%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171% %
1172% %
1173% %
1174% I m a g e I s G r a y %
1175% %
1176% %
1177% %
1178%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179% %
cristy16ea1392012-03-21 20:38:41 +00001180% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001181% %
1182%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1183*/
cristy16ea1392012-03-21 20:38:41 +00001184static MagickBooleanType ImageIsGray(Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001185{
cristy16ea1392012-03-21 20:38:41 +00001186 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001187 *p;
1188
cristybb503372010-05-27 20:51:26 +00001189 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001190 i,
1191 x,
1192 y;
1193
1194 assert(image != (Image *) NULL);
1195 assert(image->signature == MagickSignature);
1196 if (image->debug != MagickFalse)
1197 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1198
1199 if (image->storage_class == PseudoClass)
1200 {
cristybb503372010-05-27 20:51:26 +00001201 for (i=0; i < (ssize_t) image->colors; i++)
cristy16ea1392012-03-21 20:38:41 +00001202 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001203 return(MagickFalse);
1204 return(MagickTrue);
1205 }
cristybb503372010-05-27 20:51:26 +00001206 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001207 {
cristy16ea1392012-03-21 20:38:41 +00001208 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1209 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001210 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001211 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001212 {
cristy16ea1392012-03-21 20:38:41 +00001213 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001214 return(MagickFalse);
cristy16ea1392012-03-21 20:38:41 +00001215 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001216 }
1217 }
1218 return(MagickTrue);
1219}
glennrpd5045b42010-03-24 12:40:35 +00001220#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001221#endif /* MAGICKCORE_PNG_DELEGATE */
1222
1223/*
1224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1225% %
1226% %
1227% %
1228% I s M N G %
1229% %
1230% %
1231% %
1232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1233%
1234% IsMNG() returns MagickTrue if the image format type, identified by the
1235% magick string, is MNG.
1236%
1237% The format of the IsMNG method is:
1238%
1239% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1240%
1241% A description of each parameter follows:
1242%
1243% o magick: compare image format pattern against these bytes.
1244%
1245% o length: Specifies the length of the magick string.
1246%
1247%
1248*/
1249static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1250{
1251 if (length < 8)
1252 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001253
cristy3ed852e2009-09-05 21:47:34 +00001254 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1255 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001256
cristy3ed852e2009-09-05 21:47:34 +00001257 return(MagickFalse);
1258}
1259
1260/*
1261%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1262% %
1263% %
1264% %
1265% I s J N G %
1266% %
1267% %
1268% %
1269%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1270%
1271% IsJNG() returns MagickTrue if the image format type, identified by the
1272% magick string, is JNG.
1273%
1274% The format of the IsJNG method is:
1275%
1276% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1277%
1278% A description of each parameter follows:
1279%
1280% o magick: compare image format pattern against these bytes.
1281%
1282% o length: Specifies the length of the magick string.
1283%
1284%
1285*/
1286static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1287{
1288 if (length < 8)
1289 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001290
cristy3ed852e2009-09-05 21:47:34 +00001291 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1292 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001293
cristy3ed852e2009-09-05 21:47:34 +00001294 return(MagickFalse);
1295}
1296
1297/*
1298%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1299% %
1300% %
1301% %
1302% I s P N G %
1303% %
1304% %
1305% %
1306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1307%
1308% IsPNG() returns MagickTrue if the image format type, identified by the
1309% magick string, is PNG.
1310%
1311% The format of the IsPNG method is:
1312%
1313% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1314%
1315% A description of each parameter follows:
1316%
1317% o magick: compare image format pattern against these bytes.
1318%
1319% o length: Specifies the length of the magick string.
1320%
1321*/
1322static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1323{
1324 if (length < 8)
1325 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001326
cristy3ed852e2009-09-05 21:47:34 +00001327 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1328 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001329
cristy3ed852e2009-09-05 21:47:34 +00001330 return(MagickFalse);
1331}
1332
1333#if defined(MAGICKCORE_PNG_DELEGATE)
1334#if defined(__cplusplus) || defined(c_plusplus)
1335extern "C" {
1336#endif
1337
glennrpd5045b42010-03-24 12:40:35 +00001338#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001339static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001340{
1341 unsigned char
1342 buffer[4];
1343
1344 assert(image != (Image *) NULL);
1345 assert(image->signature == MagickSignature);
1346 buffer[0]=(unsigned char) (value >> 24);
1347 buffer[1]=(unsigned char) (value >> 16);
1348 buffer[2]=(unsigned char) (value >> 8);
1349 buffer[3]=(unsigned char) value;
1350 return((size_t) WriteBlob(image,4,buffer));
1351}
1352
1353static void PNGLong(png_bytep p,png_uint_32 value)
1354{
1355 *p++=(png_byte) ((value >> 24) & 0xff);
1356 *p++=(png_byte) ((value >> 16) & 0xff);
1357 *p++=(png_byte) ((value >> 8) & 0xff);
1358 *p++=(png_byte) (value & 0xff);
1359}
1360
glennrpa521b2f2010-10-29 04:11:03 +00001361#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001362static void PNGsLong(png_bytep p,png_int_32 value)
1363{
1364 *p++=(png_byte) ((value >> 24) & 0xff);
1365 *p++=(png_byte) ((value >> 16) & 0xff);
1366 *p++=(png_byte) ((value >> 8) & 0xff);
1367 *p++=(png_byte) (value & 0xff);
1368}
glennrpa521b2f2010-10-29 04:11:03 +00001369#endif
cristy3ed852e2009-09-05 21:47:34 +00001370
1371static void PNGShort(png_bytep p,png_uint_16 value)
1372{
1373 *p++=(png_byte) ((value >> 8) & 0xff);
1374 *p++=(png_byte) (value & 0xff);
1375}
1376
1377static void PNGType(png_bytep p,png_bytep type)
1378{
1379 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1380}
1381
glennrp03812ae2010-12-24 01:31:34 +00001382static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1383 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001384{
1385 if (logging != MagickFalse)
1386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001387 " Writing %c%c%c%c chunk, length: %.20g",
1388 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001389}
glennrpd5045b42010-03-24 12:40:35 +00001390#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001391
1392#if defined(__cplusplus) || defined(c_plusplus)
1393}
1394#endif
1395
glennrpd5045b42010-03-24 12:40:35 +00001396#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001397/*
1398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1399% %
1400% %
1401% %
1402% R e a d P N G I m a g e %
1403% %
1404% %
1405% %
1406%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1407%
1408% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1409% Multiple-image Network Graphics (MNG) image file and returns it. It
1410% allocates the memory necessary for the new Image structure and returns a
1411% pointer to the new image or set of images.
1412%
1413% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1414%
1415% The format of the ReadPNGImage method is:
1416%
1417% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1418%
1419% A description of each parameter follows:
1420%
1421% o image_info: the image info.
1422%
1423% o exception: return any errors or warnings in this structure.
1424%
1425% To do, more or less in chronological order (as of version 5.5.2,
1426% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1427%
1428% Get 16-bit cheap transparency working.
1429%
1430% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1431%
1432% Preserve all unknown and not-yet-handled known chunks found in input
1433% PNG file and copy them into output PNG files according to the PNG
1434% copying rules.
1435%
1436% (At this point, PNG encoding should be in full MNG compliance)
1437%
1438% Provide options for choice of background to use when the MNG BACK
1439% chunk is not present or is not mandatory (i.e., leave transparent,
1440% user specified, MNG BACK, PNG bKGD)
1441%
1442% Implement LOOP/ENDL [done, but could do discretionary loops more
1443% efficiently by linking in the duplicate frames.].
1444%
1445% Decode and act on the MHDR simplicity profile (offer option to reject
1446% files or attempt to process them anyway when the profile isn't LC or VLC).
1447%
1448% Upgrade to full MNG without Delta-PNG.
1449%
1450% o BACK [done a while ago except for background image ID]
1451% o MOVE [done 15 May 1999]
1452% o CLIP [done 15 May 1999]
1453% o DISC [done 19 May 1999]
1454% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1455% o SEEK [partially done 19 May 1999 (discard function only)]
1456% o SHOW
1457% o PAST
1458% o BASI
1459% o MNG-level tEXt/iTXt/zTXt
1460% o pHYg
1461% o pHYs
1462% o sBIT
1463% o bKGD
1464% o iTXt (wait for libpng implementation).
1465%
1466% Use the scene signature to discover when an identical scene is
1467% being reused, and just point to the original image->exception instead
1468% of storing another set of pixels. This not specific to MNG
1469% but could be applied generally.
1470%
1471% Upgrade to full MNG with Delta-PNG.
1472%
1473% JNG tEXt/iTXt/zTXt
1474%
1475% We will not attempt to read files containing the CgBI chunk.
1476% They are really Xcode files meant for display on the iPhone.
1477% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001478% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001479% since irretrievable loss of color data has occurred due to the
1480% use of premultiplied alpha.
1481*/
1482
1483#if defined(__cplusplus) || defined(c_plusplus)
1484extern "C" {
1485#endif
1486
1487/*
1488 This the function that does the actual reading of data. It is
1489 the same as the one supplied in libpng, except that it receives the
1490 datastream from the ReadBlob() function instead of standard input.
1491*/
1492static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1493{
1494 Image
1495 *image;
1496
1497 image=(Image *) png_get_io_ptr(png_ptr);
1498 if (length)
1499 {
1500 png_size_t
1501 check;
1502
1503 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1504 if (check != length)
1505 {
1506 char
1507 msg[MaxTextExtent];
1508
cristy3b6fd2e2011-05-20 12:53:50 +00001509 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001510 "Expected %.20g bytes; found %.20g bytes",(double) length,
1511 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001512 png_warning(png_ptr,msg);
1513 png_error(png_ptr,"Read Exception");
1514 }
1515 }
1516}
1517
1518#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1519 !defined(PNG_MNG_FEATURES_SUPPORTED)
1520/* We use mng_get_data() instead of png_get_data() if we have a libpng
1521 * older than libpng-1.0.3a, which was the first to allow the empty
1522 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1523 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1524 * encountered after an empty PLTE, so we have to look ahead for bKGD
1525 * chunks and remove them from the datastream that is passed to libpng,
1526 * and store their contents for later use.
1527 */
1528static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1529{
1530 MngInfo
1531 *mng_info;
1532
1533 Image
1534 *image;
1535
1536 png_size_t
1537 check;
1538
cristybb503372010-05-27 20:51:26 +00001539 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001540 i;
1541
1542 i=0;
1543 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1544 image=(Image *) mng_info->image;
1545 while (mng_info->bytes_in_read_buffer && length)
1546 {
1547 data[i]=mng_info->read_buffer[i];
1548 mng_info->bytes_in_read_buffer--;
1549 length--;
1550 i++;
1551 }
1552 if (length)
1553 {
1554 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001555
cristy3ed852e2009-09-05 21:47:34 +00001556 if (check != length)
1557 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001558
cristy3ed852e2009-09-05 21:47:34 +00001559 if (length == 4)
1560 {
1561 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1562 (data[3] == 0))
1563 {
1564 check=(png_size_t) ReadBlob(image,(size_t) length,
1565 (char *) mng_info->read_buffer);
1566 mng_info->read_buffer[4]=0;
1567 mng_info->bytes_in_read_buffer=4;
1568 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1569 mng_info->found_empty_plte=MagickTrue;
1570 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1571 {
1572 mng_info->found_empty_plte=MagickFalse;
1573 mng_info->have_saved_bkgd_index=MagickFalse;
1574 }
1575 }
glennrp0fe50b42010-11-16 03:52:51 +00001576
cristy3ed852e2009-09-05 21:47:34 +00001577 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1578 (data[3] == 1))
1579 {
1580 check=(png_size_t) ReadBlob(image,(size_t) length,
1581 (char *) mng_info->read_buffer);
1582 mng_info->read_buffer[4]=0;
1583 mng_info->bytes_in_read_buffer=4;
1584 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1585 if (mng_info->found_empty_plte)
1586 {
1587 /*
1588 Skip the bKGD data byte and CRC.
1589 */
1590 check=(png_size_t)
1591 ReadBlob(image,5,(char *) mng_info->read_buffer);
1592 check=(png_size_t) ReadBlob(image,(size_t) length,
1593 (char *) mng_info->read_buffer);
1594 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1595 mng_info->have_saved_bkgd_index=MagickTrue;
1596 mng_info->bytes_in_read_buffer=0;
1597 }
1598 }
1599 }
1600 }
1601}
1602#endif
1603
1604static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1605{
1606 Image
1607 *image;
1608
1609 image=(Image *) png_get_io_ptr(png_ptr);
1610 if (length)
1611 {
1612 png_size_t
1613 check;
1614
cristybb503372010-05-27 20:51:26 +00001615 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001616
cristy3ed852e2009-09-05 21:47:34 +00001617 if (check != length)
1618 png_error(png_ptr,"WriteBlob Failed");
1619 }
1620}
1621
1622static void png_flush_data(png_structp png_ptr)
1623{
1624 (void) png_ptr;
1625}
1626
1627#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1628static int PalettesAreEqual(Image *a,Image *b)
1629{
cristybb503372010-05-27 20:51:26 +00001630 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001631 i;
1632
1633 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1634 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001635
cristy3ed852e2009-09-05 21:47:34 +00001636 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1637 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001638
cristy3ed852e2009-09-05 21:47:34 +00001639 if (a->colors != b->colors)
1640 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001641
cristybb503372010-05-27 20:51:26 +00001642 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001643 {
1644 if ((a->colormap[i].red != b->colormap[i].red) ||
1645 (a->colormap[i].green != b->colormap[i].green) ||
1646 (a->colormap[i].blue != b->colormap[i].blue))
1647 return((int) MagickFalse);
1648 }
glennrp0fe50b42010-11-16 03:52:51 +00001649
cristy3ed852e2009-09-05 21:47:34 +00001650 return((int) MagickTrue);
1651}
1652#endif
1653
1654static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1655{
1656 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1657 mng_info->exists[i] && !mng_info->frozen[i])
1658 {
1659#ifdef MNG_OBJECT_BUFFERS
1660 if (mng_info->ob[i] != (MngBuffer *) NULL)
1661 {
1662 if (mng_info->ob[i]->reference_count > 0)
1663 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001664
cristy3ed852e2009-09-05 21:47:34 +00001665 if (mng_info->ob[i]->reference_count == 0)
1666 {
1667 if (mng_info->ob[i]->image != (Image *) NULL)
1668 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001669
cristy3ed852e2009-09-05 21:47:34 +00001670 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1671 }
1672 }
1673 mng_info->ob[i]=(MngBuffer *) NULL;
1674#endif
1675 mng_info->exists[i]=MagickFalse;
1676 mng_info->invisible[i]=MagickFalse;
1677 mng_info->viewable[i]=MagickFalse;
1678 mng_info->frozen[i]=MagickFalse;
1679 mng_info->x_off[i]=0;
1680 mng_info->y_off[i]=0;
1681 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001682 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001683 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001684 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001685 }
1686}
1687
glennrp21f0e622011-01-07 16:20:57 +00001688static void MngInfoFreeStruct(MngInfo *mng_info,
1689 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001690{
glennrp21f0e622011-01-07 16:20:57 +00001691 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001692 {
cristybb503372010-05-27 20:51:26 +00001693 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001694 i;
1695
1696 for (i=1; i < MNG_MAX_OBJECTS; i++)
1697 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001698
cristy3ed852e2009-09-05 21:47:34 +00001699 if (mng_info->global_plte != (png_colorp) NULL)
1700 mng_info->global_plte=(png_colorp)
1701 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001702
cristy3ed852e2009-09-05 21:47:34 +00001703 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1704 *have_mng_structure=MagickFalse;
1705 }
1706}
1707
1708static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1709{
1710 MngBox
1711 box;
1712
1713 box=box1;
1714 if (box.left < box2.left)
1715 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001716
cristy3ed852e2009-09-05 21:47:34 +00001717 if (box.top < box2.top)
1718 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001719
cristy3ed852e2009-09-05 21:47:34 +00001720 if (box.right > box2.right)
1721 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001722
cristy3ed852e2009-09-05 21:47:34 +00001723 if (box.bottom > box2.bottom)
1724 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001725
cristy3ed852e2009-09-05 21:47:34 +00001726 return box;
1727}
1728
1729static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1730{
1731 MngBox
1732 box;
1733
1734 /*
1735 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1736 */
cristybb503372010-05-27 20:51:26 +00001737 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1738 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1739 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1740 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001741 if (delta_type != 0)
1742 {
1743 box.left+=previous_box.left;
1744 box.right+=previous_box.right;
1745 box.top+=previous_box.top;
1746 box.bottom+=previous_box.bottom;
1747 }
glennrp0fe50b42010-11-16 03:52:51 +00001748
cristy3ed852e2009-09-05 21:47:34 +00001749 return(box);
1750}
1751
1752static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1753 unsigned char *p)
1754{
1755 MngPair
1756 pair;
1757 /*
cristybb503372010-05-27 20:51:26 +00001758 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001759 */
cristy8182b072010-05-30 20:10:53 +00001760 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1761 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001762
cristy3ed852e2009-09-05 21:47:34 +00001763 if (delta_type != 0)
1764 {
1765 pair.a+=previous_pair.a;
1766 pair.b+=previous_pair.b;
1767 }
glennrp0fe50b42010-11-16 03:52:51 +00001768
cristy3ed852e2009-09-05 21:47:34 +00001769 return(pair);
1770}
1771
cristy8182b072010-05-30 20:10:53 +00001772static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001773{
cristy8182b072010-05-30 20:10:53 +00001774 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001775}
1776
cristy16ea1392012-03-21 20:38:41 +00001777typedef struct _PNGErrorInfo
cristyc82a27b2011-10-21 01:07:16 +00001778{
cristyc82a27b2011-10-21 01:07:16 +00001779 Image
1780 *image;
1781
cristy16ea1392012-03-21 20:38:41 +00001782 ExceptionInfo
1783 *exception;
1784} PNGErrorInfo;
cristyc82a27b2011-10-21 01:07:16 +00001785
cristy16ea1392012-03-21 20:38:41 +00001786static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1787{
1788 ExceptionInfo
1789 *exception;
cristyc82a27b2011-10-21 01:07:16 +00001790
cristy16ea1392012-03-21 20:38:41 +00001791 Image
1792 *image;
1793
1794 PNGErrorInfo
1795 *error_info;
1796
1797 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1798 image=error_info->image;
1799 exception=error_info->exception;
1800
1801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1802 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1803
1804 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1805 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001806
glennrpe4017e32011-01-08 17:16:09 +00001807#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001808 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1809 * are building with libpng-1.4.x and can be ignored.
1810 */
cristy3ed852e2009-09-05 21:47:34 +00001811 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001812#else
1813 png_longjmp(ping,1);
1814#endif
cristy3ed852e2009-09-05 21:47:34 +00001815}
1816
glennrpcf002022011-01-30 02:38:15 +00001817static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001818{
cristy16ea1392012-03-21 20:38:41 +00001819 ExceptionInfo
1820 *exception;
1821
cristy3ed852e2009-09-05 21:47:34 +00001822 Image
1823 *image;
1824
cristy16ea1392012-03-21 20:38:41 +00001825 PNGErrorInfo
1826 *error_info;
1827
cristy3ed852e2009-09-05 21:47:34 +00001828 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1829 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001830
cristy16ea1392012-03-21 20:38:41 +00001831 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1832 image=error_info->image;
1833 exception=error_info->exception;
1834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1835 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001836
cristy16ea1392012-03-21 20:38:41 +00001837 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001838 message,"`%s'",image->filename);
1839}
1840
1841#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001842static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001843{
cristydf0d90e2011-12-12 01:03:55 +00001844 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001845 return((png_voidp) AcquireMagickMemory((size_t) size));
cristy3ed852e2009-09-05 21:47:34 +00001846}
1847
1848/*
1849 Free a pointer. It is removed from the list at the same time.
1850*/
glennrpcf002022011-01-30 02:38:15 +00001851static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001852{
glennrp3bd393f2011-12-21 18:54:53 +00001853 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001854 ptr=RelinquishMagickMemory(ptr);
1855 return((png_free_ptr) NULL);
1856}
1857#endif
1858
1859#if defined(__cplusplus) || defined(c_plusplus)
1860}
1861#endif
1862
1863static int
glennrpedaa0382012-04-12 14:16:21 +00001864Magick_png_read_raw_profile(png_struct *ping,Image *image,
1865 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001866{
cristybb503372010-05-27 20:51:26 +00001867 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001868 i;
1869
1870 register unsigned char
1871 *dp;
1872
1873 register png_charp
1874 sp;
1875
1876 png_uint_32
1877 length,
1878 nibbles;
1879
1880 StringInfo
1881 *profile;
1882
glennrp0c3e06b2010-11-19 13:45:02 +00001883 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001884 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1885 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1886 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1887 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1888 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1889 13,14,15};
1890
1891 sp=text[ii].text+1;
1892 /* look for newline */
1893 while (*sp != '\n')
1894 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001895
cristy3ed852e2009-09-05 21:47:34 +00001896 /* look for length */
1897 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1898 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001899
cristyf2f27272009-12-17 14:48:46 +00001900 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001901
glennrp97f90e22011-02-22 05:47:58 +00001902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1903 " length: %lu",(unsigned long) length);
1904
cristy3ed852e2009-09-05 21:47:34 +00001905 while (*sp != ' ' && *sp != '\n')
1906 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001907
cristy3ed852e2009-09-05 21:47:34 +00001908 /* allocate space */
1909 if (length == 0)
1910 {
glennrpedaa0382012-04-12 14:16:21 +00001911 png_warning(ping,"invalid profile length");
cristy3ed852e2009-09-05 21:47:34 +00001912 return(MagickFalse);
1913 }
glennrp0fe50b42010-11-16 03:52:51 +00001914
cristy8723e4b2011-09-01 13:11:19 +00001915 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001916
cristy3ed852e2009-09-05 21:47:34 +00001917 if (profile == (StringInfo *) NULL)
1918 {
glennrpedaa0382012-04-12 14:16:21 +00001919 png_warning(ping, "unable to copy profile");
cristy3ed852e2009-09-05 21:47:34 +00001920 return(MagickFalse);
1921 }
glennrp0fe50b42010-11-16 03:52:51 +00001922
cristy3ed852e2009-09-05 21:47:34 +00001923 /* copy profile, skipping white space and column 1 "=" signs */
1924 dp=GetStringInfoDatum(profile);
1925 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001926
cristybb503372010-05-27 20:51:26 +00001927 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001928 {
1929 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1930 {
1931 if (*sp == '\0')
1932 {
glennrpedaa0382012-04-12 14:16:21 +00001933 png_warning(ping, "ran out of profile data");
cristy3ed852e2009-09-05 21:47:34 +00001934 profile=DestroyStringInfo(profile);
1935 return(MagickFalse);
1936 }
1937 sp++;
1938 }
glennrp0fe50b42010-11-16 03:52:51 +00001939
cristy3ed852e2009-09-05 21:47:34 +00001940 if (i%2 == 0)
1941 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001942
cristy3ed852e2009-09-05 21:47:34 +00001943 else
1944 (*dp++)+=unhex[(int) *sp++];
1945 }
1946 /*
1947 We have already read "Raw profile type.
1948 */
cristy16ea1392012-03-21 20:38:41 +00001949 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001950 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001951
cristy3ed852e2009-09-05 21:47:34 +00001952 if (image_info->verbose)
1953 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001954
cristy3ed852e2009-09-05 21:47:34 +00001955 return MagickTrue;
1956}
1957
1958#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1959static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1960{
1961 Image
1962 *image;
1963
1964
1965 /* The unknown chunk structure contains the chunk data:
1966 png_byte name[5];
1967 png_byte *data;
1968 png_size_t size;
1969
1970 Note that libpng has already taken care of the CRC handling.
1971 */
1972
1973
1974 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1975 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1976 return(0); /* Did not recognize */
1977
1978 /* recognized vpAg */
1979
1980 if (chunk->size != 9)
1981 return(-1); /* Error return */
1982
1983 if (chunk->data[8] != 0)
1984 return(0); /* ImageMagick requires pixel units */
1985
1986 image=(Image *) png_get_user_chunk_ptr(ping);
1987
cristybb503372010-05-27 20:51:26 +00001988 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001989 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001990
cristybb503372010-05-27 20:51:26 +00001991 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001992 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1993
1994 /* Return one of the following: */
1995 /* return(-n); chunk had an error */
1996 /* return(0); did not recognize */
1997 /* return(n); success */
1998
1999 return(1);
2000
2001}
2002#endif
2003
2004/*
2005%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2006% %
2007% %
2008% %
2009% R e a d O n e P N G I m a g e %
2010% %
2011% %
2012% %
2013%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2014%
2015% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2016% (minus the 8-byte signature) and returns it. It allocates the memory
2017% necessary for the new Image structure and returns a pointer to the new
2018% image.
2019%
2020% The format of the ReadOnePNGImage method is:
2021%
2022% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2023% ExceptionInfo *exception)
2024%
2025% A description of each parameter follows:
2026%
2027% o mng_info: Specifies a pointer to a MngInfo structure.
2028%
2029% o image_info: the image info.
2030%
2031% o exception: return any errors or warnings in this structure.
2032%
2033*/
2034static Image *ReadOnePNGImage(MngInfo *mng_info,
2035 const ImageInfo *image_info, ExceptionInfo *exception)
2036{
2037 /* Read one PNG image */
2038
glennrpcc95c3f2011-04-18 16:46:48 +00002039 /* To do: Read the tIME chunk into the date:modify property */
2040 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2041
cristy3ed852e2009-09-05 21:47:34 +00002042 Image
2043 *image;
2044
2045 int
glennrp4eb39312011-03-30 21:34:55 +00002046 intent,
glennrpcb395ac2011-03-30 19:50:23 +00002047 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002048 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002049 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002050 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00002051 pass,
2052 ping_bit_depth,
2053 ping_color_type,
2054 ping_interlace_method,
2055 ping_compression_method,
2056 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002057 ping_num_trans,
2058 unit_type;
2059
2060 double
2061 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002062
2063 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002064 logging,
cristy3ed852e2009-09-05 21:47:34 +00002065 status;
2066
cristy16ea1392012-03-21 20:38:41 +00002067 PixelInfo
2068 transparent_color;
2069
2070 PNGErrorInfo
2071 error_info;
2072
glennrpfaa852b2010-03-30 12:17:00 +00002073 png_bytep
2074 ping_trans_alpha;
2075
2076 png_color_16p
2077 ping_background,
2078 ping_trans_color;
2079
cristy3ed852e2009-09-05 21:47:34 +00002080 png_info
2081 *end_info,
2082 *ping_info;
2083
2084 png_struct
2085 *ping;
2086
2087 png_textp
2088 text;
2089
glennrpfaa852b2010-03-30 12:17:00 +00002090 png_uint_32
2091 ping_height,
2092 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002093 x_resolution,
2094 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002095
cristy16ea1392012-03-21 20:38:41 +00002096 QuantumInfo
2097 *quantum_info;
2098
cristy3ed852e2009-09-05 21:47:34 +00002099 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002100 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002101
cristybb503372010-05-27 20:51:26 +00002102 ssize_t
cristy756ae432011-11-19 02:18:25 +00002103 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002104 y;
2105
2106 register unsigned char
2107 *p;
2108
cristybb503372010-05-27 20:51:26 +00002109 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002110 i,
2111 x;
2112
cristy16ea1392012-03-21 20:38:41 +00002113 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002114 *q;
2115
2116 size_t
glennrp39992b42010-11-14 00:03:43 +00002117 length,
cristy3ed852e2009-09-05 21:47:34 +00002118 row_offset;
2119
cristyeb3b22a2011-03-31 20:16:11 +00002120 ssize_t
2121 j;
2122
glennrp629960f2012-05-29 19:13:52 +00002123#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002124 png_byte unused_chunks[]=
2125 {
2126 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2127 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2128 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2129 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2130 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2131 116, 73, 77, 69, (png_byte) '\0', /* tIME */
glennrp629960f2012-05-29 19:13:52 +00002132#ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2133 /* ignore the APNG chunks */
2134 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2135 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2136 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2137#endif
cristy3ed852e2009-09-05 21:47:34 +00002138 };
2139#endif
2140
2141 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002142 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002143
glennrp25c1e2b2010-03-25 01:39:56 +00002144#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002145 if (image_info->verbose)
2146 printf("Your PNG library (libpng-%s) is rather old.\n",
2147 PNG_LIBPNG_VER_STRING);
2148#endif
2149
glennrp61b4c952009-11-10 20:40:41 +00002150#if (PNG_LIBPNG_VER >= 10400)
2151# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2152 if (image_info->verbose)
2153 {
2154 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2155 PNG_LIBPNG_VER_STRING);
2156 printf("Please update it.\n");
2157 }
2158# endif
2159#endif
2160
cristy16ea1392012-03-21 20:38:41 +00002161
2162 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002163 image=mng_info->image;
2164
glennrpa6a06632011-01-19 15:15:34 +00002165 if (logging != MagickFalse)
2166 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2167 " image->matte=%d",(int) image->matte);
2168
glennrp0e319732011-01-25 21:53:13 +00002169 /* Set to an out-of-range color unless tRNS chunk is present */
2170 transparent_color.red=65537;
2171 transparent_color.green=65537;
2172 transparent_color.blue=65537;
cristy16ea1392012-03-21 20:38:41 +00002173 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002174
glennrpcb395ac2011-03-30 19:50:23 +00002175 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002176 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002177 num_raw_profiles = 0;
2178
cristy3ed852e2009-09-05 21:47:34 +00002179 /*
2180 Allocate the PNG structures
2181 */
2182#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00002183 error_info.image=image;
2184 error_info.exception=exception;
2185 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002186 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2187 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002188#else
cristy16ea1392012-03-21 20:38:41 +00002189 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002190 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002191#endif
2192 if (ping == (png_struct *) NULL)
2193 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002194
cristy3ed852e2009-09-05 21:47:34 +00002195 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002196
cristy3ed852e2009-09-05 21:47:34 +00002197 if (ping_info == (png_info *) NULL)
2198 {
2199 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2200 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2201 }
glennrp0fe50b42010-11-16 03:52:51 +00002202
cristy3ed852e2009-09-05 21:47:34 +00002203 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002204
cristy3ed852e2009-09-05 21:47:34 +00002205 if (end_info == (png_info *) NULL)
2206 {
2207 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2208 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2209 }
glennrp0fe50b42010-11-16 03:52:51 +00002210
glennrpcf002022011-01-30 02:38:15 +00002211 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002212
glennrpfaa852b2010-03-30 12:17:00 +00002213 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002214 {
2215 /*
2216 PNG image is corrupt.
2217 */
2218 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002219
2220#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002221 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002222#endif
glennrpedaa0382012-04-12 14:16:21 +00002223
2224 if (ping_pixels != (unsigned char *) NULL)
2225 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2226
cristy3ed852e2009-09-05 21:47:34 +00002227 if (logging != MagickFalse)
2228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2229 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002230
cristy3ed852e2009-09-05 21:47:34 +00002231 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002232 {
cristy16ea1392012-03-21 20:38:41 +00002233 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002234 image->columns=0;
2235 }
glennrp0fe50b42010-11-16 03:52:51 +00002236
cristy3ed852e2009-09-05 21:47:34 +00002237 return(GetFirstImageInList(image));
2238 }
glennrpedaa0382012-04-12 14:16:21 +00002239
2240 /* { For navigation to end of SETJMP-protected block. Within this
2241 * block, use png_error() instead of Throwing an Exception, to ensure
2242 * that libpng is able to clean up, and that the semaphore is unlocked.
2243 */
2244
2245#ifdef PNG_SETJMP_NOT_THREAD_SAFE
2246 LockSemaphoreInfo(ping_semaphore);
2247#endif
2248
cristy3ed852e2009-09-05 21:47:34 +00002249 /*
2250 Prepare PNG for reading.
2251 */
glennrpfaa852b2010-03-30 12:17:00 +00002252
cristy3ed852e2009-09-05 21:47:34 +00002253 mng_info->image_found++;
2254 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002255
cristy3ed852e2009-09-05 21:47:34 +00002256 if (LocaleCompare(image_info->magick,"MNG") == 0)
2257 {
2258#if defined(PNG_MNG_FEATURES_SUPPORTED)
2259 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2260 png_set_read_fn(ping,image,png_get_data);
2261#else
2262#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2263 png_permit_empty_plte(ping,MagickTrue);
2264 png_set_read_fn(ping,image,png_get_data);
2265#else
2266 mng_info->image=image;
2267 mng_info->bytes_in_read_buffer=0;
2268 mng_info->found_empty_plte=MagickFalse;
2269 mng_info->have_saved_bkgd_index=MagickFalse;
2270 png_set_read_fn(ping,mng_info,mng_get_data);
2271#endif
2272#endif
2273 }
glennrp0fe50b42010-11-16 03:52:51 +00002274
cristy3ed852e2009-09-05 21:47:34 +00002275 else
2276 png_set_read_fn(ping,image,png_get_data);
2277
2278#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2279 /* Ignore unused chunks and all unknown chunks except for vpAg */
2280 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2281 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2282 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2283 (int)sizeof(unused_chunks)/5);
2284 /* Callback for other unknown chunks */
2285 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2286#endif
2287
glennrp9bf97b62012-06-06 21:03:14 +00002288#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2289 /* Disable new libpng-1.5.10 feature */
2290 png_set_check_for_invalid_index (ping, 0);
2291#endif
2292
glennrp991e92a2010-01-28 03:09:00 +00002293#if (PNG_LIBPNG_VER < 10400)
2294# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2295 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002296 /* Disable thread-unsafe features of pnggccrd */
2297 if (png_access_version_number() >= 10200)
2298 {
2299 png_uint_32 mmx_disable_mask=0;
2300 png_uint_32 asm_flags;
2301
2302 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2303 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2304 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2305 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2306 asm_flags=png_get_asm_flags(ping);
2307 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2308 }
glennrp991e92a2010-01-28 03:09:00 +00002309# endif
cristy3ed852e2009-09-05 21:47:34 +00002310#endif
2311
2312 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002313
2314 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2315 &ping_bit_depth,&ping_color_type,
2316 &ping_interlace_method,&ping_compression_method,
2317 &ping_filter_method);
2318
2319 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2320 &ping_trans_color);
2321
2322 (void) png_get_bKGD(ping, ping_info, &ping_background);
2323
2324 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002325 {
glennrpfaa852b2010-03-30 12:17:00 +00002326 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2327 {
2328 png_set_packing(ping);
2329 ping_bit_depth = 8;
2330 }
cristy3ed852e2009-09-05 21:47:34 +00002331 }
glennrpfaa852b2010-03-30 12:17:00 +00002332
2333 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002334 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002335 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002336 if (logging != MagickFalse)
2337 {
2338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002339 " PNG width: %.20g, height: %.20g",
2340 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002341
cristy3ed852e2009-09-05 21:47:34 +00002342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2343 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002344 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002345
cristy3ed852e2009-09-05 21:47:34 +00002346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2347 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002348 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002349
cristy3ed852e2009-09-05 21:47:34 +00002350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2351 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002352 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002353 }
2354
glennrpfaa852b2010-03-30 12:17:00 +00002355#ifdef PNG_READ_iCCP_SUPPORTED
2356 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002357 {
2358 int
2359 compression;
2360
glennrpe4017e32011-01-08 17:16:09 +00002361#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002362 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002363 info;
2364#else
2365 png_bytep
2366 info;
2367#endif
2368
2369 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002370 name;
2371
2372 png_uint_32
2373 profile_length;
2374
2375 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2376 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002377
cristy3ed852e2009-09-05 21:47:34 +00002378 if (profile_length != 0)
2379 {
2380 StringInfo
2381 *profile;
2382
2383 if (logging != MagickFalse)
2384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2385 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002386 profile=BlobToStringInfo(info,profile_length);
2387 if (profile == (StringInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00002388 {
2389 png_warning(ping, "ICC profile is NULL");
2390 profile=DestroyStringInfo(profile);
2391 }
2392 else
2393 {
2394 (void) SetImageProfile(image,"icc",profile,exception);
2395 profile=DestroyStringInfo(profile);
2396 }
cristy3ed852e2009-09-05 21:47:34 +00002397 }
2398 }
2399#endif
2400#if defined(PNG_READ_sRGB_SUPPORTED)
2401 {
cristy3ed852e2009-09-05 21:47:34 +00002402 if (mng_info->have_global_srgb)
cristy2ea7a8e2012-02-08 01:04:50 +00002403 {
cristy2ea7a8e2012-02-08 01:04:50 +00002404 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2405 (mng_info->global_srgb_intent);
2406 }
glennrp0fe50b42010-11-16 03:52:51 +00002407
cristy3ed852e2009-09-05 21:47:34 +00002408 if (png_get_sRGB(ping,ping_info,&intent))
2409 {
glennrpcf002022011-01-30 02:38:15 +00002410 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2411 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002412
cristy3ed852e2009-09-05 21:47:34 +00002413 if (logging != MagickFalse)
2414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002415 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002416 }
2417 }
2418#endif
2419 {
glennrpfaa852b2010-03-30 12:17:00 +00002420 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2421 if (mng_info->have_global_gama)
2422 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002423
cristy3ed852e2009-09-05 21:47:34 +00002424 if (png_get_gAMA(ping,ping_info,&file_gamma))
2425 {
2426 image->gamma=(float) file_gamma;
2427 if (logging != MagickFalse)
2428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2429 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2430 }
2431 }
glennrpfaa852b2010-03-30 12:17:00 +00002432 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2433 {
2434 if (mng_info->have_global_chrm != MagickFalse)
2435 {
2436 (void) png_set_cHRM(ping,ping_info,
2437 mng_info->global_chrm.white_point.x,
2438 mng_info->global_chrm.white_point.y,
2439 mng_info->global_chrm.red_primary.x,
2440 mng_info->global_chrm.red_primary.y,
2441 mng_info->global_chrm.green_primary.x,
2442 mng_info->global_chrm.green_primary.y,
2443 mng_info->global_chrm.blue_primary.x,
2444 mng_info->global_chrm.blue_primary.y);
2445 }
2446 }
glennrp0fe50b42010-11-16 03:52:51 +00002447
glennrpfaa852b2010-03-30 12:17:00 +00002448 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002449 {
2450 (void) png_get_cHRM(ping,ping_info,
2451 &image->chromaticity.white_point.x,
2452 &image->chromaticity.white_point.y,
2453 &image->chromaticity.red_primary.x,
2454 &image->chromaticity.red_primary.y,
2455 &image->chromaticity.green_primary.x,
2456 &image->chromaticity.green_primary.y,
2457 &image->chromaticity.blue_primary.x,
2458 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002459
cristy3ed852e2009-09-05 21:47:34 +00002460 if (logging != MagickFalse)
2461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2462 " Reading PNG cHRM chunk.");
2463 }
glennrp0fe50b42010-11-16 03:52:51 +00002464
glennrpe610a072010-08-05 17:08:46 +00002465 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002466 {
glennrpe610a072010-08-05 17:08:46 +00002467 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002468 Magick_RenderingIntent_to_PNG_RenderingIntent
2469 (image->rendering_intent));
cristyda7803d2012-05-03 01:22:45 +00002470 png_set_gAMA(ping,ping_info,1.000f/2.200f);
glennrpfaa852b2010-03-30 12:17:00 +00002471 png_set_cHRM(ping,ping_info,
2472 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2473 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002474 }
cristy3ed852e2009-09-05 21:47:34 +00002475#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002476 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002477 {
cristy905ef802011-02-23 00:29:18 +00002478 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2479 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002480
cristy3ed852e2009-09-05 21:47:34 +00002481 if (logging != MagickFalse)
2482 if (image->page.x || image->page.y)
2483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002484 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2485 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002486 }
2487#endif
2488#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002489 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2490 {
2491 if (mng_info->have_global_phys)
2492 {
2493 png_set_pHYs(ping,ping_info,
2494 mng_info->global_x_pixels_per_unit,
2495 mng_info->global_y_pixels_per_unit,
2496 mng_info->global_phys_unit_type);
2497 }
2498 }
2499
2500 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002501 {
cristy3ed852e2009-09-05 21:47:34 +00002502 /*
2503 Set image resolution.
2504 */
2505 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002506 &unit_type);
cristy16ea1392012-03-21 20:38:41 +00002507 image->resolution.x=(double) x_resolution;
2508 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002509
cristy3ed852e2009-09-05 21:47:34 +00002510 if (unit_type == PNG_RESOLUTION_METER)
2511 {
2512 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00002513 image->resolution.x=(double) x_resolution/100.0;
2514 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002515 }
glennrp0fe50b42010-11-16 03:52:51 +00002516
cristy3ed852e2009-09-05 21:47:34 +00002517 if (logging != MagickFalse)
2518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002519 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2520 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002521 }
cristy3ed852e2009-09-05 21:47:34 +00002522#endif
glennrp823b55c2011-03-14 18:46:46 +00002523
glennrpfaa852b2010-03-30 12:17:00 +00002524 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002525 {
2526 int
2527 number_colors;
2528
2529 png_colorp
2530 palette;
2531
2532 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002533
cristy3ed852e2009-09-05 21:47:34 +00002534 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002535 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002536 {
2537 if (mng_info->global_plte_length)
2538 {
2539 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2540 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002541
glennrpfaa852b2010-03-30 12:17:00 +00002542 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrpedaa0382012-04-12 14:16:21 +00002543 {
cristy3ed852e2009-09-05 21:47:34 +00002544 if (mng_info->global_trns_length)
2545 {
glennrpedaa0382012-04-12 14:16:21 +00002546 png_warning(ping,
2547 "global tRNS has more entries than global PLTE");
cristy3ed852e2009-09-05 21:47:34 +00002548 }
glennrpedaa0382012-04-12 14:16:21 +00002549 else
2550 {
2551 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2552 (int) mng_info->global_trns_length,NULL);
2553 }
2554 }
glennrpbfd9e612011-04-22 14:02:20 +00002555#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002556 if (
2557#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2558 mng_info->have_saved_bkgd_index ||
2559#endif
glennrpfaa852b2010-03-30 12:17:00 +00002560 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002561 {
2562 png_color_16
2563 background;
2564
2565#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2566 if (mng_info->have_saved_bkgd_index)
2567 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002568#endif
glennrpfaa852b2010-03-30 12:17:00 +00002569 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2570 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002571
cristy3ed852e2009-09-05 21:47:34 +00002572 background.red=(png_uint_16)
2573 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002574
cristy3ed852e2009-09-05 21:47:34 +00002575 background.green=(png_uint_16)
2576 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002577
cristy3ed852e2009-09-05 21:47:34 +00002578 background.blue=(png_uint_16)
2579 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002580
glennrpc6c391a2011-04-27 02:23:56 +00002581 background.gray=(png_uint_16)
2582 mng_info->global_plte[background.index].green;
2583
cristy3ed852e2009-09-05 21:47:34 +00002584 png_set_bKGD(ping,ping_info,&background);
2585 }
2586#endif
2587 }
2588 else
glennrpedaa0382012-04-12 14:16:21 +00002589 png_error(ping,"No global PLTE in file");
cristy3ed852e2009-09-05 21:47:34 +00002590 }
2591 }
2592
glennrpbfd9e612011-04-22 14:02:20 +00002593#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002594 if (mng_info->have_global_bkgd &&
2595 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002596 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002597
glennrpfaa852b2010-03-30 12:17:00 +00002598 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002599 {
glennrpbfd9e612011-04-22 14:02:20 +00002600 unsigned int
2601 bkgd_scale;
2602
cristy3ed852e2009-09-05 21:47:34 +00002603 /*
2604 Set image background color.
2605 */
2606 if (logging != MagickFalse)
2607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2608 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002609
glennrpbfd9e612011-04-22 14:02:20 +00002610 /* Scale background components to 16-bit, then scale
2611 * to quantum depth
2612 */
2613 if (logging != MagickFalse)
2614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2615 " raw ping_background=(%d,%d,%d).",ping_background->red,
2616 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002617
glennrpbfd9e612011-04-22 14:02:20 +00002618 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002619
glennrpbfd9e612011-04-22 14:02:20 +00002620 if (ping_bit_depth == 1)
2621 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002622
glennrpbfd9e612011-04-22 14:02:20 +00002623 else if (ping_bit_depth == 2)
2624 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002625
glennrpbfd9e612011-04-22 14:02:20 +00002626 else if (ping_bit_depth == 4)
2627 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002628
glennrpbfd9e612011-04-22 14:02:20 +00002629 if (ping_bit_depth <= 8)
2630 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002631
glennrpbfd9e612011-04-22 14:02:20 +00002632 ping_background->red *= bkgd_scale;
2633 ping_background->green *= bkgd_scale;
2634 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002635
glennrpbfd9e612011-04-22 14:02:20 +00002636 if (logging != MagickFalse)
2637 {
glennrp2cbb4482010-06-02 04:37:24 +00002638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2639 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002640
glennrp2cbb4482010-06-02 04:37:24 +00002641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2642 " ping_background=(%d,%d,%d).",ping_background->red,
2643 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002644 }
glennrp2cbb4482010-06-02 04:37:24 +00002645
glennrpbfd9e612011-04-22 14:02:20 +00002646 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002647 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002648
glennrpbfd9e612011-04-22 14:02:20 +00002649 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002650 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002651
glennrpbfd9e612011-04-22 14:02:20 +00002652 image->background_color.blue=
2653 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002654
cristy16ea1392012-03-21 20:38:41 +00002655 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002656
glennrpbfd9e612011-04-22 14:02:20 +00002657 if (logging != MagickFalse)
2658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2659 " image->background_color=(%.20g,%.20g,%.20g).",
2660 (double) image->background_color.red,
2661 (double) image->background_color.green,
2662 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002663 }
glennrpbfd9e612011-04-22 14:02:20 +00002664#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002665
glennrpfaa852b2010-03-30 12:17:00 +00002666 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002667 {
2668 /*
glennrpa6a06632011-01-19 15:15:34 +00002669 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002670 */
2671 int
2672 max_sample;
2673
cristy35ef8242010-06-03 16:24:13 +00002674 size_t
2675 one=1;
2676
cristy3ed852e2009-09-05 21:47:34 +00002677 if (logging != MagickFalse)
2678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2679 " Reading PNG tRNS chunk.");
2680
cristyf9cca6a2010-06-04 23:49:28 +00002681 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002682
glennrpfaa852b2010-03-30 12:17:00 +00002683 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2684 (int)ping_trans_color->gray > max_sample) ||
2685 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2686 ((int)ping_trans_color->red > max_sample ||
2687 (int)ping_trans_color->green > max_sample ||
2688 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002689 {
2690 if (logging != MagickFalse)
2691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2692 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002693 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002694 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002695 image->matte=MagickFalse;
2696 }
2697 else
2698 {
glennrpa6a06632011-01-19 15:15:34 +00002699 int
2700 scale_to_short;
2701
2702 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2703
2704 /* Scale transparent_color to short */
2705 transparent_color.red= scale_to_short*ping_trans_color->red;
2706 transparent_color.green= scale_to_short*ping_trans_color->green;
2707 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy16ea1392012-03-21 20:38:41 +00002708 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002709
glennrpfaa852b2010-03-30 12:17:00 +00002710 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002711 {
glennrp0f111982010-07-07 20:18:33 +00002712 if (logging != MagickFalse)
2713 {
2714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2715 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002716
glennrp0f111982010-07-07 20:18:33 +00002717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00002718 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002719 }
cristy16ea1392012-03-21 20:38:41 +00002720 transparent_color.red=transparent_color.alpha;
2721 transparent_color.green=transparent_color.alpha;
2722 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002723 }
2724 }
2725 }
2726#if defined(PNG_READ_sBIT_SUPPORTED)
2727 if (mng_info->have_global_sbit)
2728 {
glennrpfaa852b2010-03-30 12:17:00 +00002729 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002730 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2731 }
2732#endif
2733 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002734
cristy3ed852e2009-09-05 21:47:34 +00002735 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002736
2737 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2738
cristy3ed852e2009-09-05 21:47:34 +00002739 /*
2740 Initialize image structure.
2741 */
2742 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002743 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002744 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002745 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002746 if (mng_info->mng_type == 0)
2747 {
glennrpfaa852b2010-03-30 12:17:00 +00002748 mng_info->mng_width=ping_width;
2749 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002750 mng_info->frame=mng_info->image_box;
2751 mng_info->clip=mng_info->image_box;
2752 }
glennrp0fe50b42010-11-16 03:52:51 +00002753
cristy3ed852e2009-09-05 21:47:34 +00002754 else
2755 {
2756 image->page.y=mng_info->y_off[mng_info->object_id];
2757 }
glennrp0fe50b42010-11-16 03:52:51 +00002758
cristy3ed852e2009-09-05 21:47:34 +00002759 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002760 image->columns=ping_width;
2761 image->rows=ping_height;
glennrpa6c5d342012-05-14 13:21:17 +00002762
cristy16ea1392012-03-21 20:38:41 +00002763 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2764 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
glennrp8d0bca52012-05-17 02:33:23 +00002765 {
2766 if (!png_get_valid(ping,ping_info,PNG_INFO_gAMA) &&
2767 !png_get_valid(ping,ping_info,PNG_INFO_cHRM) &&
2768 !png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2769 {
2770 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
2771 * and reset image->chromaticity.
2772 */
2773 SetImageColorspace(image,GRAYColorspace,exception);
2774 }
2775
2776 else
2777 {
2778 /* Use colorspace data from PNG ancillary chunks */
2779 image->colorspace=GRAYColorspace;
2780 }
2781 }
glennrpa6c5d342012-05-14 13:21:17 +00002782
glennrpfaa852b2010-03-30 12:17:00 +00002783 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002784 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002785 {
cristybefe4d22010-06-07 01:18:58 +00002786 size_t
2787 one;
2788
cristy3ed852e2009-09-05 21:47:34 +00002789 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002790 one=1;
2791 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002792#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2793 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002794 image->colors=256;
2795#else
2796 if (image->colors > 65536L)
2797 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002798#endif
glennrpfaa852b2010-03-30 12:17:00 +00002799 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002800 {
2801 int
2802 number_colors;
2803
2804 png_colorp
2805 palette;
2806
2807 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002808 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002809
cristy3ed852e2009-09-05 21:47:34 +00002810 if (logging != MagickFalse)
2811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2812 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2813 }
2814 }
2815
2816 if (image->storage_class == PseudoClass)
2817 {
2818 /*
2819 Initialize image colormap.
2820 */
cristy16ea1392012-03-21 20:38:41 +00002821 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
glennrpedaa0382012-04-12 14:16:21 +00002822 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00002823
glennrpfaa852b2010-03-30 12:17:00 +00002824 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002825 {
2826 int
2827 number_colors;
2828
2829 png_colorp
2830 palette;
2831
2832 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002833
glennrp6af6cf12011-04-22 13:05:16 +00002834 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002835 {
2836 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2837 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2838 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2839 }
glennrp6af6cf12011-04-22 13:05:16 +00002840
glennrp67b9c1a2011-04-22 18:47:36 +00002841 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002842 {
2843 image->colormap[i].red=0;
2844 image->colormap[i].green=0;
2845 image->colormap[i].blue=0;
2846 }
cristy3ed852e2009-09-05 21:47:34 +00002847 }
glennrp0fe50b42010-11-16 03:52:51 +00002848
cristy3ed852e2009-09-05 21:47:34 +00002849 else
2850 {
cristybb503372010-05-27 20:51:26 +00002851 size_t
cristy3ed852e2009-09-05 21:47:34 +00002852 scale;
2853
glennrpfaa852b2010-03-30 12:17:00 +00002854 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002855
cristy3ed852e2009-09-05 21:47:34 +00002856 if (scale < 1)
2857 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002858
cristybb503372010-05-27 20:51:26 +00002859 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002860 {
2861 image->colormap[i].red=(Quantum) (i*scale);
2862 image->colormap[i].green=(Quantum) (i*scale);
2863 image->colormap[i].blue=(Quantum) (i*scale);
2864 }
2865 }
2866 }
glennrp147bc912011-03-30 18:47:21 +00002867
glennrpcb395ac2011-03-30 19:50:23 +00002868 /* Set some properties for reporting by "identify" */
2869 {
glennrp147bc912011-03-30 18:47:21 +00002870 char
2871 msg[MaxTextExtent];
2872
2873 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2874 ping_interlace_method in value */
2875
cristy3b6fd2e2011-05-20 12:53:50 +00002876 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002877 "%d, %d",(int) ping_width, (int) ping_height);
cristy16ea1392012-03-21 20:38:41 +00002878 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002879
cristy3b6fd2e2011-05-20 12:53:50 +00002880 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristy16ea1392012-03-21 20:38:41 +00002881 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002882
glennrp5dff4352012-06-06 22:12:04 +00002883 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
2884 (int) ping_color_type,
2885 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
cristy16ea1392012-03-21 20:38:41 +00002886 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002887
cristy3b6fd2e2011-05-20 12:53:50 +00002888 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002889 (int) ping_interlace_method);
cristy16ea1392012-03-21 20:38:41 +00002890 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00002891 }
glennrp147bc912011-03-30 18:47:21 +00002892
cristy3ed852e2009-09-05 21:47:34 +00002893 /*
2894 Read image scanlines.
2895 */
2896 if (image->delay != 0)
2897 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002898
glennrp0ca69b12010-07-26 01:57:52 +00002899 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002900 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2901 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002902 {
glennrp1b888c42012-03-02 15:18:14 +00002903 /* This happens later in non-ping decodes */
2904 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2905 image->storage_class=DirectClass;
2906
cristy3ed852e2009-09-05 21:47:34 +00002907 if (logging != MagickFalse)
2908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002909 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002910 mng_info->scenes_found-1);
2911 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002912
2913#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002914 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002915#endif
glennrpedaa0382012-04-12 14:16:21 +00002916
cristy3ed852e2009-09-05 21:47:34 +00002917 if (logging != MagickFalse)
2918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2919 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002920
cristy3ed852e2009-09-05 21:47:34 +00002921 return(image);
2922 }
glennrp0fe50b42010-11-16 03:52:51 +00002923
cristy3ed852e2009-09-05 21:47:34 +00002924 if (logging != MagickFalse)
2925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2926 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002927
cristy3ed852e2009-09-05 21:47:34 +00002928 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002929 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2930 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002931
cristy3ed852e2009-09-05 21:47:34 +00002932 else
glennrpcf002022011-01-30 02:38:15 +00002933 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2934 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002935
glennrpcf002022011-01-30 02:38:15 +00002936 if (ping_pixels == (unsigned char *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00002937 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00002938
cristy3ed852e2009-09-05 21:47:34 +00002939 if (logging != MagickFalse)
2940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2941 " Converting PNG pixels to pixel packets");
2942 /*
2943 Convert PNG pixels to pixel packets.
2944 */
cristy16ea1392012-03-21 20:38:41 +00002945 quantum_info=AcquireQuantumInfo(image_info,image);
2946
2947 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00002948 png_error(ping,"Failed to allocate quantum_info");
glennrp0fe50b42010-11-16 03:52:51 +00002949
glennrpc8cbc5d2011-01-01 00:12:34 +00002950 {
2951
2952 MagickBooleanType
2953 found_transparent_pixel;
2954
2955 found_transparent_pixel=MagickFalse;
2956
cristy3ed852e2009-09-05 21:47:34 +00002957 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002958 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002959 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002960 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002961 /*
2962 Convert image to DirectClass pixel packets.
2963 */
glennrp67b9c1a2011-04-22 18:47:36 +00002964#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2965 int
2966 depth;
2967
2968 depth=(ssize_t) ping_bit_depth;
2969#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002970 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2971 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2972 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2973 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002974
glennrpc8cbc5d2011-01-01 00:12:34 +00002975 for (y=0; y < (ssize_t) image->rows; y++)
2976 {
2977 if (num_passes > 1)
2978 row_offset=ping_rowbytes*y;
2979
2980 else
2981 row_offset=0;
2982
glennrpcf002022011-01-30 02:38:15 +00002983 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy862a33c2012-05-17 22:49:37 +00002984 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002985
cristy16ea1392012-03-21 20:38:41 +00002986 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002987 break;
2988
cristy16ea1392012-03-21 20:38:41 +00002989 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2990 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2991 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002992
cristy16ea1392012-03-21 20:38:41 +00002993 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2994 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2995 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002996
cristy16ea1392012-03-21 20:38:41 +00002997 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2998 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2999 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003000
cristy16ea1392012-03-21 20:38:41 +00003001 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3002 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3003 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003004
cristy16ea1392012-03-21 20:38:41 +00003005 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3006 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3007 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00003008
glennrpc8cbc5d2011-01-01 00:12:34 +00003009 if (found_transparent_pixel == MagickFalse)
3010 {
3011 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00003012 if (y== 0 && logging != MagickFalse)
3013 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3014 " Looking for cheap transparent pixel");
3015
glennrpc8cbc5d2011-01-01 00:12:34 +00003016 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3017 {
glennrp5aa37f62011-01-02 03:07:57 +00003018 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3019 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy16ea1392012-03-21 20:38:41 +00003020 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00003021 {
glennrpa6a06632011-01-19 15:15:34 +00003022 if (logging != MagickFalse)
3023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3024 " ...got one.");
3025
glennrpc8cbc5d2011-01-01 00:12:34 +00003026 found_transparent_pixel = MagickTrue;
3027 break;
3028 }
glennrp4f25bd02011-01-01 18:51:28 +00003029 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3030 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
cristy16ea1392012-03-21 20:38:41 +00003031 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3032 transparent_color.red &&
3033 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3034 transparent_color.green &&
3035 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3036 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00003037 {
glennrpa6a06632011-01-19 15:15:34 +00003038 if (logging != MagickFalse)
3039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3040 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00003041 found_transparent_pixel = MagickTrue;
3042 break;
3043 }
cristy16ea1392012-03-21 20:38:41 +00003044 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00003045 }
3046 }
3047
3048 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3049 {
3050 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3051 image->rows);
3052
3053 if (status == MagickFalse)
3054 break;
3055 }
3056 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3057 break;
3058 }
3059
3060 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3061 {
3062 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003063 if (status == MagickFalse)
3064 break;
3065 }
cristy3ed852e2009-09-05 21:47:34 +00003066 }
cristy3ed852e2009-09-05 21:47:34 +00003067 }
glennrp0fe50b42010-11-16 03:52:51 +00003068
cristy3ed852e2009-09-05 21:47:34 +00003069 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003070
cristy3ed852e2009-09-05 21:47:34 +00003071 for (pass=0; pass < num_passes; pass++)
3072 {
3073 Quantum
3074 *quantum_scanline;
3075
3076 register Quantum
3077 *r;
3078
3079 /*
3080 Convert grayscale image to PseudoClass pixel packets.
3081 */
glennrpc17d96f2011-06-27 01:20:11 +00003082 if (logging != MagickFalse)
3083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3084 " Converting grayscale pixels to pixel packets");
cristy16ea1392012-03-21 20:38:41 +00003085
glennrpfaa852b2010-03-30 12:17:00 +00003086 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00003087 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003088
cristy3ed852e2009-09-05 21:47:34 +00003089 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3090 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003091
cristy3ed852e2009-09-05 21:47:34 +00003092 if (quantum_scanline == (Quantum *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003093 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003094
cristybb503372010-05-27 20:51:26 +00003095 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003096 {
3097 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003098 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003099
cristy3ed852e2009-09-05 21:47:34 +00003100 else
3101 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003102
glennrpcf002022011-01-30 02:38:15 +00003103 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003104 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003105
cristy16ea1392012-03-21 20:38:41 +00003106 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003107 break;
glennrp0fe50b42010-11-16 03:52:51 +00003108
glennrpcf002022011-01-30 02:38:15 +00003109 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003110 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003111
glennrpfaa852b2010-03-30 12:17:00 +00003112 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003113 {
3114 case 1:
3115 {
cristybb503372010-05-27 20:51:26 +00003116 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003117 bit;
3118
cristybb503372010-05-27 20:51:26 +00003119 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003120 {
3121 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003122 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003123 p++;
3124 }
glennrp0fe50b42010-11-16 03:52:51 +00003125
cristy3ed852e2009-09-05 21:47:34 +00003126 if ((image->columns % 8) != 0)
3127 {
cristybb503372010-05-27 20:51:26 +00003128 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003129 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003130 }
glennrp0fe50b42010-11-16 03:52:51 +00003131
cristy3ed852e2009-09-05 21:47:34 +00003132 break;
3133 }
glennrp47b9dd52010-11-24 18:12:06 +00003134
cristy3ed852e2009-09-05 21:47:34 +00003135 case 2:
3136 {
cristybb503372010-05-27 20:51:26 +00003137 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003138 {
glennrpa18d5bc2011-04-23 14:51:34 +00003139 *r++=(*p >> 6) & 0x03;
3140 *r++=(*p >> 4) & 0x03;
3141 *r++=(*p >> 2) & 0x03;
3142 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003143 }
glennrp0fe50b42010-11-16 03:52:51 +00003144
cristy3ed852e2009-09-05 21:47:34 +00003145 if ((image->columns % 4) != 0)
3146 {
cristybb503372010-05-27 20:51:26 +00003147 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003148 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003149 }
glennrp0fe50b42010-11-16 03:52:51 +00003150
cristy3ed852e2009-09-05 21:47:34 +00003151 break;
3152 }
glennrp47b9dd52010-11-24 18:12:06 +00003153
cristy3ed852e2009-09-05 21:47:34 +00003154 case 4:
3155 {
cristybb503372010-05-27 20:51:26 +00003156 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003157 {
glennrpa18d5bc2011-04-23 14:51:34 +00003158 *r++=(*p >> 4) & 0x0f;
3159 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003160 }
glennrp0fe50b42010-11-16 03:52:51 +00003161
cristy3ed852e2009-09-05 21:47:34 +00003162 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003163 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003164
cristy3ed852e2009-09-05 21:47:34 +00003165 break;
3166 }
glennrp47b9dd52010-11-24 18:12:06 +00003167
cristy3ed852e2009-09-05 21:47:34 +00003168 case 8:
3169 {
glennrpfaa852b2010-03-30 12:17:00 +00003170 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003171 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003172 {
glennrpa18d5bc2011-04-23 14:51:34 +00003173 *r++=*p++;
cristy16ea1392012-03-21 20:38:41 +00003174 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3175 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003176 found_transparent_pixel = MagickTrue;
cristy16ea1392012-03-21 20:38:41 +00003177 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003178 }
glennrp0fe50b42010-11-16 03:52:51 +00003179
cristy3ed852e2009-09-05 21:47:34 +00003180 else
cristybb503372010-05-27 20:51:26 +00003181 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003182 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003183
cristy3ed852e2009-09-05 21:47:34 +00003184 break;
3185 }
glennrp47b9dd52010-11-24 18:12:06 +00003186
cristy3ed852e2009-09-05 21:47:34 +00003187 case 16:
3188 {
cristybb503372010-05-27 20:51:26 +00003189 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003190 {
glennrpc17d96f2011-06-27 01:20:11 +00003191#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003192 size_t
3193 quantum;
3194
3195 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003196 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003197
3198 else
glennrpc17d96f2011-06-27 01:20:11 +00003199 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003200
glennrp58f77c72011-04-23 14:09:09 +00003201 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003202 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003203 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003204
3205 if (ping_color_type == 4)
3206 {
glennrpc17d96f2011-06-27 01:20:11 +00003207 if (image->colors > 256)
3208 quantum=((*p++) << 8);
3209 else
3210 quantum=0;
3211
glennrp58f77c72011-04-23 14:09:09 +00003212 quantum|=(*p++);
cristy16ea1392012-03-21 20:38:41 +00003213 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3214 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003215 found_transparent_pixel = MagickTrue;
cristy16ea1392012-03-21 20:38:41 +00003216 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003217 }
glennrp58f77c72011-04-23 14:09:09 +00003218
3219#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3220 *r++=(*p++);
3221 p++; /* strip low byte */
3222
3223 if (ping_color_type == 4)
3224 {
cristy16ea1392012-03-21 20:38:41 +00003225 SetPixelAlpha(image,*p++,q);
3226 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003227 found_transparent_pixel = MagickTrue;
3228 p++;
cristy16ea1392012-03-21 20:38:41 +00003229 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003230 }
cristy3ed852e2009-09-05 21:47:34 +00003231#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003232 }
glennrp47b9dd52010-11-24 18:12:06 +00003233
cristy3ed852e2009-09-05 21:47:34 +00003234 break;
3235 }
glennrp47b9dd52010-11-24 18:12:06 +00003236
cristy3ed852e2009-09-05 21:47:34 +00003237 default:
3238 break;
3239 }
glennrp3faa9a32011-04-23 14:00:25 +00003240
cristy3ed852e2009-09-05 21:47:34 +00003241 /*
3242 Transfer image scanline.
3243 */
3244 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003245
cristy16ea1392012-03-21 20:38:41 +00003246 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3247
3248 if (q == (Quantum *) NULL)
3249 break;
cristybb503372010-05-27 20:51:26 +00003250 for (x=0; x < (ssize_t) image->columns; x++)
cristy16ea1392012-03-21 20:38:41 +00003251 {
3252 SetPixelIndex(image,*r++,q);
3253 q+=GetPixelChannels(image);
3254 }
glennrp0fe50b42010-11-16 03:52:51 +00003255
cristy3ed852e2009-09-05 21:47:34 +00003256 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3257 break;
glennrp0fe50b42010-11-16 03:52:51 +00003258
cristy7a287bf2010-02-14 02:18:09 +00003259 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3260 {
cristycee97112010-05-28 00:44:52 +00003261 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003262 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003263
cristy7a287bf2010-02-14 02:18:09 +00003264 if (status == MagickFalse)
3265 break;
3266 }
cristy3ed852e2009-09-05 21:47:34 +00003267 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003268
cristy7a287bf2010-02-14 02:18:09 +00003269 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003270 {
3271 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003272
cristy3ed852e2009-09-05 21:47:34 +00003273 if (status == MagickFalse)
3274 break;
3275 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003276
cristy3ed852e2009-09-05 21:47:34 +00003277 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3278 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003279
3280 image->matte=found_transparent_pixel;
3281
3282 if (logging != MagickFalse)
3283 {
3284 if (found_transparent_pixel != MagickFalse)
3285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3286 " Found transparent pixel");
3287 else
glennrp5aa37f62011-01-02 03:07:57 +00003288 {
3289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3290 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003291
glennrp5aa37f62011-01-02 03:07:57 +00003292 ping_color_type&=0x03;
3293 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003294 }
3295 }
3296
cristy16ea1392012-03-21 20:38:41 +00003297 if (quantum_info != (QuantumInfo *) NULL)
3298 quantum_info=DestroyQuantumInfo(quantum_info);
3299
cristy5c6f7892010-05-05 22:53:29 +00003300 if (image->storage_class == PseudoClass)
3301 {
cristyaeb2cbc2010-05-07 13:28:58 +00003302 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003303 matte;
3304
3305 matte=image->matte;
3306 image->matte=MagickFalse;
cristy16ea1392012-03-21 20:38:41 +00003307 (void) SyncImage(image,exception);
cristyaeb2cbc2010-05-07 13:28:58 +00003308 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003309 }
glennrp47b9dd52010-11-24 18:12:06 +00003310
glennrp4eb39312011-03-30 21:34:55 +00003311 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003312
3313 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003314 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003315 {
3316 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003317 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003318 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00003319 (void) SetImageBackgroundColor(image,exception);
glennrpedaa0382012-04-12 14:16:21 +00003320#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00003321 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003322#endif
3323 if (logging != MagickFalse)
3324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3325 " exit ReadOnePNGImage() early.");
3326 return(image);
3327 }
glennrp47b9dd52010-11-24 18:12:06 +00003328
glennrpfaa852b2010-03-30 12:17:00 +00003329 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003330 {
3331 ClassType
3332 storage_class;
3333
3334 /*
3335 Image has a transparent background.
3336 */
3337 storage_class=image->storage_class;
3338 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003339
glennrp3c218112010-11-27 15:31:26 +00003340/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003341
glennrp0fe50b42010-11-16 03:52:51 +00003342 if (storage_class == PseudoClass)
3343 {
3344 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003345 {
glennrp0fe50b42010-11-16 03:52:51 +00003346 for (x=0; x < ping_num_trans; x++)
3347 {
cristy16ea1392012-03-21 20:38:41 +00003348 image->colormap[x].matte=MagickTrue;
3349 image->colormap[x].alpha =
3350 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003351 }
glennrpc11cf6a2010-03-20 16:46:19 +00003352 }
glennrp47b9dd52010-11-24 18:12:06 +00003353
glennrp0fe50b42010-11-16 03:52:51 +00003354 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3355 {
3356 for (x=0; x < (int) image->colors; x++)
3357 {
3358 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy16ea1392012-03-21 20:38:41 +00003359 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003360 {
cristy16ea1392012-03-21 20:38:41 +00003361 image->colormap[x].matte=MagickTrue;
3362 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003363 }
3364 }
3365 }
cristy16ea1392012-03-21 20:38:41 +00003366 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003367 }
glennrp47b9dd52010-11-24 18:12:06 +00003368
glennrpa6a06632011-01-19 15:15:34 +00003369#if 1 /* Should have already been done above, but glennrp problem P10
3370 * needs this.
3371 */
glennrp0fe50b42010-11-16 03:52:51 +00003372 else
3373 {
3374 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003375 {
glennrp0fe50b42010-11-16 03:52:51 +00003376 image->storage_class=storage_class;
3377 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3378
cristy16ea1392012-03-21 20:38:41 +00003379 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003380 break;
3381
glennrp0fe50b42010-11-16 03:52:51 +00003382
glennrpa6a06632011-01-19 15:15:34 +00003383 /* Caution: on a Q8 build, this does not distinguish between
3384 * 16-bit colors that differ only in the low byte
3385 */
glennrp0fe50b42010-11-16 03:52:51 +00003386 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3387 {
cristy16ea1392012-03-21 20:38:41 +00003388 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3389 transparent_color.red &&
3390 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3391 transparent_color.green &&
3392 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3393 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003394 {
cristy16ea1392012-03-21 20:38:41 +00003395 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003396 }
glennrp0fe50b42010-11-16 03:52:51 +00003397
glennrp67b9c1a2011-04-22 18:47:36 +00003398#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003399 else
glennrp4f25bd02011-01-01 18:51:28 +00003400 {
cristy16ea1392012-03-21 20:38:41 +00003401 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003402 }
glennrpa6a06632011-01-19 15:15:34 +00003403#endif
glennrp0fe50b42010-11-16 03:52:51 +00003404
cristy16ea1392012-03-21 20:38:41 +00003405 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003406 }
3407
3408 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3409 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003410 }
glennrp0fe50b42010-11-16 03:52:51 +00003411 }
glennrpa6a06632011-01-19 15:15:34 +00003412#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003413
cristy3ed852e2009-09-05 21:47:34 +00003414 image->storage_class=DirectClass;
3415 }
glennrp3c218112010-11-27 15:31:26 +00003416
cristyeb3b22a2011-03-31 20:16:11 +00003417 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003418 {
3419 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003420 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3421 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003422 else
glennrpa0ed0092011-04-18 16:36:29 +00003423 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3424 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003425
glennrp4eb39312011-03-30 21:34:55 +00003426 if (status != MagickFalse)
3427 for (i=0; i < (ssize_t) num_text; i++)
3428 {
3429 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003430
glennrp4eb39312011-03-30 21:34:55 +00003431 if (logging != MagickFalse)
3432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3433 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003434
glennrp4eb39312011-03-30 21:34:55 +00003435 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003436 {
glennrpedaa0382012-04-12 14:16:21 +00003437 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3438 (int) i,exception);
glennrp4eb39312011-03-30 21:34:55 +00003439 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003440 }
glennrp0fe50b42010-11-16 03:52:51 +00003441
glennrp4eb39312011-03-30 21:34:55 +00003442 else
3443 {
3444 char
3445 *value;
3446
3447 length=text[i].text_length;
3448 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3449 sizeof(*value));
3450 if (value == (char *) NULL)
3451 {
glennrpedaa0382012-04-12 14:16:21 +00003452 png_error(ping,"Memory allocation failed");
glennrp4eb39312011-03-30 21:34:55 +00003453 break;
3454 }
3455 *value='\0';
3456 (void) ConcatenateMagickString(value,text[i].text,length+2);
3457
3458 /* Don't save "density" or "units" property if we have a pHYs
3459 * chunk
3460 */
3461 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3462 (LocaleCompare(text[i].key,"density") != 0 &&
3463 LocaleCompare(text[i].key,"units") != 0))
cristy16ea1392012-03-21 20:38:41 +00003464 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003465
3466 if (logging != MagickFalse)
3467 {
3468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3469 " length: %lu",(unsigned long) length);
3470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3471 " Keyword: %s",text[i].key);
3472 }
3473
3474 value=DestroyString(value);
3475 }
3476 }
3477 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003478 }
glennrp3c218112010-11-27 15:31:26 +00003479
cristy3ed852e2009-09-05 21:47:34 +00003480#ifdef MNG_OBJECT_BUFFERS
3481 /*
3482 Store the object if necessary.
3483 */
3484 if (object_id && !mng_info->frozen[object_id])
3485 {
3486 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3487 {
3488 /*
3489 create a new object buffer.
3490 */
3491 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003492 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003493
cristy3ed852e2009-09-05 21:47:34 +00003494 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3495 {
3496 mng_info->ob[object_id]->image=(Image *) NULL;
3497 mng_info->ob[object_id]->reference_count=1;
3498 }
3499 }
glennrp47b9dd52010-11-24 18:12:06 +00003500
cristy3ed852e2009-09-05 21:47:34 +00003501 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3502 mng_info->ob[object_id]->frozen)
3503 {
3504 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003505 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003506
cristy3ed852e2009-09-05 21:47:34 +00003507 if (mng_info->ob[object_id]->frozen)
glennrpedaa0382012-04-12 14:16:21 +00003508 png_error(ping,"Cannot overwrite frozen MNG object buffer");
cristy3ed852e2009-09-05 21:47:34 +00003509 }
glennrp0fe50b42010-11-16 03:52:51 +00003510
cristy3ed852e2009-09-05 21:47:34 +00003511 else
3512 {
cristy3ed852e2009-09-05 21:47:34 +00003513
3514 if (mng_info->ob[object_id]->image != (Image *) NULL)
3515 mng_info->ob[object_id]->image=DestroyImage
3516 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003517
cristy3ed852e2009-09-05 21:47:34 +00003518 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristy16ea1392012-03-21 20:38:41 +00003519 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003520
cristy3ed852e2009-09-05 21:47:34 +00003521 if (mng_info->ob[object_id]->image != (Image *) NULL)
3522 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003523
cristy3ed852e2009-09-05 21:47:34 +00003524 else
glennrpedaa0382012-04-12 14:16:21 +00003525 png_error(ping, "Cloning image for object buffer failed");
glennrp0fe50b42010-11-16 03:52:51 +00003526
glennrpfaa852b2010-03-30 12:17:00 +00003527 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003528 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003529
glennrpfaa852b2010-03-30 12:17:00 +00003530 mng_info->ob[object_id]->width=ping_width;
3531 mng_info->ob[object_id]->height=ping_height;
3532 mng_info->ob[object_id]->color_type=ping_color_type;
3533 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3534 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3535 mng_info->ob[object_id]->compression_method=
3536 ping_compression_method;
3537 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003538
glennrpfaa852b2010-03-30 12:17:00 +00003539 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003540 {
3541 int
3542 number_colors;
3543
3544 png_colorp
3545 plte;
3546
3547 /*
3548 Copy the PLTE to the object buffer.
3549 */
3550 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3551 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003552
cristy3ed852e2009-09-05 21:47:34 +00003553 for (i=0; i < number_colors; i++)
3554 {
3555 mng_info->ob[object_id]->plte[i]=plte[i];
3556 }
3557 }
glennrp47b9dd52010-11-24 18:12:06 +00003558
cristy3ed852e2009-09-05 21:47:34 +00003559 else
3560 mng_info->ob[object_id]->plte_length=0;
3561 }
3562 }
3563#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003564
3565 /* Set image->matte to MagickTrue if the input colortype supports
3566 * alpha or if a valid tRNS chunk is present, no matter whether there
3567 * is actual transparency present.
3568 */
3569 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3570 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3571 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3572 MagickTrue : MagickFalse;
3573
glennrpcb395ac2011-03-30 19:50:23 +00003574 /* Set more properties for identify to retrieve */
3575 {
3576 char
3577 msg[MaxTextExtent];
3578
glennrp4eb39312011-03-30 21:34:55 +00003579 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003580 {
3581 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003582 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003583 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy16ea1392012-03-21 20:38:41 +00003584 (void) SetImageProperty(image,"png:text ",msg,
3585 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003586 }
3587
3588 if (num_raw_profiles != 0)
3589 {
cristy3b6fd2e2011-05-20 12:53:50 +00003590 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003591 "%d were found", num_raw_profiles);
cristy16ea1392012-03-21 20:38:41 +00003592 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3593 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003594 }
3595
glennrpcb395ac2011-03-30 19:50:23 +00003596 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003597 {
cristy3b6fd2e2011-05-20 12:53:50 +00003598 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003599 "chunk was found (see Chromaticity, above)");
cristy16ea1392012-03-21 20:38:41 +00003600 (void) SetImageProperty(image,"png:cHRM ",msg,
3601 exception);
glennrp59612252011-03-30 21:45:21 +00003602 }
glennrpcb395ac2011-03-30 19:50:23 +00003603
3604 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003605 {
cristy3b6fd2e2011-05-20 12:53:50 +00003606 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003607 "chunk was found (see Background color, above)");
cristy16ea1392012-03-21 20:38:41 +00003608 (void) SetImageProperty(image,"png:bKGD ",msg,
3609 exception);
glennrp59612252011-03-30 21:45:21 +00003610 }
3611
cristy3b6fd2e2011-05-20 12:53:50 +00003612 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003613 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003614
3615 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy16ea1392012-03-21 20:38:41 +00003616 (void) SetImageProperty(image,"png:iCCP ",msg,
3617 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003618
glennrpcb395ac2011-03-30 19:50:23 +00003619 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy16ea1392012-03-21 20:38:41 +00003620 (void) SetImageProperty(image,"png:tRNS ",msg,
3621 exception);
glennrp4eb39312011-03-30 21:34:55 +00003622
3623#if defined(PNG_sRGB_SUPPORTED)
3624 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3625 {
cristy3b6fd2e2011-05-20 12:53:50 +00003626 (void) FormatLocaleString(msg,MaxTextExtent,
cristy16ea1392012-03-21 20:38:41 +00003627 "intent=%d (See Rendering intent)", (int) intent);
3628 (void) SetImageProperty(image,"png:sRGB ",msg,
3629 exception);
glennrp4eb39312011-03-30 21:34:55 +00003630 }
3631#endif
3632
3633 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3634 {
cristy3b6fd2e2011-05-20 12:53:50 +00003635 (void) FormatLocaleString(msg,MaxTextExtent,
cristy16ea1392012-03-21 20:38:41 +00003636 "gamma=%.8g (See Gamma, above)",
3637 file_gamma);
3638 (void) SetImageProperty(image,"png:gAMA ",msg,
3639 exception);
glennrp4eb39312011-03-30 21:34:55 +00003640 }
3641
3642#if defined(PNG_pHYs_SUPPORTED)
3643 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3644 {
cristy3b6fd2e2011-05-20 12:53:50 +00003645 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003646 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003647 (double) x_resolution,(double) y_resolution, unit_type);
cristy16ea1392012-03-21 20:38:41 +00003648 (void) SetImageProperty(image,"png:pHYs ",msg,
3649 exception);
glennrp4eb39312011-03-30 21:34:55 +00003650 }
3651#endif
3652
3653#if defined(PNG_oFFs_SUPPORTED)
3654 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3655 {
cristy3b6fd2e2011-05-20 12:53:50 +00003656 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003657 (double) image->page.x,(double) image->page.y);
cristy16ea1392012-03-21 20:38:41 +00003658 (void) SetImageProperty(image,"png:oFFs ",msg,
3659 exception);
glennrp4eb39312011-03-30 21:34:55 +00003660 }
3661#endif
3662
glennrp07523c72011-03-31 18:12:10 +00003663 if ((image->page.width != 0 && image->page.width != image->columns) ||
3664 (image->page.height != 0 && image->page.height != image->rows))
3665 {
cristy3b6fd2e2011-05-20 12:53:50 +00003666 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003667 "width=%.20g, height=%.20g",
3668 (double) image->page.width,(double) image->page.height);
cristy16ea1392012-03-21 20:38:41 +00003669 (void) SetImageProperty(image,"png:vpAg ",msg,
3670 exception);
glennrp07523c72011-03-31 18:12:10 +00003671 }
glennrpcb395ac2011-03-30 19:50:23 +00003672 }
3673
cristy3ed852e2009-09-05 21:47:34 +00003674 /*
3675 Relinquish resources.
3676 */
3677 png_destroy_read_struct(&ping,&ping_info,&end_info);
3678
glennrpcf002022011-01-30 02:38:15 +00003679 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003680
3681 if (logging != MagickFalse)
3682 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3683 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003684
glennrpedaa0382012-04-12 14:16:21 +00003685#ifdef PNG_SETJMP_NOT_THREAD_SAFE
3686 UnlockSemaphoreInfo(ping_semaphore);
3687#endif
3688
3689 /* } for navigation to beginning of SETJMP-protected block, revert to
3690 * Throwing an Exception when an error occurs.
3691 */
3692
cristy3ed852e2009-09-05 21:47:34 +00003693 return(image);
3694
3695/* end of reading one PNG image */
3696}
3697
3698static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3699{
3700 Image
3701 *image,
3702 *previous;
3703
3704 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003705 have_mng_structure,
3706 logging,
cristy3ed852e2009-09-05 21:47:34 +00003707 status;
3708
3709 MngInfo
3710 *mng_info;
3711
3712 char
3713 magic_number[MaxTextExtent];
3714
cristy3ed852e2009-09-05 21:47:34 +00003715 ssize_t
3716 count;
3717
3718 /*
3719 Open image file.
3720 */
3721 assert(image_info != (const ImageInfo *) NULL);
3722 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003723
cristy3ed852e2009-09-05 21:47:34 +00003724 if (image_info->debug != MagickFalse)
3725 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3726 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003727
cristy3ed852e2009-09-05 21:47:34 +00003728 assert(exception != (ExceptionInfo *) NULL);
3729 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003730 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy16ea1392012-03-21 20:38:41 +00003731 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003732 mng_info=(MngInfo *) NULL;
3733 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003734
cristy3ed852e2009-09-05 21:47:34 +00003735 if (status == MagickFalse)
3736 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003737
cristy3ed852e2009-09-05 21:47:34 +00003738 /*
3739 Verify PNG signature.
3740 */
3741 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003742
glennrpdde35db2011-02-21 12:06:32 +00003743 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003744 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003745
cristy3ed852e2009-09-05 21:47:34 +00003746 /*
3747 Allocate a MngInfo structure.
3748 */
3749 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003750 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003751
cristy3ed852e2009-09-05 21:47:34 +00003752 if (mng_info == (MngInfo *) NULL)
3753 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003754
cristy3ed852e2009-09-05 21:47:34 +00003755 /*
3756 Initialize members of the MngInfo structure.
3757 */
3758 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3759 mng_info->image=image;
3760 have_mng_structure=MagickTrue;
3761
3762 previous=image;
3763 image=ReadOnePNGImage(mng_info,image_info,exception);
3764 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003765
cristy3ed852e2009-09-05 21:47:34 +00003766 if (image == (Image *) NULL)
3767 {
3768 if (previous != (Image *) NULL)
3769 {
3770 if (previous->signature != MagickSignature)
3771 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003772
cristy3ed852e2009-09-05 21:47:34 +00003773 (void) CloseBlob(previous);
3774 (void) DestroyImageList(previous);
3775 }
glennrp0fe50b42010-11-16 03:52:51 +00003776
cristy3ed852e2009-09-05 21:47:34 +00003777 if (logging != MagickFalse)
3778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3779 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003780
cristy3ed852e2009-09-05 21:47:34 +00003781 return((Image *) NULL);
3782 }
glennrp47b9dd52010-11-24 18:12:06 +00003783
cristy3ed852e2009-09-05 21:47:34 +00003784 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003785
cristy3ed852e2009-09-05 21:47:34 +00003786 if ((image->columns == 0) || (image->rows == 0))
3787 {
3788 if (logging != MagickFalse)
3789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3790 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003791
cristy3ed852e2009-09-05 21:47:34 +00003792 ThrowReaderException(CorruptImageError,"CorruptImage");
3793 }
glennrp47b9dd52010-11-24 18:12:06 +00003794
cristy3ed852e2009-09-05 21:47:34 +00003795 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3796 {
cristy16ea1392012-03-21 20:38:41 +00003797 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003798 image->matte=MagickFalse;
3799 }
glennrp0fe50b42010-11-16 03:52:51 +00003800
cristy3ed852e2009-09-05 21:47:34 +00003801 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy16ea1392012-03-21 20:38:41 +00003802 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003803
cristy3ed852e2009-09-05 21:47:34 +00003804 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3806 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3807 (double) image->page.width,(double) image->page.height,
3808 (double) image->page.x,(double) image->page.y);
3809
3810 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003812
cristy3ed852e2009-09-05 21:47:34 +00003813 return(image);
3814}
3815
3816
3817
3818#if defined(JNG_SUPPORTED)
3819/*
3820%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3821% %
3822% %
3823% %
3824% R e a d O n e J N G I m a g e %
3825% %
3826% %
3827% %
3828%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3829%
3830% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3831% (minus the 8-byte signature) and returns it. It allocates the memory
3832% necessary for the new Image structure and returns a pointer to the new
3833% image.
3834%
3835% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3836%
3837% The format of the ReadOneJNGImage method is:
3838%
3839% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3840% ExceptionInfo *exception)
3841%
3842% A description of each parameter follows:
3843%
3844% o mng_info: Specifies a pointer to a MngInfo structure.
3845%
3846% o image_info: the image info.
3847%
3848% o exception: return any errors or warnings in this structure.
3849%
3850*/
3851static Image *ReadOneJNGImage(MngInfo *mng_info,
3852 const ImageInfo *image_info, ExceptionInfo *exception)
3853{
3854 Image
3855 *alpha_image,
3856 *color_image,
3857 *image,
3858 *jng_image;
3859
3860 ImageInfo
3861 *alpha_image_info,
3862 *color_image_info;
3863
cristy4383ec82011-01-05 15:42:32 +00003864 MagickBooleanType
3865 logging;
3866
cristybb503372010-05-27 20:51:26 +00003867 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003868 y;
3869
3870 MagickBooleanType
3871 status;
3872
3873 png_uint_32
3874 jng_height,
3875 jng_width;
3876
3877 png_byte
3878 jng_color_type,
3879 jng_image_sample_depth,
3880 jng_image_compression_method,
3881 jng_image_interlace_method,
3882 jng_alpha_sample_depth,
3883 jng_alpha_compression_method,
3884 jng_alpha_filter_method,
3885 jng_alpha_interlace_method;
3886
cristy16ea1392012-03-21 20:38:41 +00003887 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003888 *s;
3889
cristybb503372010-05-27 20:51:26 +00003890 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003891 i,
3892 x;
3893
cristy16ea1392012-03-21 20:38:41 +00003894 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003895 *q;
3896
3897 register unsigned char
3898 *p;
3899
3900 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003901 read_JSEP,
3902 reading_idat,
3903 skip_to_iend;
3904
cristybb503372010-05-27 20:51:26 +00003905 size_t
cristy3ed852e2009-09-05 21:47:34 +00003906 length;
3907
3908 jng_alpha_compression_method=0;
3909 jng_alpha_sample_depth=8;
3910 jng_color_type=0;
3911 jng_height=0;
3912 jng_width=0;
3913 alpha_image=(Image *) NULL;
3914 color_image=(Image *) NULL;
3915 alpha_image_info=(ImageInfo *) NULL;
3916 color_image_info=(ImageInfo *) NULL;
3917
3918 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003919 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003920
3921 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003922
cristy16ea1392012-03-21 20:38:41 +00003923 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003924 {
3925 /*
3926 Allocate next image structure.
3927 */
3928 if (logging != MagickFalse)
3929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3930 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003931
cristy16ea1392012-03-21 20:38:41 +00003932 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003933
cristy3ed852e2009-09-05 21:47:34 +00003934 if (GetNextImageInList(image) == (Image *) NULL)
3935 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003936
cristy3ed852e2009-09-05 21:47:34 +00003937 image=SyncNextImageInList(image);
3938 }
3939 mng_info->image=image;
3940
3941 /*
3942 Signature bytes have already been read.
3943 */
3944
3945 read_JSEP=MagickFalse;
3946 reading_idat=MagickFalse;
3947 skip_to_iend=MagickFalse;
3948 for (;;)
3949 {
3950 char
3951 type[MaxTextExtent];
3952
3953 unsigned char
3954 *chunk;
3955
3956 unsigned int
3957 count;
3958
3959 /*
3960 Read a new JNG chunk.
3961 */
3962 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3963 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003964
cristy3ed852e2009-09-05 21:47:34 +00003965 if (status == MagickFalse)
3966 break;
glennrp0fe50b42010-11-16 03:52:51 +00003967
cristy3ed852e2009-09-05 21:47:34 +00003968 type[0]='\0';
3969 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3970 length=ReadBlobMSBLong(image);
3971 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3972
3973 if (logging != MagickFalse)
3974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003975 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3976 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003977
3978 if (length > PNG_UINT_31_MAX || count == 0)
3979 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003980
cristy3ed852e2009-09-05 21:47:34 +00003981 p=NULL;
3982 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003983
cristy3ed852e2009-09-05 21:47:34 +00003984 if (length)
3985 {
3986 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003987
cristy3ed852e2009-09-05 21:47:34 +00003988 if (chunk == (unsigned char *) NULL)
3989 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003990
cristybb503372010-05-27 20:51:26 +00003991 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003992 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003993
cristy3ed852e2009-09-05 21:47:34 +00003994 p=chunk;
3995 }
glennrp47b9dd52010-11-24 18:12:06 +00003996
cristy3ed852e2009-09-05 21:47:34 +00003997 (void) ReadBlobMSBLong(image); /* read crc word */
3998
3999 if (skip_to_iend)
4000 {
4001 if (length)
4002 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004003
cristy3ed852e2009-09-05 21:47:34 +00004004 continue;
4005 }
4006
4007 if (memcmp(type,mng_JHDR,4) == 0)
4008 {
4009 if (length == 16)
4010 {
cristybb503372010-05-27 20:51:26 +00004011 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004012 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00004013 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004014 (p[6] << 8) | p[7]);
4015 jng_color_type=p[8];
4016 jng_image_sample_depth=p[9];
4017 jng_image_compression_method=p[10];
4018 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00004019
cristy3ed852e2009-09-05 21:47:34 +00004020 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4021 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00004022
cristy3ed852e2009-09-05 21:47:34 +00004023 jng_alpha_sample_depth=p[12];
4024 jng_alpha_compression_method=p[13];
4025 jng_alpha_filter_method=p[14];
4026 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00004027
cristy3ed852e2009-09-05 21:47:34 +00004028 if (logging != MagickFalse)
4029 {
4030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004031 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00004032
cristy3ed852e2009-09-05 21:47:34 +00004033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004034 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00004035
cristy3ed852e2009-09-05 21:47:34 +00004036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4037 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00004038
cristy3ed852e2009-09-05 21:47:34 +00004039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4040 " jng_image_sample_depth: %3d",
4041 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004042
cristy3ed852e2009-09-05 21:47:34 +00004043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4044 " jng_image_compression_method:%3d",
4045 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004046
cristy3ed852e2009-09-05 21:47:34 +00004047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4048 " jng_image_interlace_method: %3d",
4049 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00004050
cristy3ed852e2009-09-05 21:47:34 +00004051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4052 " jng_alpha_sample_depth: %3d",
4053 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004054
cristy3ed852e2009-09-05 21:47:34 +00004055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4056 " jng_alpha_compression_method:%3d",
4057 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004058
cristy3ed852e2009-09-05 21:47:34 +00004059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4060 " jng_alpha_filter_method: %3d",
4061 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004062
cristy3ed852e2009-09-05 21:47:34 +00004063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4064 " jng_alpha_interlace_method: %3d",
4065 jng_alpha_interlace_method);
4066 }
4067 }
glennrp47b9dd52010-11-24 18:12:06 +00004068
cristy3ed852e2009-09-05 21:47:34 +00004069 if (length)
4070 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004071
cristy3ed852e2009-09-05 21:47:34 +00004072 continue;
4073 }
4074
4075
4076 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4077 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4078 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4079 {
4080 /*
4081 o create color_image
4082 o open color_blob, attached to color_image
4083 o if (color type has alpha)
4084 open alpha_blob, attached to alpha_image
4085 */
4086
cristy73bd4a52010-10-05 11:24:23 +00004087 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004088
cristy3ed852e2009-09-05 21:47:34 +00004089 if (color_image_info == (ImageInfo *) NULL)
4090 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004091
cristy3ed852e2009-09-05 21:47:34 +00004092 GetImageInfo(color_image_info);
cristy16ea1392012-03-21 20:38:41 +00004093 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004094
cristy3ed852e2009-09-05 21:47:34 +00004095 if (color_image == (Image *) NULL)
4096 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4097
4098 if (logging != MagickFalse)
4099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4100 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004101
cristy3ed852e2009-09-05 21:47:34 +00004102 (void) AcquireUniqueFilename(color_image->filename);
4103 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4104 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004105
cristy3ed852e2009-09-05 21:47:34 +00004106 if (status == MagickFalse)
4107 return((Image *) NULL);
4108
4109 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4110 {
4111 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004112 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004113
cristy3ed852e2009-09-05 21:47:34 +00004114 if (alpha_image_info == (ImageInfo *) NULL)
4115 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004116
cristy3ed852e2009-09-05 21:47:34 +00004117 GetImageInfo(alpha_image_info);
cristy16ea1392012-03-21 20:38:41 +00004118 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004119
cristy3ed852e2009-09-05 21:47:34 +00004120 if (alpha_image == (Image *) NULL)
4121 {
4122 alpha_image=DestroyImage(alpha_image);
4123 ThrowReaderException(ResourceLimitError,
4124 "MemoryAllocationFailed");
4125 }
glennrp0fe50b42010-11-16 03:52:51 +00004126
cristy3ed852e2009-09-05 21:47:34 +00004127 if (logging != MagickFalse)
4128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4129 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004130
cristy3ed852e2009-09-05 21:47:34 +00004131 (void) AcquireUniqueFilename(alpha_image->filename);
4132 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4133 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004134
cristy3ed852e2009-09-05 21:47:34 +00004135 if (status == MagickFalse)
4136 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004137
cristy3ed852e2009-09-05 21:47:34 +00004138 if (jng_alpha_compression_method == 0)
4139 {
4140 unsigned char
4141 data[18];
4142
4143 if (logging != MagickFalse)
4144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4145 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004146
cristy3ed852e2009-09-05 21:47:34 +00004147 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4148 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004149
cristy3ed852e2009-09-05 21:47:34 +00004150 (void) WriteBlobMSBULong(alpha_image,13L);
4151 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004152 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004153 PNGLong(data+4,jng_width);
4154 PNGLong(data+8,jng_height);
4155 data[12]=jng_alpha_sample_depth;
4156 data[13]=0; /* color_type gray */
4157 data[14]=0; /* compression method 0 */
4158 data[15]=0; /* filter_method 0 */
4159 data[16]=0; /* interlace_method 0 */
4160 (void) WriteBlob(alpha_image,17,data);
4161 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4162 }
4163 }
4164 reading_idat=MagickTrue;
4165 }
4166
4167 if (memcmp(type,mng_JDAT,4) == 0)
4168 {
glennrp47b9dd52010-11-24 18:12:06 +00004169 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004170
4171 if (logging != MagickFalse)
4172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4173 " Copying JDAT chunk data to color_blob.");
4174
4175 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004176
cristy3ed852e2009-09-05 21:47:34 +00004177 if (length)
4178 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004179
cristy3ed852e2009-09-05 21:47:34 +00004180 continue;
4181 }
4182
4183 if (memcmp(type,mng_IDAT,4) == 0)
4184 {
4185 png_byte
4186 data[5];
4187
glennrp47b9dd52010-11-24 18:12:06 +00004188 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004189
4190 if (image_info->ping == MagickFalse)
4191 {
4192 if (logging != MagickFalse)
4193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4194 " Copying IDAT chunk data to alpha_blob.");
4195
cristybb503372010-05-27 20:51:26 +00004196 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004197 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004198 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004199 (void) WriteBlob(alpha_image,4,data);
4200 (void) WriteBlob(alpha_image,length,chunk);
4201 (void) WriteBlobMSBULong(alpha_image,
4202 crc32(crc32(0,data,4),chunk,(uInt) length));
4203 }
glennrp0fe50b42010-11-16 03:52:51 +00004204
cristy3ed852e2009-09-05 21:47:34 +00004205 if (length)
4206 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004207
cristy3ed852e2009-09-05 21:47:34 +00004208 continue;
4209 }
4210
4211 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4212 {
glennrp47b9dd52010-11-24 18:12:06 +00004213 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004214
4215 if (image_info->ping == MagickFalse)
4216 {
4217 if (logging != MagickFalse)
4218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4219 " Copying JDAA chunk data to alpha_blob.");
4220
4221 (void) WriteBlob(alpha_image,length,chunk);
4222 }
glennrp0fe50b42010-11-16 03:52:51 +00004223
cristy3ed852e2009-09-05 21:47:34 +00004224 if (length)
4225 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004226
cristy3ed852e2009-09-05 21:47:34 +00004227 continue;
4228 }
4229
4230 if (memcmp(type,mng_JSEP,4) == 0)
4231 {
4232 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004233
cristy3ed852e2009-09-05 21:47:34 +00004234 if (length)
4235 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004236
cristy3ed852e2009-09-05 21:47:34 +00004237 continue;
4238 }
4239
4240 if (memcmp(type,mng_bKGD,4) == 0)
4241 {
4242 if (length == 2)
4243 {
4244 image->background_color.red=ScaleCharToQuantum(p[1]);
4245 image->background_color.green=image->background_color.red;
4246 image->background_color.blue=image->background_color.red;
4247 }
glennrp0fe50b42010-11-16 03:52:51 +00004248
cristy3ed852e2009-09-05 21:47:34 +00004249 if (length == 6)
4250 {
4251 image->background_color.red=ScaleCharToQuantum(p[1]);
4252 image->background_color.green=ScaleCharToQuantum(p[3]);
4253 image->background_color.blue=ScaleCharToQuantum(p[5]);
4254 }
glennrp0fe50b42010-11-16 03:52:51 +00004255
cristy3ed852e2009-09-05 21:47:34 +00004256 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4257 continue;
4258 }
4259
4260 if (memcmp(type,mng_gAMA,4) == 0)
4261 {
4262 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004263 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004264
cristy3ed852e2009-09-05 21:47:34 +00004265 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4266 continue;
4267 }
4268
4269 if (memcmp(type,mng_cHRM,4) == 0)
4270 {
4271 if (length == 32)
4272 {
cristy8182b072010-05-30 20:10:53 +00004273 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4274 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4275 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4276 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4277 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4278 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4279 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4280 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004281 }
glennrp47b9dd52010-11-24 18:12:06 +00004282
cristy3ed852e2009-09-05 21:47:34 +00004283 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4284 continue;
4285 }
4286
4287 if (memcmp(type,mng_sRGB,4) == 0)
4288 {
4289 if (length == 1)
4290 {
glennrpe610a072010-08-05 17:08:46 +00004291 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004292 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristyda7803d2012-05-03 01:22:45 +00004293 image->gamma=1.000f/2.200f;
cristy3ed852e2009-09-05 21:47:34 +00004294 image->chromaticity.red_primary.x=0.6400f;
4295 image->chromaticity.red_primary.y=0.3300f;
4296 image->chromaticity.green_primary.x=0.3000f;
4297 image->chromaticity.green_primary.y=0.6000f;
4298 image->chromaticity.blue_primary.x=0.1500f;
4299 image->chromaticity.blue_primary.y=0.0600f;
4300 image->chromaticity.white_point.x=0.3127f;
4301 image->chromaticity.white_point.y=0.3290f;
4302 }
glennrp47b9dd52010-11-24 18:12:06 +00004303
cristy3ed852e2009-09-05 21:47:34 +00004304 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4305 continue;
4306 }
4307
4308 if (memcmp(type,mng_oFFs,4) == 0)
4309 {
4310 if (length > 8)
4311 {
glennrp5eae7602011-02-22 15:21:32 +00004312 image->page.x=(ssize_t) mng_get_long(p);
4313 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004314
cristy3ed852e2009-09-05 21:47:34 +00004315 if ((int) p[8] != 0)
4316 {
4317 image->page.x/=10000;
4318 image->page.y/=10000;
4319 }
4320 }
glennrp47b9dd52010-11-24 18:12:06 +00004321
cristy3ed852e2009-09-05 21:47:34 +00004322 if (length)
4323 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004324
cristy3ed852e2009-09-05 21:47:34 +00004325 continue;
4326 }
4327
4328 if (memcmp(type,mng_pHYs,4) == 0)
4329 {
4330 if (length > 8)
4331 {
cristy16ea1392012-03-21 20:38:41 +00004332 image->resolution.x=(double) mng_get_long(p);
4333 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004334 if ((int) p[8] == PNG_RESOLUTION_METER)
4335 {
4336 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00004337 image->resolution.x=image->resolution.x/100.0f;
4338 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004339 }
4340 }
glennrp0fe50b42010-11-16 03:52:51 +00004341
cristy3ed852e2009-09-05 21:47:34 +00004342 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4343 continue;
4344 }
4345
4346#if 0
4347 if (memcmp(type,mng_iCCP,4) == 0)
4348 {
glennrpfd05d622011-02-25 04:10:33 +00004349 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004350 if (length)
4351 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004352
cristy3ed852e2009-09-05 21:47:34 +00004353 continue;
4354 }
4355#endif
4356
4357 if (length)
4358 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4359
4360 if (memcmp(type,mng_IEND,4))
4361 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004362
cristy3ed852e2009-09-05 21:47:34 +00004363 break;
4364 }
4365
4366
4367 /* IEND found */
4368
4369 /*
4370 Finish up reading image data:
4371
4372 o read main image from color_blob.
4373
4374 o close color_blob.
4375
4376 o if (color_type has alpha)
4377 if alpha_encoding is PNG
4378 read secondary image from alpha_blob via ReadPNG
4379 if alpha_encoding is JPEG
4380 read secondary image from alpha_blob via ReadJPEG
4381
4382 o close alpha_blob.
4383
4384 o copy intensity of secondary image into
cristy16ea1392012-03-21 20:38:41 +00004385 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004386
4387 o destroy the secondary image.
4388 */
4389
4390 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004391
cristy3ed852e2009-09-05 21:47:34 +00004392 if (logging != MagickFalse)
4393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4394 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004395
cristy3b6fd2e2011-05-20 12:53:50 +00004396 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004397 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004398
cristy3ed852e2009-09-05 21:47:34 +00004399 color_image_info->ping=MagickFalse; /* To do: avoid this */
4400 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004401
cristy3ed852e2009-09-05 21:47:34 +00004402 if (jng_image == (Image *) NULL)
4403 return((Image *) NULL);
4404
4405 (void) RelinquishUniqueFileResource(color_image->filename);
4406 color_image=DestroyImage(color_image);
4407 color_image_info=DestroyImageInfo(color_image_info);
4408
4409 if (jng_image == (Image *) NULL)
4410 return((Image *) NULL);
4411
4412 if (logging != MagickFalse)
4413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4414 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004415
cristy3ed852e2009-09-05 21:47:34 +00004416 image->rows=jng_height;
4417 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004418
cristybb503372010-05-27 20:51:26 +00004419 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004420 {
cristy16ea1392012-03-21 20:38:41 +00004421 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004422 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00004423 for (x=(ssize_t) image->columns; x != 0; x--)
4424 {
4425 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4426 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4427 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4428 q+=GetPixelChannels(image);
4429 s+=GetPixelChannels(jng_image);
4430 }
glennrp47b9dd52010-11-24 18:12:06 +00004431
cristy3ed852e2009-09-05 21:47:34 +00004432 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4433 break;
4434 }
glennrp0fe50b42010-11-16 03:52:51 +00004435
cristy3ed852e2009-09-05 21:47:34 +00004436 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004437
cristy3ed852e2009-09-05 21:47:34 +00004438 if (image_info->ping == MagickFalse)
4439 {
4440 if (jng_color_type >= 12)
4441 {
4442 if (jng_alpha_compression_method == 0)
4443 {
4444 png_byte
4445 data[5];
4446 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4447 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004448 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004449 (void) WriteBlob(alpha_image,4,data);
4450 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4451 }
glennrp0fe50b42010-11-16 03:52:51 +00004452
cristy3ed852e2009-09-05 21:47:34 +00004453 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004454
cristy3ed852e2009-09-05 21:47:34 +00004455 if (logging != MagickFalse)
4456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00004457 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004458
cristy3b6fd2e2011-05-20 12:53:50 +00004459 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004460 "%s",alpha_image->filename);
4461
4462 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004463
cristy3ed852e2009-09-05 21:47:34 +00004464 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004465 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004466 {
4467 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy16ea1392012-03-21 20:38:41 +00004468 exception);
cristy3ed852e2009-09-05 21:47:34 +00004469 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004470
cristy3ed852e2009-09-05 21:47:34 +00004471 if (image->matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00004472 for (x=(ssize_t) image->columns; x != 0; x--)
4473 {
4474 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4475 q+=GetPixelChannels(image);
4476 s+=GetPixelChannels(jng_image);
4477 }
glennrp0fe50b42010-11-16 03:52:51 +00004478
cristy3ed852e2009-09-05 21:47:34 +00004479 else
cristy16ea1392012-03-21 20:38:41 +00004480 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004481 {
cristy16ea1392012-03-21 20:38:41 +00004482 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4483 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004484 image->matte=MagickTrue;
cristy16ea1392012-03-21 20:38:41 +00004485 q+=GetPixelChannels(image);
4486 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004487 }
glennrp0fe50b42010-11-16 03:52:51 +00004488
cristy3ed852e2009-09-05 21:47:34 +00004489 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4490 break;
4491 }
4492 (void) RelinquishUniqueFileResource(alpha_image->filename);
4493 alpha_image=DestroyImage(alpha_image);
4494 alpha_image_info=DestroyImageInfo(alpha_image_info);
4495 if (jng_image != (Image *) NULL)
4496 jng_image=DestroyImage(jng_image);
4497 }
4498 }
4499
glennrp47b9dd52010-11-24 18:12:06 +00004500 /* Read the JNG image. */
4501
cristy3ed852e2009-09-05 21:47:34 +00004502 if (mng_info->mng_type == 0)
4503 {
4504 mng_info->mng_width=jng_width;
4505 mng_info->mng_height=jng_height;
4506 }
glennrp0fe50b42010-11-16 03:52:51 +00004507
cristy3ed852e2009-09-05 21:47:34 +00004508 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004509 {
4510 image->page.width=jng_width;
4511 image->page.height=jng_height;
4512 }
4513
cristy3ed852e2009-09-05 21:47:34 +00004514 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004515 {
4516 image->page.x=mng_info->x_off[mng_info->object_id];
4517 image->page.y=mng_info->y_off[mng_info->object_id];
4518 }
4519
cristy3ed852e2009-09-05 21:47:34 +00004520 else
glennrp0fe50b42010-11-16 03:52:51 +00004521 {
4522 image->page.y=mng_info->y_off[mng_info->object_id];
4523 }
4524
cristy3ed852e2009-09-05 21:47:34 +00004525 mng_info->image_found++;
4526 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4527 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004528
cristy3ed852e2009-09-05 21:47:34 +00004529 if (logging != MagickFalse)
4530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4531 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004532
cristy3ed852e2009-09-05 21:47:34 +00004533 return(image);
4534}
4535
4536/*
4537%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4538% %
4539% %
4540% %
4541% R e a d J N G I m a g e %
4542% %
4543% %
4544% %
4545%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4546%
4547% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4548% (including the 8-byte signature) and returns it. It allocates the memory
4549% necessary for the new Image structure and returns a pointer to the new
4550% image.
4551%
4552% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4553%
4554% The format of the ReadJNGImage method is:
4555%
4556% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4557% *exception)
4558%
4559% A description of each parameter follows:
4560%
4561% o image_info: the image info.
4562%
4563% o exception: return any errors or warnings in this structure.
4564%
4565*/
4566
4567static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4568{
4569 Image
4570 *image,
4571 *previous;
4572
4573 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004574 have_mng_structure,
4575 logging,
cristy3ed852e2009-09-05 21:47:34 +00004576 status;
4577
4578 MngInfo
4579 *mng_info;
4580
4581 char
4582 magic_number[MaxTextExtent];
4583
cristy3ed852e2009-09-05 21:47:34 +00004584 size_t
4585 count;
4586
4587 /*
4588 Open image file.
4589 */
4590 assert(image_info != (const ImageInfo *) NULL);
4591 assert(image_info->signature == MagickSignature);
4592 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4593 assert(exception != (ExceptionInfo *) NULL);
4594 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004595 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004596 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004597 mng_info=(MngInfo *) NULL;
4598 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004599
cristy3ed852e2009-09-05 21:47:34 +00004600 if (status == MagickFalse)
4601 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004602
cristy3ed852e2009-09-05 21:47:34 +00004603 if (LocaleCompare(image_info->magick,"JNG") != 0)
4604 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004605
glennrp47b9dd52010-11-24 18:12:06 +00004606 /* Verify JNG signature. */
4607
cristy3ed852e2009-09-05 21:47:34 +00004608 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004609
glennrp3b8763e2011-02-21 12:08:18 +00004610 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004611 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004612
glennrp47b9dd52010-11-24 18:12:06 +00004613 /* Allocate a MngInfo structure. */
4614
cristy3ed852e2009-09-05 21:47:34 +00004615 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004616 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004617
cristy3ed852e2009-09-05 21:47:34 +00004618 if (mng_info == (MngInfo *) NULL)
4619 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004620
glennrp47b9dd52010-11-24 18:12:06 +00004621 /* Initialize members of the MngInfo structure. */
4622
cristy3ed852e2009-09-05 21:47:34 +00004623 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4624 have_mng_structure=MagickTrue;
4625
4626 mng_info->image=image;
4627 previous=image;
4628 image=ReadOneJNGImage(mng_info,image_info,exception);
4629 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004630
cristy3ed852e2009-09-05 21:47:34 +00004631 if (image == (Image *) NULL)
4632 {
4633 if (IsImageObject(previous) != MagickFalse)
4634 {
4635 (void) CloseBlob(previous);
4636 (void) DestroyImageList(previous);
4637 }
glennrp0fe50b42010-11-16 03:52:51 +00004638
cristy3ed852e2009-09-05 21:47:34 +00004639 if (logging != MagickFalse)
4640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4641 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004642
cristy3ed852e2009-09-05 21:47:34 +00004643 return((Image *) NULL);
4644 }
4645 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004646
cristy3ed852e2009-09-05 21:47:34 +00004647 if (image->columns == 0 || image->rows == 0)
4648 {
4649 if (logging != MagickFalse)
4650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4651 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004652
cristy3ed852e2009-09-05 21:47:34 +00004653 ThrowReaderException(CorruptImageError,"CorruptImage");
4654 }
glennrp0fe50b42010-11-16 03:52:51 +00004655
cristy3ed852e2009-09-05 21:47:34 +00004656 if (logging != MagickFalse)
4657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004658
cristy3ed852e2009-09-05 21:47:34 +00004659 return(image);
4660}
4661#endif
4662
4663static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4664{
4665 char
4666 page_geometry[MaxTextExtent];
4667
4668 Image
4669 *image,
4670 *previous;
4671
cristy4383ec82011-01-05 15:42:32 +00004672 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004673 logging,
4674 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004675
cristy3ed852e2009-09-05 21:47:34 +00004676 volatile int
4677 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004678 object_id,
4679 term_chunk_found,
4680 skip_to_iend;
4681
cristybb503372010-05-27 20:51:26 +00004682 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004683 image_count=0;
4684
4685 MagickBooleanType
4686 status;
4687
4688 MagickOffsetType
4689 offset;
4690
4691 MngInfo
4692 *mng_info;
4693
4694 MngBox
4695 default_fb,
4696 fb,
4697 previous_fb;
4698
4699#if defined(MNG_INSERT_LAYERS)
cristy16ea1392012-03-21 20:38:41 +00004700 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004701 mng_background_color;
4702#endif
4703
4704 register unsigned char
4705 *p;
4706
cristybb503372010-05-27 20:51:26 +00004707 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004708 i;
4709
4710 size_t
4711 count;
4712
cristybb503372010-05-27 20:51:26 +00004713 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004714 loop_level;
4715
4716 volatile short
4717 skipping_loop;
4718
4719#if defined(MNG_INSERT_LAYERS)
4720 unsigned int
4721 mandatory_back=0;
4722#endif
4723
4724 volatile unsigned int
4725#ifdef MNG_OBJECT_BUFFERS
4726 mng_background_object=0,
4727#endif
4728 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4729
cristybb503372010-05-27 20:51:26 +00004730 size_t
cristy3ed852e2009-09-05 21:47:34 +00004731 default_frame_timeout,
4732 frame_timeout,
4733#if defined(MNG_INSERT_LAYERS)
4734 image_height,
4735 image_width,
4736#endif
4737 length;
4738
glennrp38ea0832010-06-02 18:50:28 +00004739 /* These delays are all measured in image ticks_per_second,
4740 * not in MNG ticks_per_second
4741 */
cristybb503372010-05-27 20:51:26 +00004742 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004743 default_frame_delay,
4744 final_delay,
4745 final_image_delay,
4746 frame_delay,
4747#if defined(MNG_INSERT_LAYERS)
4748 insert_layers,
4749#endif
4750 mng_iterations=1,
4751 simplicity=0,
4752 subframe_height=0,
4753 subframe_width=0;
4754
4755 previous_fb.top=0;
4756 previous_fb.bottom=0;
4757 previous_fb.left=0;
4758 previous_fb.right=0;
4759 default_fb.top=0;
4760 default_fb.bottom=0;
4761 default_fb.left=0;
4762 default_fb.right=0;
4763
glennrp47b9dd52010-11-24 18:12:06 +00004764 /* Open image file. */
4765
cristy3ed852e2009-09-05 21:47:34 +00004766 assert(image_info != (const ImageInfo *) NULL);
4767 assert(image_info->signature == MagickSignature);
4768 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4769 assert(exception != (ExceptionInfo *) NULL);
4770 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004771 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004772 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004773 mng_info=(MngInfo *) NULL;
4774 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004775
cristy3ed852e2009-09-05 21:47:34 +00004776 if (status == MagickFalse)
4777 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004778
cristy3ed852e2009-09-05 21:47:34 +00004779 first_mng_object=MagickFalse;
4780 skipping_loop=(-1);
4781 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004782
4783 /* Allocate a MngInfo structure. */
4784
cristy73bd4a52010-10-05 11:24:23 +00004785 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004786
cristy3ed852e2009-09-05 21:47:34 +00004787 if (mng_info == (MngInfo *) NULL)
4788 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004789
glennrp47b9dd52010-11-24 18:12:06 +00004790 /* Initialize members of the MngInfo structure. */
4791
cristy3ed852e2009-09-05 21:47:34 +00004792 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4793 mng_info->image=image;
4794 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004795
4796 if (LocaleCompare(image_info->magick,"MNG") == 0)
4797 {
4798 char
4799 magic_number[MaxTextExtent];
4800
glennrp47b9dd52010-11-24 18:12:06 +00004801 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004802 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4803 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4804 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004805
4806 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004807 for (i=0; i < MNG_MAX_OBJECTS; i++)
4808 {
cristybb503372010-05-27 20:51:26 +00004809 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4810 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004811 }
4812 mng_info->exists[0]=MagickTrue;
4813 }
glennrp47b9dd52010-11-24 18:12:06 +00004814
cristy3ed852e2009-09-05 21:47:34 +00004815 first_mng_object=MagickTrue;
4816 mng_type=0;
4817#if defined(MNG_INSERT_LAYERS)
4818 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4819#endif
4820 default_frame_delay=0;
4821 default_frame_timeout=0;
4822 frame_delay=0;
4823 final_delay=1;
4824 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4825 object_id=0;
4826 skip_to_iend=MagickFalse;
4827 term_chunk_found=MagickFalse;
4828 mng_info->framing_mode=1;
4829#if defined(MNG_INSERT_LAYERS)
4830 mandatory_back=MagickFalse;
4831#endif
4832#if defined(MNG_INSERT_LAYERS)
4833 mng_background_color=image->background_color;
4834#endif
4835 default_fb=mng_info->frame;
4836 previous_fb=mng_info->frame;
4837 do
4838 {
4839 char
4840 type[MaxTextExtent];
4841
4842 if (LocaleCompare(image_info->magick,"MNG") == 0)
4843 {
4844 unsigned char
4845 *chunk;
4846
4847 /*
4848 Read a new chunk.
4849 */
4850 type[0]='\0';
4851 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4852 length=ReadBlobMSBLong(image);
4853 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4854
4855 if (logging != MagickFalse)
4856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004857 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4858 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004859
4860 if (length > PNG_UINT_31_MAX)
4861 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004862
cristy3ed852e2009-09-05 21:47:34 +00004863 if (count == 0)
4864 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004865
cristy3ed852e2009-09-05 21:47:34 +00004866 p=NULL;
4867 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004868
cristy3ed852e2009-09-05 21:47:34 +00004869 if (length)
4870 {
4871 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004872
cristy3ed852e2009-09-05 21:47:34 +00004873 if (chunk == (unsigned char *) NULL)
4874 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004875
cristybb503372010-05-27 20:51:26 +00004876 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004877 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004878
cristy3ed852e2009-09-05 21:47:34 +00004879 p=chunk;
4880 }
glennrp0fe50b42010-11-16 03:52:51 +00004881
cristy3ed852e2009-09-05 21:47:34 +00004882 (void) ReadBlobMSBLong(image); /* read crc word */
4883
4884#if !defined(JNG_SUPPORTED)
4885 if (memcmp(type,mng_JHDR,4) == 0)
4886 {
4887 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004888
cristy3ed852e2009-09-05 21:47:34 +00004889 if (mng_info->jhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00004890 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004891 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004892
cristy3ed852e2009-09-05 21:47:34 +00004893 mng_info->jhdr_warning++;
4894 }
4895#endif
4896 if (memcmp(type,mng_DHDR,4) == 0)
4897 {
4898 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004899
cristy3ed852e2009-09-05 21:47:34 +00004900 if (mng_info->dhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00004901 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004902 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004903
cristy3ed852e2009-09-05 21:47:34 +00004904 mng_info->dhdr_warning++;
4905 }
4906 if (memcmp(type,mng_MEND,4) == 0)
4907 break;
glennrp47b9dd52010-11-24 18:12:06 +00004908
cristy3ed852e2009-09-05 21:47:34 +00004909 if (skip_to_iend)
4910 {
4911 if (memcmp(type,mng_IEND,4) == 0)
4912 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004913
cristy3ed852e2009-09-05 21:47:34 +00004914 if (length)
4915 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004916
cristy3ed852e2009-09-05 21:47:34 +00004917 if (logging != MagickFalse)
4918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4919 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004920
cristy3ed852e2009-09-05 21:47:34 +00004921 continue;
4922 }
glennrp0fe50b42010-11-16 03:52:51 +00004923
cristy3ed852e2009-09-05 21:47:34 +00004924 if (memcmp(type,mng_MHDR,4) == 0)
4925 {
cristybb503372010-05-27 20:51:26 +00004926 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004927 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004928
cristybb503372010-05-27 20:51:26 +00004929 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004930 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004931
cristy3ed852e2009-09-05 21:47:34 +00004932 if (logging != MagickFalse)
4933 {
4934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004935 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004937 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004938 }
glennrp0fe50b42010-11-16 03:52:51 +00004939
cristy3ed852e2009-09-05 21:47:34 +00004940 p+=8;
cristy8182b072010-05-30 20:10:53 +00004941 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004942
cristy3ed852e2009-09-05 21:47:34 +00004943 if (mng_info->ticks_per_second == 0)
4944 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004945
cristy3ed852e2009-09-05 21:47:34 +00004946 else
4947 default_frame_delay=1UL*image->ticks_per_second/
4948 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004949
cristy3ed852e2009-09-05 21:47:34 +00004950 frame_delay=default_frame_delay;
4951 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004952
cristy3ed852e2009-09-05 21:47:34 +00004953 if (length > 16)
4954 {
4955 p+=16;
cristy8182b072010-05-30 20:10:53 +00004956 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004957 }
glennrp0fe50b42010-11-16 03:52:51 +00004958
cristy3ed852e2009-09-05 21:47:34 +00004959 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004960
cristy3ed852e2009-09-05 21:47:34 +00004961 if ((simplicity != 0) && ((simplicity | 11) == 11))
4962 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004963
cristy3ed852e2009-09-05 21:47:34 +00004964 if ((simplicity != 0) && ((simplicity | 9) == 9))
4965 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004966
cristy3ed852e2009-09-05 21:47:34 +00004967#if defined(MNG_INSERT_LAYERS)
4968 if (mng_type != 3)
4969 insert_layers=MagickTrue;
4970#endif
cristy16ea1392012-03-21 20:38:41 +00004971 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004972 {
glennrp47b9dd52010-11-24 18:12:06 +00004973 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00004974 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004975
cristy3ed852e2009-09-05 21:47:34 +00004976 if (GetNextImageInList(image) == (Image *) NULL)
4977 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004978
cristy3ed852e2009-09-05 21:47:34 +00004979 image=SyncNextImageInList(image);
4980 mng_info->image=image;
4981 }
4982
4983 if ((mng_info->mng_width > 65535L) ||
4984 (mng_info->mng_height > 65535L))
4985 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004986
cristy3b6fd2e2011-05-20 12:53:50 +00004987 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004988 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004989 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004990
cristy3ed852e2009-09-05 21:47:34 +00004991 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004992 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004993 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004994 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004995 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004996
cristy3ed852e2009-09-05 21:47:34 +00004997 for (i=0; i < MNG_MAX_OBJECTS; i++)
4998 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004999
cristy3ed852e2009-09-05 21:47:34 +00005000 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5001 continue;
5002 }
5003
5004 if (memcmp(type,mng_TERM,4) == 0)
5005 {
5006 int
5007 repeat=0;
5008
5009
5010 if (length)
5011 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005012
cristy3ed852e2009-09-05 21:47:34 +00005013 if (repeat == 3)
5014 {
cristy8182b072010-05-30 20:10:53 +00005015 final_delay=(png_uint_32) mng_get_long(&p[2]);
5016 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00005017
cristy3ed852e2009-09-05 21:47:34 +00005018 if (mng_iterations == PNG_UINT_31_MAX)
5019 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00005020
cristy3ed852e2009-09-05 21:47:34 +00005021 image->iterations=mng_iterations;
5022 term_chunk_found=MagickTrue;
5023 }
glennrp0fe50b42010-11-16 03:52:51 +00005024
cristy3ed852e2009-09-05 21:47:34 +00005025 if (logging != MagickFalse)
5026 {
5027 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5028 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00005029
cristy3ed852e2009-09-05 21:47:34 +00005030 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005031 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00005032
cristy3ed852e2009-09-05 21:47:34 +00005033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005034 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00005035 }
glennrp0fe50b42010-11-16 03:52:51 +00005036
cristy3ed852e2009-09-05 21:47:34 +00005037 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5038 continue;
5039 }
5040 if (memcmp(type,mng_DEFI,4) == 0)
5041 {
5042 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005043 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005044 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5045 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005046
cristy3ed852e2009-09-05 21:47:34 +00005047 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 if (mng_type == 2 && object_id != 0)
cristy16ea1392012-03-21 20:38:41 +00005050 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005051 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5052 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005053
cristy3ed852e2009-09-05 21:47:34 +00005054 if (object_id > MNG_MAX_OBJECTS)
5055 {
5056 /*
glennrpedaa0382012-04-12 14:16:21 +00005057 Instead of using a warning we should allocate a larger
cristy3ed852e2009-09-05 21:47:34 +00005058 MngInfo structure and continue.
5059 */
cristy16ea1392012-03-21 20:38:41 +00005060 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005061 CoderError,"object id too large","`%s'",image->filename);
5062 object_id=MNG_MAX_OBJECTS;
5063 }
glennrp0fe50b42010-11-16 03:52:51 +00005064
cristy3ed852e2009-09-05 21:47:34 +00005065 if (mng_info->exists[object_id])
5066 if (mng_info->frozen[object_id])
5067 {
5068 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristy16ea1392012-03-21 20:38:41 +00005069 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005070 GetMagickModule(),CoderError,
5071 "DEFI cannot redefine a frozen MNG object","`%s'",
5072 image->filename);
5073 continue;
5074 }
glennrp0fe50b42010-11-16 03:52:51 +00005075
cristy3ed852e2009-09-05 21:47:34 +00005076 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005077
cristy3ed852e2009-09-05 21:47:34 +00005078 if (length > 2)
5079 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005080
cristy3ed852e2009-09-05 21:47:34 +00005081 /*
5082 Extract object offset info.
5083 */
5084 if (length > 11)
5085 {
glennrp0fe50b42010-11-16 03:52:51 +00005086 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5087 (p[5] << 16) | (p[6] << 8) | p[7]);
5088
5089 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5090 (p[9] << 16) | (p[10] << 8) | p[11]);
5091
cristy3ed852e2009-09-05 21:47:34 +00005092 if (logging != MagickFalse)
5093 {
5094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005095 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005096 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005097
cristy3ed852e2009-09-05 21:47:34 +00005098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005099 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005100 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005101 }
5102 }
glennrp0fe50b42010-11-16 03:52:51 +00005103
cristy3ed852e2009-09-05 21:47:34 +00005104 /*
5105 Extract object clipping info.
5106 */
5107 if (length > 27)
5108 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5109 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005110
cristy3ed852e2009-09-05 21:47:34 +00005111 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5112 continue;
5113 }
5114 if (memcmp(type,mng_bKGD,4) == 0)
5115 {
5116 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005117
cristy3ed852e2009-09-05 21:47:34 +00005118 if (length > 5)
5119 {
5120 mng_info->mng_global_bkgd.red=
5121 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005122
cristy3ed852e2009-09-05 21:47:34 +00005123 mng_info->mng_global_bkgd.green=
5124 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005125
cristy3ed852e2009-09-05 21:47:34 +00005126 mng_info->mng_global_bkgd.blue=
5127 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005128
cristy3ed852e2009-09-05 21:47:34 +00005129 mng_info->have_global_bkgd=MagickTrue;
5130 }
glennrp0fe50b42010-11-16 03:52:51 +00005131
cristy3ed852e2009-09-05 21:47:34 +00005132 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5133 continue;
5134 }
5135 if (memcmp(type,mng_BACK,4) == 0)
5136 {
5137#if defined(MNG_INSERT_LAYERS)
5138 if (length > 6)
5139 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005140
cristy3ed852e2009-09-05 21:47:34 +00005141 else
5142 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005143
cristy3ed852e2009-09-05 21:47:34 +00005144 if (mandatory_back && length > 5)
5145 {
5146 mng_background_color.red=
5147 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005148
cristy3ed852e2009-09-05 21:47:34 +00005149 mng_background_color.green=
5150 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005151
cristy3ed852e2009-09-05 21:47:34 +00005152 mng_background_color.blue=
5153 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005154
cristy16ea1392012-03-21 20:38:41 +00005155 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005156 }
glennrp0fe50b42010-11-16 03:52:51 +00005157
cristy3ed852e2009-09-05 21:47:34 +00005158#ifdef MNG_OBJECT_BUFFERS
5159 if (length > 8)
5160 mng_background_object=(p[7] << 8) | p[8];
5161#endif
5162#endif
5163 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5164 continue;
5165 }
glennrp47b9dd52010-11-24 18:12:06 +00005166
cristy3ed852e2009-09-05 21:47:34 +00005167 if (memcmp(type,mng_PLTE,4) == 0)
5168 {
glennrp47b9dd52010-11-24 18:12:06 +00005169 /* Read global PLTE. */
5170
cristy3ed852e2009-09-05 21:47:34 +00005171 if (length && (length < 769))
5172 {
5173 if (mng_info->global_plte == (png_colorp) NULL)
5174 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5175 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005176
cristybb503372010-05-27 20:51:26 +00005177 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005178 {
5179 mng_info->global_plte[i].red=p[3*i];
5180 mng_info->global_plte[i].green=p[3*i+1];
5181 mng_info->global_plte[i].blue=p[3*i+2];
5182 }
glennrp0fe50b42010-11-16 03:52:51 +00005183
cristy35ef8242010-06-03 16:24:13 +00005184 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005185 }
5186#ifdef MNG_LOOSE
5187 for ( ; i < 256; i++)
5188 {
5189 mng_info->global_plte[i].red=i;
5190 mng_info->global_plte[i].green=i;
5191 mng_info->global_plte[i].blue=i;
5192 }
glennrp0fe50b42010-11-16 03:52:51 +00005193
cristy3ed852e2009-09-05 21:47:34 +00005194 if (length)
5195 mng_info->global_plte_length=256;
5196#endif
5197 else
5198 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005199
cristy3ed852e2009-09-05 21:47:34 +00005200 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5201 continue;
5202 }
glennrp47b9dd52010-11-24 18:12:06 +00005203
cristy3ed852e2009-09-05 21:47:34 +00005204 if (memcmp(type,mng_tRNS,4) == 0)
5205 {
5206 /* read global tRNS */
5207
5208 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005209 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005210 mng_info->global_trns[i]=p[i];
5211
5212#ifdef MNG_LOOSE
5213 for ( ; i < 256; i++)
5214 mng_info->global_trns[i]=255;
5215#endif
cristy12560f32010-06-03 16:51:08 +00005216 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005217 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5218 continue;
5219 }
5220 if (memcmp(type,mng_gAMA,4) == 0)
5221 {
5222 if (length == 4)
5223 {
cristybb503372010-05-27 20:51:26 +00005224 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005225 igamma;
5226
cristy8182b072010-05-30 20:10:53 +00005227 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005228 mng_info->global_gamma=((float) igamma)*0.00001;
5229 mng_info->have_global_gama=MagickTrue;
5230 }
glennrp0fe50b42010-11-16 03:52:51 +00005231
cristy3ed852e2009-09-05 21:47:34 +00005232 else
5233 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005234
cristy3ed852e2009-09-05 21:47:34 +00005235 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5236 continue;
5237 }
5238
5239 if (memcmp(type,mng_cHRM,4) == 0)
5240 {
glennrp47b9dd52010-11-24 18:12:06 +00005241 /* Read global cHRM */
5242
cristy3ed852e2009-09-05 21:47:34 +00005243 if (length == 32)
5244 {
cristy8182b072010-05-30 20:10:53 +00005245 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5246 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5247 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005248 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005249 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005250 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005251 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005252 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005253 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005254 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005255 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005256 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005257 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005258 mng_info->have_global_chrm=MagickTrue;
5259 }
5260 else
5261 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005262
cristy3ed852e2009-09-05 21:47:34 +00005263 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5264 continue;
5265 }
glennrp47b9dd52010-11-24 18:12:06 +00005266
cristy3ed852e2009-09-05 21:47:34 +00005267 if (memcmp(type,mng_sRGB,4) == 0)
5268 {
5269 /*
5270 Read global sRGB.
5271 */
5272 if (length)
5273 {
glennrpe610a072010-08-05 17:08:46 +00005274 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005275 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005276 mng_info->have_global_srgb=MagickTrue;
5277 }
5278 else
5279 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005280
cristy3ed852e2009-09-05 21:47:34 +00005281 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5282 continue;
5283 }
glennrp47b9dd52010-11-24 18:12:06 +00005284
cristy3ed852e2009-09-05 21:47:34 +00005285 if (memcmp(type,mng_iCCP,4) == 0)
5286 {
glennrpfd05d622011-02-25 04:10:33 +00005287 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005288
5289 /*
5290 Read global iCCP.
5291 */
5292 if (length)
5293 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005294
cristy3ed852e2009-09-05 21:47:34 +00005295 continue;
5296 }
glennrp47b9dd52010-11-24 18:12:06 +00005297
cristy3ed852e2009-09-05 21:47:34 +00005298 if (memcmp(type,mng_FRAM,4) == 0)
5299 {
5300 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005301 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005302 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5303 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005304
cristy3ed852e2009-09-05 21:47:34 +00005305 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5306 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005307
cristy3ed852e2009-09-05 21:47:34 +00005308 frame_delay=default_frame_delay;
5309 frame_timeout=default_frame_timeout;
5310 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005311
cristy3ed852e2009-09-05 21:47:34 +00005312 if (length)
5313 if (p[0])
5314 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005315
cristy3ed852e2009-09-05 21:47:34 +00005316 if (logging != MagickFalse)
5317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5318 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005319
cristy3ed852e2009-09-05 21:47:34 +00005320 if (length > 6)
5321 {
glennrp47b9dd52010-11-24 18:12:06 +00005322 /* Note the delay and frame clipping boundaries. */
5323
cristy3ed852e2009-09-05 21:47:34 +00005324 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005325
cristybb503372010-05-27 20:51:26 +00005326 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005327 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005328
cristy3ed852e2009-09-05 21:47:34 +00005329 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005330
cristybb503372010-05-27 20:51:26 +00005331 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005332 {
5333 int
5334 change_delay,
5335 change_timeout,
5336 change_clipping;
5337
5338 change_delay=(*p++);
5339 change_timeout=(*p++);
5340 change_clipping=(*p++);
5341 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005342
cristy3ed852e2009-09-05 21:47:34 +00005343 if (change_delay)
5344 {
cristy8182b072010-05-30 20:10:53 +00005345 frame_delay=1UL*image->ticks_per_second*
5346 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005347
cristy8182b072010-05-30 20:10:53 +00005348 if (mng_info->ticks_per_second != 0)
5349 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005350
glennrpbb010dd2010-06-01 13:07:15 +00005351 else
5352 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005353
cristy3ed852e2009-09-05 21:47:34 +00005354 if (change_delay == 2)
5355 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005356
cristy3ed852e2009-09-05 21:47:34 +00005357 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005358
cristy3ed852e2009-09-05 21:47:34 +00005359 if (logging != MagickFalse)
5360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005361 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005362 }
glennrp47b9dd52010-11-24 18:12:06 +00005363
cristy3ed852e2009-09-05 21:47:34 +00005364 if (change_timeout)
5365 {
glennrpbb010dd2010-06-01 13:07:15 +00005366 frame_timeout=1UL*image->ticks_per_second*
5367 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005368
glennrpbb010dd2010-06-01 13:07:15 +00005369 if (mng_info->ticks_per_second != 0)
5370 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005371
glennrpbb010dd2010-06-01 13:07:15 +00005372 else
5373 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005374
cristy3ed852e2009-09-05 21:47:34 +00005375 if (change_delay == 2)
5376 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005377
cristy3ed852e2009-09-05 21:47:34 +00005378 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005379
cristy3ed852e2009-09-05 21:47:34 +00005380 if (logging != MagickFalse)
5381 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005382 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005383 }
glennrp47b9dd52010-11-24 18:12:06 +00005384
cristy3ed852e2009-09-05 21:47:34 +00005385 if (change_clipping)
5386 {
5387 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5388 p+=17;
5389 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005390
cristy3ed852e2009-09-05 21:47:34 +00005391 if (logging != MagickFalse)
5392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005393 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005394 (double) fb.left,(double) fb.right,(double) fb.top,
5395 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005396
cristy3ed852e2009-09-05 21:47:34 +00005397 if (change_clipping == 2)
5398 default_fb=fb;
5399 }
5400 }
5401 }
5402 mng_info->clip=fb;
5403 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005404
cristybb503372010-05-27 20:51:26 +00005405 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005406 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005407
cristybb503372010-05-27 20:51:26 +00005408 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005409 -mng_info->clip.top);
5410 /*
5411 Insert a background layer behind the frame if framing_mode is 4.
5412 */
5413#if defined(MNG_INSERT_LAYERS)
5414 if (logging != MagickFalse)
5415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005416 " subframe_width=%.20g, subframe_height=%.20g",(double)
5417 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005418
cristy3ed852e2009-09-05 21:47:34 +00005419 if (insert_layers && (mng_info->framing_mode == 4) &&
5420 (subframe_width) && (subframe_height))
5421 {
glennrp47b9dd52010-11-24 18:12:06 +00005422 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005423 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005424 {
cristy16ea1392012-03-21 20:38:41 +00005425 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005426
cristy3ed852e2009-09-05 21:47:34 +00005427 if (GetNextImageInList(image) == (Image *) NULL)
5428 {
5429 image=DestroyImageList(image);
5430 MngInfoFreeStruct(mng_info,&have_mng_structure);
5431 return((Image *) NULL);
5432 }
glennrp47b9dd52010-11-24 18:12:06 +00005433
cristy3ed852e2009-09-05 21:47:34 +00005434 image=SyncNextImageInList(image);
5435 }
glennrp0fe50b42010-11-16 03:52:51 +00005436
cristy3ed852e2009-09-05 21:47:34 +00005437 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005438
cristy3ed852e2009-09-05 21:47:34 +00005439 if (term_chunk_found)
5440 {
5441 image->start_loop=MagickTrue;
5442 image->iterations=mng_iterations;
5443 term_chunk_found=MagickFalse;
5444 }
glennrp0fe50b42010-11-16 03:52:51 +00005445
cristy3ed852e2009-09-05 21:47:34 +00005446 else
5447 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005448
cristy3ed852e2009-09-05 21:47:34 +00005449 image->columns=subframe_width;
5450 image->rows=subframe_height;
5451 image->page.width=subframe_width;
5452 image->page.height=subframe_height;
5453 image->page.x=mng_info->clip.left;
5454 image->page.y=mng_info->clip.top;
5455 image->background_color=mng_background_color;
5456 image->matte=MagickFalse;
5457 image->delay=0;
cristy16ea1392012-03-21 20:38:41 +00005458 (void) SetImageBackgroundColor(image,exception);
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 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005463 (double) mng_info->clip.left,(double) mng_info->clip.right,
5464 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005465 }
5466#endif
5467 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5468 continue;
5469 }
5470 if (memcmp(type,mng_CLIP,4) == 0)
5471 {
5472 unsigned int
5473 first_object,
5474 last_object;
5475
5476 /*
5477 Read CLIP.
5478 */
5479 first_object=(p[0] << 8) | p[1];
5480 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005481
cristy3ed852e2009-09-05 21:47:34 +00005482 for (i=(int) first_object; i <= (int) last_object; i++)
5483 {
5484 if (mng_info->exists[i] && !mng_info->frozen[i])
5485 {
5486 MngBox
5487 box;
5488
5489 box=mng_info->object_clip[i];
5490 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5491 }
5492 }
glennrp47b9dd52010-11-24 18:12:06 +00005493
cristy3ed852e2009-09-05 21:47:34 +00005494 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5495 continue;
5496 }
5497 if (memcmp(type,mng_SAVE,4) == 0)
5498 {
5499 for (i=1; i < MNG_MAX_OBJECTS; i++)
5500 if (mng_info->exists[i])
5501 {
5502 mng_info->frozen[i]=MagickTrue;
5503#ifdef MNG_OBJECT_BUFFERS
5504 if (mng_info->ob[i] != (MngBuffer *) NULL)
5505 mng_info->ob[i]->frozen=MagickTrue;
5506#endif
5507 }
glennrp0fe50b42010-11-16 03:52:51 +00005508
cristy3ed852e2009-09-05 21:47:34 +00005509 if (length)
5510 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005511
cristy3ed852e2009-09-05 21:47:34 +00005512 continue;
5513 }
5514
5515 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5516 {
glennrp47b9dd52010-11-24 18:12:06 +00005517 /* Read DISC or SEEK. */
5518
cristy3ed852e2009-09-05 21:47:34 +00005519 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5520 {
5521 for (i=1; i < MNG_MAX_OBJECTS; i++)
5522 MngInfoDiscardObject(mng_info,i);
5523 }
glennrp0fe50b42010-11-16 03:52:51 +00005524
cristy3ed852e2009-09-05 21:47:34 +00005525 else
5526 {
cristybb503372010-05-27 20:51:26 +00005527 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005528 j;
5529
cristybb503372010-05-27 20:51:26 +00005530 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005531 {
5532 i=p[j] << 8 | p[j+1];
5533 MngInfoDiscardObject(mng_info,i);
5534 }
5535 }
glennrp0fe50b42010-11-16 03:52:51 +00005536
cristy3ed852e2009-09-05 21:47:34 +00005537 if (length)
5538 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005539
cristy3ed852e2009-09-05 21:47:34 +00005540 continue;
5541 }
glennrp47b9dd52010-11-24 18:12:06 +00005542
cristy3ed852e2009-09-05 21:47:34 +00005543 if (memcmp(type,mng_MOVE,4) == 0)
5544 {
cristybb503372010-05-27 20:51:26 +00005545 size_t
cristy3ed852e2009-09-05 21:47:34 +00005546 first_object,
5547 last_object;
5548
glennrp47b9dd52010-11-24 18:12:06 +00005549 /* read MOVE */
5550
cristy3ed852e2009-09-05 21:47:34 +00005551 first_object=(p[0] << 8) | p[1];
5552 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005553 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005554 {
5555 if (mng_info->exists[i] && !mng_info->frozen[i])
5556 {
5557 MngPair
5558 new_pair;
5559
5560 MngPair
5561 old_pair;
5562
5563 old_pair.a=mng_info->x_off[i];
5564 old_pair.b=mng_info->y_off[i];
5565 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5566 mng_info->x_off[i]=new_pair.a;
5567 mng_info->y_off[i]=new_pair.b;
5568 }
5569 }
glennrp47b9dd52010-11-24 18:12:06 +00005570
cristy3ed852e2009-09-05 21:47:34 +00005571 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5572 continue;
5573 }
5574
5575 if (memcmp(type,mng_LOOP,4) == 0)
5576 {
cristybb503372010-05-27 20:51:26 +00005577 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005578 loop_level=chunk[0];
5579 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005580
5581 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005582 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005583
cristy3ed852e2009-09-05 21:47:34 +00005584 if (logging != MagickFalse)
5585 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005586 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5587 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005588
cristy3ed852e2009-09-05 21:47:34 +00005589 if (loop_iters == 0)
5590 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005591
cristy3ed852e2009-09-05 21:47:34 +00005592 else
5593 {
5594 mng_info->loop_jump[loop_level]=TellBlob(image);
5595 mng_info->loop_count[loop_level]=loop_iters;
5596 }
glennrp0fe50b42010-11-16 03:52:51 +00005597
cristy3ed852e2009-09-05 21:47:34 +00005598 mng_info->loop_iteration[loop_level]=0;
5599 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5600 continue;
5601 }
glennrp47b9dd52010-11-24 18:12:06 +00005602
cristy3ed852e2009-09-05 21:47:34 +00005603 if (memcmp(type,mng_ENDL,4) == 0)
5604 {
5605 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005606
cristy3ed852e2009-09-05 21:47:34 +00005607 if (skipping_loop > 0)
5608 {
5609 if (skipping_loop == loop_level)
5610 {
5611 /*
5612 Found end of zero-iteration loop.
5613 */
5614 skipping_loop=(-1);
5615 mng_info->loop_active[loop_level]=0;
5616 }
5617 }
glennrp47b9dd52010-11-24 18:12:06 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619 else
5620 {
5621 if (mng_info->loop_active[loop_level] == 1)
5622 {
5623 mng_info->loop_count[loop_level]--;
5624 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005625
cristy3ed852e2009-09-05 21:47:34 +00005626 if (logging != MagickFalse)
5627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005628 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005629 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005630 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005631
cristy3ed852e2009-09-05 21:47:34 +00005632 if (mng_info->loop_count[loop_level] != 0)
5633 {
5634 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5635 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005636
cristy3ed852e2009-09-05 21:47:34 +00005637 if (offset < 0)
5638 ThrowReaderException(CorruptImageError,
5639 "ImproperImageHeader");
5640 }
glennrp47b9dd52010-11-24 18:12:06 +00005641
cristy3ed852e2009-09-05 21:47:34 +00005642 else
5643 {
5644 short
5645 last_level;
5646
5647 /*
5648 Finished loop.
5649 */
5650 mng_info->loop_active[loop_level]=0;
5651 last_level=(-1);
5652 for (i=0; i < loop_level; i++)
5653 if (mng_info->loop_active[i] == 1)
5654 last_level=(short) i;
5655 loop_level=last_level;
5656 }
5657 }
5658 }
glennrp47b9dd52010-11-24 18:12:06 +00005659
cristy3ed852e2009-09-05 21:47:34 +00005660 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5661 continue;
5662 }
glennrp47b9dd52010-11-24 18:12:06 +00005663
cristy3ed852e2009-09-05 21:47:34 +00005664 if (memcmp(type,mng_CLON,4) == 0)
5665 {
5666 if (mng_info->clon_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005667 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005668 CoderError,"CLON is not implemented yet","`%s'",
5669 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005670
cristy3ed852e2009-09-05 21:47:34 +00005671 mng_info->clon_warning++;
5672 }
glennrp47b9dd52010-11-24 18:12:06 +00005673
cristy3ed852e2009-09-05 21:47:34 +00005674 if (memcmp(type,mng_MAGN,4) == 0)
5675 {
5676 png_uint_16
5677 magn_first,
5678 magn_last,
5679 magn_mb,
5680 magn_ml,
5681 magn_mr,
5682 magn_mt,
5683 magn_mx,
5684 magn_my,
5685 magn_methx,
5686 magn_methy;
5687
5688 if (length > 1)
5689 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005690
cristy3ed852e2009-09-05 21:47:34 +00005691 else
5692 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005693
cristy3ed852e2009-09-05 21:47:34 +00005694 if (length > 3)
5695 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005696
cristy3ed852e2009-09-05 21:47:34 +00005697 else
5698 magn_last=magn_first;
5699#ifndef MNG_OBJECT_BUFFERS
5700 if (magn_first || magn_last)
5701 if (mng_info->magn_warning == 0)
5702 {
cristy16ea1392012-03-21 20:38:41 +00005703 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005704 GetMagickModule(),CoderError,
5705 "MAGN is not implemented yet for nonzero objects",
5706 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005707
cristy3ed852e2009-09-05 21:47:34 +00005708 mng_info->magn_warning++;
5709 }
5710#endif
5711 if (length > 4)
5712 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005713
cristy3ed852e2009-09-05 21:47:34 +00005714 else
5715 magn_methx=0;
5716
5717 if (length > 6)
5718 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005719
cristy3ed852e2009-09-05 21:47:34 +00005720 else
5721 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005722
cristy3ed852e2009-09-05 21:47:34 +00005723 if (magn_mx == 0)
5724 magn_mx=1;
5725
5726 if (length > 8)
5727 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005728
cristy3ed852e2009-09-05 21:47:34 +00005729 else
5730 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005731
cristy3ed852e2009-09-05 21:47:34 +00005732 if (magn_my == 0)
5733 magn_my=1;
5734
5735 if (length > 10)
5736 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005737
cristy3ed852e2009-09-05 21:47:34 +00005738 else
5739 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005740
cristy3ed852e2009-09-05 21:47:34 +00005741 if (magn_ml == 0)
5742 magn_ml=1;
5743
5744 if (length > 12)
5745 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005746
cristy3ed852e2009-09-05 21:47:34 +00005747 else
5748 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005749
cristy3ed852e2009-09-05 21:47:34 +00005750 if (magn_mr == 0)
5751 magn_mr=1;
5752
5753 if (length > 14)
5754 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005755
cristy3ed852e2009-09-05 21:47:34 +00005756 else
5757 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005758
cristy3ed852e2009-09-05 21:47:34 +00005759 if (magn_mt == 0)
5760 magn_mt=1;
5761
5762 if (length > 16)
5763 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005764
cristy3ed852e2009-09-05 21:47:34 +00005765 else
5766 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005767
cristy3ed852e2009-09-05 21:47:34 +00005768 if (magn_mb == 0)
5769 magn_mb=1;
5770
5771 if (length > 17)
5772 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005773
cristy3ed852e2009-09-05 21:47:34 +00005774 else
5775 magn_methy=magn_methx;
5776
glennrp47b9dd52010-11-24 18:12:06 +00005777
cristy3ed852e2009-09-05 21:47:34 +00005778 if (magn_methx > 5 || magn_methy > 5)
5779 if (mng_info->magn_warning == 0)
5780 {
cristy16ea1392012-03-21 20:38:41 +00005781 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005782 GetMagickModule(),CoderError,
5783 "Unknown MAGN method in MNG datastream","`%s'",
5784 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005785
cristy3ed852e2009-09-05 21:47:34 +00005786 mng_info->magn_warning++;
5787 }
5788#ifdef MNG_OBJECT_BUFFERS
5789 /* Magnify existing objects in the range magn_first to magn_last */
5790#endif
5791 if (magn_first == 0 || magn_last == 0)
5792 {
5793 /* Save the magnification factors for object 0 */
5794 mng_info->magn_mb=magn_mb;
5795 mng_info->magn_ml=magn_ml;
5796 mng_info->magn_mr=magn_mr;
5797 mng_info->magn_mt=magn_mt;
5798 mng_info->magn_mx=magn_mx;
5799 mng_info->magn_my=magn_my;
5800 mng_info->magn_methx=magn_methx;
5801 mng_info->magn_methy=magn_methy;
5802 }
5803 }
glennrp47b9dd52010-11-24 18:12:06 +00005804
cristy3ed852e2009-09-05 21:47:34 +00005805 if (memcmp(type,mng_PAST,4) == 0)
5806 {
5807 if (mng_info->past_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005808 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005809 CoderError,"PAST is not implemented yet","`%s'",
5810 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005811
cristy3ed852e2009-09-05 21:47:34 +00005812 mng_info->past_warning++;
5813 }
glennrp47b9dd52010-11-24 18:12:06 +00005814
cristy3ed852e2009-09-05 21:47:34 +00005815 if (memcmp(type,mng_SHOW,4) == 0)
5816 {
5817 if (mng_info->show_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005818 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005819 CoderError,"SHOW is not implemented yet","`%s'",
5820 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005821
cristy3ed852e2009-09-05 21:47:34 +00005822 mng_info->show_warning++;
5823 }
glennrp47b9dd52010-11-24 18:12:06 +00005824
cristy3ed852e2009-09-05 21:47:34 +00005825 if (memcmp(type,mng_sBIT,4) == 0)
5826 {
5827 if (length < 4)
5828 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005829
cristy3ed852e2009-09-05 21:47:34 +00005830 else
5831 {
5832 mng_info->global_sbit.gray=p[0];
5833 mng_info->global_sbit.red=p[0];
5834 mng_info->global_sbit.green=p[1];
5835 mng_info->global_sbit.blue=p[2];
5836 mng_info->global_sbit.alpha=p[3];
5837 mng_info->have_global_sbit=MagickTrue;
5838 }
5839 }
5840 if (memcmp(type,mng_pHYs,4) == 0)
5841 {
5842 if (length > 8)
5843 {
5844 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005845 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005846 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005847 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005848 mng_info->global_phys_unit_type=p[8];
5849 mng_info->have_global_phys=MagickTrue;
5850 }
glennrp47b9dd52010-11-24 18:12:06 +00005851
cristy3ed852e2009-09-05 21:47:34 +00005852 else
5853 mng_info->have_global_phys=MagickFalse;
5854 }
5855 if (memcmp(type,mng_pHYg,4) == 0)
5856 {
5857 if (mng_info->phyg_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005858 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005859 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005860
cristy3ed852e2009-09-05 21:47:34 +00005861 mng_info->phyg_warning++;
5862 }
5863 if (memcmp(type,mng_BASI,4) == 0)
5864 {
5865 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005866
cristy3ed852e2009-09-05 21:47:34 +00005867 if (mng_info->basi_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005868 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005869 CoderError,"BASI is not implemented yet","`%s'",
5870 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005871
cristy3ed852e2009-09-05 21:47:34 +00005872 mng_info->basi_warning++;
5873#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005874 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005875 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005876 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005877 (p[6] << 8) | p[7]);
5878 basi_color_type=p[8];
5879 basi_compression_method=p[9];
5880 basi_filter_type=p[10];
5881 basi_interlace_method=p[11];
5882 if (length > 11)
5883 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005884
cristy3ed852e2009-09-05 21:47:34 +00005885 else
5886 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005887
cristy3ed852e2009-09-05 21:47:34 +00005888 if (length > 13)
5889 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005890
cristy3ed852e2009-09-05 21:47:34 +00005891 else
5892 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005893
cristy3ed852e2009-09-05 21:47:34 +00005894 if (length > 15)
5895 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005896
cristy3ed852e2009-09-05 21:47:34 +00005897 else
5898 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005899
cristy3ed852e2009-09-05 21:47:34 +00005900 if (length > 17)
5901 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005902
cristy3ed852e2009-09-05 21:47:34 +00005903 else
5904 {
5905 if (basi_sample_depth == 16)
5906 basi_alpha=65535L;
5907 else
5908 basi_alpha=255;
5909 }
glennrp47b9dd52010-11-24 18:12:06 +00005910
cristy3ed852e2009-09-05 21:47:34 +00005911 if (length > 19)
5912 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005913
cristy3ed852e2009-09-05 21:47:34 +00005914 else
5915 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005916
cristy3ed852e2009-09-05 21:47:34 +00005917#endif
5918 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5919 continue;
5920 }
glennrp47b9dd52010-11-24 18:12:06 +00005921
cristy3ed852e2009-09-05 21:47:34 +00005922 if (memcmp(type,mng_IHDR,4)
5923#if defined(JNG_SUPPORTED)
5924 && memcmp(type,mng_JHDR,4)
5925#endif
5926 )
5927 {
5928 /* Not an IHDR or JHDR chunk */
5929 if (length)
5930 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005931
cristy3ed852e2009-09-05 21:47:34 +00005932 continue;
5933 }
5934/* Process IHDR */
5935 if (logging != MagickFalse)
5936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5937 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005938
cristy3ed852e2009-09-05 21:47:34 +00005939 mng_info->exists[object_id]=MagickTrue;
5940 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005941
cristy3ed852e2009-09-05 21:47:34 +00005942 if (mng_info->invisible[object_id])
5943 {
5944 if (logging != MagickFalse)
5945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5946 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005947
cristy3ed852e2009-09-05 21:47:34 +00005948 skip_to_iend=MagickTrue;
5949 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5950 continue;
5951 }
5952#if defined(MNG_INSERT_LAYERS)
5953 if (length < 8)
5954 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005955
cristy8182b072010-05-30 20:10:53 +00005956 image_width=(size_t) mng_get_long(p);
5957 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005958#endif
5959 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5960
5961 /*
5962 Insert a transparent background layer behind the entire animation
5963 if it is not full screen.
5964 */
5965#if defined(MNG_INSERT_LAYERS)
5966 if (insert_layers && mng_type && first_mng_object)
5967 {
5968 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5969 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005970 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005971 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005972 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005973 {
cristy16ea1392012-03-21 20:38:41 +00005974 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005975 {
5976 /*
5977 Allocate next image structure.
5978 */
cristy16ea1392012-03-21 20:38:41 +00005979 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005980
cristy3ed852e2009-09-05 21:47:34 +00005981 if (GetNextImageInList(image) == (Image *) NULL)
5982 {
5983 image=DestroyImageList(image);
5984 MngInfoFreeStruct(mng_info,&have_mng_structure);
5985 return((Image *) NULL);
5986 }
glennrp47b9dd52010-11-24 18:12:06 +00005987
cristy3ed852e2009-09-05 21:47:34 +00005988 image=SyncNextImageInList(image);
5989 }
5990 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005991
cristy3ed852e2009-09-05 21:47:34 +00005992 if (term_chunk_found)
5993 {
5994 image->start_loop=MagickTrue;
5995 image->iterations=mng_iterations;
5996 term_chunk_found=MagickFalse;
5997 }
glennrp47b9dd52010-11-24 18:12:06 +00005998
cristy3ed852e2009-09-05 21:47:34 +00005999 else
6000 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006001
6002 /* Make a background rectangle. */
6003
cristy3ed852e2009-09-05 21:47:34 +00006004 image->delay=0;
6005 image->columns=mng_info->mng_width;
6006 image->rows=mng_info->mng_height;
6007 image->page.width=mng_info->mng_width;
6008 image->page.height=mng_info->mng_height;
6009 image->page.x=0;
6010 image->page.y=0;
6011 image->background_color=mng_background_color;
cristy16ea1392012-03-21 20:38:41 +00006012 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006013 if (logging != MagickFalse)
6014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006015 " Inserted transparent background layer, W=%.20g, H=%.20g",
6016 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00006017 }
6018 }
6019 /*
6020 Insert a background layer behind the upcoming image if
6021 framing_mode is 3, and we haven't already inserted one.
6022 */
6023 if (insert_layers && (mng_info->framing_mode == 3) &&
6024 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6025 (simplicity & 0x08)))
6026 {
cristy16ea1392012-03-21 20:38:41 +00006027 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006028 {
6029 /*
6030 Allocate next image structure.
6031 */
cristy16ea1392012-03-21 20:38:41 +00006032 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006033
cristy3ed852e2009-09-05 21:47:34 +00006034 if (GetNextImageInList(image) == (Image *) NULL)
6035 {
6036 image=DestroyImageList(image);
6037 MngInfoFreeStruct(mng_info,&have_mng_structure);
6038 return((Image *) NULL);
6039 }
glennrp47b9dd52010-11-24 18:12:06 +00006040
cristy3ed852e2009-09-05 21:47:34 +00006041 image=SyncNextImageInList(image);
6042 }
glennrp0fe50b42010-11-16 03:52:51 +00006043
cristy3ed852e2009-09-05 21:47:34 +00006044 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00006045
cristy3ed852e2009-09-05 21:47:34 +00006046 if (term_chunk_found)
6047 {
6048 image->start_loop=MagickTrue;
6049 image->iterations=mng_iterations;
6050 term_chunk_found=MagickFalse;
6051 }
glennrp0fe50b42010-11-16 03:52:51 +00006052
cristy3ed852e2009-09-05 21:47:34 +00006053 else
6054 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006055
cristy3ed852e2009-09-05 21:47:34 +00006056 image->delay=0;
6057 image->columns=subframe_width;
6058 image->rows=subframe_height;
6059 image->page.width=subframe_width;
6060 image->page.height=subframe_height;
6061 image->page.x=mng_info->clip.left;
6062 image->page.y=mng_info->clip.top;
6063 image->background_color=mng_background_color;
6064 image->matte=MagickFalse;
cristy16ea1392012-03-21 20:38:41 +00006065 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006066
cristy3ed852e2009-09-05 21:47:34 +00006067 if (logging != MagickFalse)
6068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006069 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006070 (double) mng_info->clip.left,(double) mng_info->clip.right,
6071 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006072 }
6073#endif /* MNG_INSERT_LAYERS */
6074 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006075
cristy16ea1392012-03-21 20:38:41 +00006076 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006077 {
6078 /*
6079 Allocate next image structure.
6080 */
cristy16ea1392012-03-21 20:38:41 +00006081 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006082
cristy3ed852e2009-09-05 21:47:34 +00006083 if (GetNextImageInList(image) == (Image *) NULL)
6084 {
6085 image=DestroyImageList(image);
6086 MngInfoFreeStruct(mng_info,&have_mng_structure);
6087 return((Image *) NULL);
6088 }
glennrp47b9dd52010-11-24 18:12:06 +00006089
cristy3ed852e2009-09-05 21:47:34 +00006090 image=SyncNextImageInList(image);
6091 }
6092 mng_info->image=image;
6093 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6094 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006095
cristy3ed852e2009-09-05 21:47:34 +00006096 if (status == MagickFalse)
6097 break;
glennrp0fe50b42010-11-16 03:52:51 +00006098
cristy3ed852e2009-09-05 21:47:34 +00006099 if (term_chunk_found)
6100 {
6101 image->start_loop=MagickTrue;
6102 term_chunk_found=MagickFalse;
6103 }
glennrp0fe50b42010-11-16 03:52:51 +00006104
cristy3ed852e2009-09-05 21:47:34 +00006105 else
6106 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006107
cristy3ed852e2009-09-05 21:47:34 +00006108 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6109 {
6110 image->delay=frame_delay;
6111 frame_delay=default_frame_delay;
6112 }
glennrp0fe50b42010-11-16 03:52:51 +00006113
cristy3ed852e2009-09-05 21:47:34 +00006114 else
6115 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006116
cristy3ed852e2009-09-05 21:47:34 +00006117 image->page.width=mng_info->mng_width;
6118 image->page.height=mng_info->mng_height;
6119 image->page.x=mng_info->x_off[object_id];
6120 image->page.y=mng_info->y_off[object_id];
6121 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006122
cristy3ed852e2009-09-05 21:47:34 +00006123 /*
6124 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6125 */
glennrp47b9dd52010-11-24 18:12:06 +00006126
cristy3ed852e2009-09-05 21:47:34 +00006127 if (logging != MagickFalse)
6128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6129 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6130 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006131
cristybb503372010-05-27 20:51:26 +00006132 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006133
cristy3ed852e2009-09-05 21:47:34 +00006134 if (offset < 0)
6135 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6136 }
6137
6138 previous=image;
6139 mng_info->image=image;
6140 mng_info->mng_type=mng_type;
6141 mng_info->object_id=object_id;
6142
6143 if (memcmp(type,mng_IHDR,4) == 0)
6144 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006145
cristy3ed852e2009-09-05 21:47:34 +00006146#if defined(JNG_SUPPORTED)
6147 else
6148 image=ReadOneJNGImage(mng_info,image_info,exception);
6149#endif
6150
6151 if (image == (Image *) NULL)
6152 {
6153 if (IsImageObject(previous) != MagickFalse)
6154 {
6155 (void) DestroyImageList(previous);
6156 (void) CloseBlob(previous);
6157 }
glennrp47b9dd52010-11-24 18:12:06 +00006158
cristy3ed852e2009-09-05 21:47:34 +00006159 MngInfoFreeStruct(mng_info,&have_mng_structure);
6160 return((Image *) NULL);
6161 }
glennrp0fe50b42010-11-16 03:52:51 +00006162
cristy3ed852e2009-09-05 21:47:34 +00006163 if (image->columns == 0 || image->rows == 0)
6164 {
6165 (void) CloseBlob(image);
6166 image=DestroyImageList(image);
6167 MngInfoFreeStruct(mng_info,&have_mng_structure);
6168 return((Image *) NULL);
6169 }
glennrp0fe50b42010-11-16 03:52:51 +00006170
cristy3ed852e2009-09-05 21:47:34 +00006171 mng_info->image=image;
6172
6173 if (mng_type)
6174 {
6175 MngBox
6176 crop_box;
6177
6178 if (mng_info->magn_methx || mng_info->magn_methy)
6179 {
6180 png_uint_32
6181 magnified_height,
6182 magnified_width;
6183
6184 if (logging != MagickFalse)
6185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6186 " Processing MNG MAGN chunk");
6187
6188 if (mng_info->magn_methx == 1)
6189 {
6190 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006191
cristy3ed852e2009-09-05 21:47:34 +00006192 if (image->columns > 1)
6193 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006194
cristy3ed852e2009-09-05 21:47:34 +00006195 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006196 magnified_width += (png_uint_32)
6197 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006198 }
glennrp47b9dd52010-11-24 18:12:06 +00006199
cristy3ed852e2009-09-05 21:47:34 +00006200 else
6201 {
cristy4e5bc842010-06-09 13:56:01 +00006202 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006203
cristy3ed852e2009-09-05 21:47:34 +00006204 if (image->columns > 1)
6205 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006206
cristy3ed852e2009-09-05 21:47:34 +00006207 if (image->columns > 2)
6208 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006209
cristy3ed852e2009-09-05 21:47:34 +00006210 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006211 magnified_width += (png_uint_32)
6212 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006213 }
glennrp47b9dd52010-11-24 18:12:06 +00006214
cristy3ed852e2009-09-05 21:47:34 +00006215 if (mng_info->magn_methy == 1)
6216 {
6217 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006218
cristy3ed852e2009-09-05 21:47:34 +00006219 if (image->rows > 1)
6220 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006221
cristy3ed852e2009-09-05 21:47:34 +00006222 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006223 magnified_height += (png_uint_32)
6224 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006225 }
glennrp47b9dd52010-11-24 18:12:06 +00006226
cristy3ed852e2009-09-05 21:47:34 +00006227 else
6228 {
cristy4e5bc842010-06-09 13:56:01 +00006229 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006230
cristy3ed852e2009-09-05 21:47:34 +00006231 if (image->rows > 1)
6232 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006233
cristy3ed852e2009-09-05 21:47:34 +00006234 if (image->rows > 2)
6235 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006236
cristy3ed852e2009-09-05 21:47:34 +00006237 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006238 magnified_height += (png_uint_32)
6239 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006240 }
glennrp47b9dd52010-11-24 18:12:06 +00006241
cristy3ed852e2009-09-05 21:47:34 +00006242 if (magnified_height > image->rows ||
6243 magnified_width > image->columns)
6244 {
6245 Image
6246 *large_image;
6247
6248 int
6249 yy;
6250
cristy16ea1392012-03-21 20:38:41 +00006251 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006252 *next,
6253 *prev;
6254
6255 png_uint_16
6256 magn_methx,
6257 magn_methy;
6258
cristy16ea1392012-03-21 20:38:41 +00006259 ssize_t
6260 m,
6261 y;
6262
6263 register Quantum
6264 *n,
6265 *q;
6266
6267 register ssize_t
6268 x;
6269
glennrp47b9dd52010-11-24 18:12:06 +00006270 /* Allocate next image structure. */
6271
cristy3ed852e2009-09-05 21:47:34 +00006272 if (logging != MagickFalse)
6273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6274 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006275
cristy16ea1392012-03-21 20:38:41 +00006276 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006277
cristy3ed852e2009-09-05 21:47:34 +00006278 if (GetNextImageInList(image) == (Image *) NULL)
6279 {
6280 image=DestroyImageList(image);
6281 MngInfoFreeStruct(mng_info,&have_mng_structure);
6282 return((Image *) NULL);
6283 }
6284
6285 large_image=SyncNextImageInList(image);
6286
6287 large_image->columns=magnified_width;
6288 large_image->rows=magnified_height;
6289
6290 magn_methx=mng_info->magn_methx;
6291 magn_methy=mng_info->magn_methy;
6292
glennrp3faa9a32011-04-23 14:00:25 +00006293#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006294#define QM unsigned short
6295 if (magn_methx != 1 || magn_methy != 1)
6296 {
6297 /*
6298 Scale pixels to unsigned shorts to prevent
6299 overflow of intermediate values of interpolations
6300 */
cristybb503372010-05-27 20:51:26 +00006301 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006302 {
6303 q=GetAuthenticPixels(image,0,y,image->columns,1,
6304 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006305
cristybb503372010-05-27 20:51:26 +00006306 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006307 {
cristy16ea1392012-03-21 20:38:41 +00006308 SetPixelRed(image,ScaleQuantumToShort(
6309 GetPixelRed(image,q)),q);
6310 SetPixelGreen(image,ScaleQuantumToShort(
6311 GetPixelGreen(image,q)),q);
6312 SetPixelBlue(image,ScaleQuantumToShort(
6313 GetPixelBlue(image,q)),q);
6314 SetPixelAlpha(image,ScaleQuantumToShort(
6315 GetPixelAlpha(image,q)),q);
6316 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006317 }
glennrp47b9dd52010-11-24 18:12:06 +00006318
cristy3ed852e2009-09-05 21:47:34 +00006319 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6320 break;
6321 }
6322 }
6323#else
6324#define QM Quantum
6325#endif
6326
6327 if (image->matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006328 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006329
cristy3ed852e2009-09-05 21:47:34 +00006330 else
6331 {
cristy16ea1392012-03-21 20:38:41 +00006332 large_image->background_color.alpha=OpaqueAlpha;
6333 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006334
cristy3ed852e2009-09-05 21:47:34 +00006335 if (magn_methx == 4)
6336 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006337
cristy3ed852e2009-09-05 21:47:34 +00006338 if (magn_methx == 5)
6339 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006340
cristy3ed852e2009-09-05 21:47:34 +00006341 if (magn_methy == 4)
6342 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006343
cristy3ed852e2009-09-05 21:47:34 +00006344 if (magn_methy == 5)
6345 magn_methy=3;
6346 }
6347
6348 /* magnify the rows into the right side of the large image */
6349
6350 if (logging != MagickFalse)
6351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006352 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006353 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006354 yy=0;
cristy16ea1392012-03-21 20:38:41 +00006355 length=(size_t) image->columns*GetPixelChannels(image);
6356 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6357 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006358
cristy16ea1392012-03-21 20:38:41 +00006359 if ((prev == (Quantum *) NULL) ||
6360 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006361 {
6362 image=DestroyImageList(image);
6363 MngInfoFreeStruct(mng_info,&have_mng_structure);
6364 ThrowReaderException(ResourceLimitError,
6365 "MemoryAllocationFailed");
6366 }
glennrp47b9dd52010-11-24 18:12:06 +00006367
cristy3ed852e2009-09-05 21:47:34 +00006368 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6369 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006370
cristybb503372010-05-27 20:51:26 +00006371 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006372 {
6373 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006374 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006375
cristybb503372010-05-27 20:51:26 +00006376 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6377 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006378
cristybb503372010-05-27 20:51:26 +00006379 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6380 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006381
cristybb503372010-05-27 20:51:26 +00006382 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006383 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006384
cristy3ed852e2009-09-05 21:47:34 +00006385 else
cristybb503372010-05-27 20:51:26 +00006386 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006387
cristy3ed852e2009-09-05 21:47:34 +00006388 n=prev;
6389 prev=next;
6390 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006391
cristybb503372010-05-27 20:51:26 +00006392 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006393 {
6394 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6395 exception);
6396 (void) CopyMagickMemory(next,n,length);
6397 }
glennrp47b9dd52010-11-24 18:12:06 +00006398
cristy3ed852e2009-09-05 21:47:34 +00006399 for (i=0; i < m; i++, yy++)
6400 {
cristy16ea1392012-03-21 20:38:41 +00006401 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006402 *pixels;
6403
cristybb503372010-05-27 20:51:26 +00006404 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006405 pixels=prev;
6406 n=next;
6407 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006408 1,exception);
cristy16ea1392012-03-21 20:38:41 +00006409 q+=(large_image->columns-image->columns)*
6410 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006411
cristybb503372010-05-27 20:51:26 +00006412 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006413 {
glennrpfd05d622011-02-25 04:10:33 +00006414 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006415 /*
6416 if (image->storage_class == PseudoClass)
6417 {
6418 }
6419 */
6420
6421 if (magn_methy <= 1)
6422 {
glennrpbb4f99d2011-05-22 11:13:17 +00006423 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006424 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6425 SetPixelGreen(large_image,GetPixelGreen(image,
6426 pixels),q);
6427 SetPixelBlue(large_image,GetPixelBlue(image,
6428 pixels),q);
6429 SetPixelAlpha(large_image,GetPixelAlpha(image,
6430 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006431 }
glennrp47b9dd52010-11-24 18:12:06 +00006432
cristy3ed852e2009-09-05 21:47:34 +00006433 else if (magn_methy == 2 || magn_methy == 4)
6434 {
6435 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006436 {
cristy16ea1392012-03-21 20:38:41 +00006437 SetPixelRed(large_image,GetPixelRed(image,
6438 pixels),q);
6439 SetPixelGreen(large_image,GetPixelGreen(image,
6440 pixels),q);
6441 SetPixelBlue(large_image,GetPixelBlue(image,
6442 pixels),q);
6443 SetPixelAlpha(large_image,GetPixelAlpha(image,
6444 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006445 }
glennrp47b9dd52010-11-24 18:12:06 +00006446
cristy3ed852e2009-09-05 21:47:34 +00006447 else
6448 {
6449 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006450 SetPixelRed(large_image,((QM) (((ssize_t)
6451 (2*i*(GetPixelRed(image,n)
6452 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006453 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006454 +GetPixelRed(image,pixels)))),q);
6455 SetPixelGreen(large_image,((QM) (((ssize_t)
6456 (2*i*(GetPixelGreen(image,n)
6457 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006458 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006459 +GetPixelGreen(image,pixels)))),q);
6460 SetPixelBlue(large_image,((QM) (((ssize_t)
6461 (2*i*(GetPixelBlue(image,n)
6462 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006463 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006464 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006465
cristy3ed852e2009-09-05 21:47:34 +00006466 if (image->matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006467 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6468 (2*i*(GetPixelAlpha(image,n)
6469 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006470 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006471 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006472 }
glennrp47b9dd52010-11-24 18:12:06 +00006473
cristy3ed852e2009-09-05 21:47:34 +00006474 if (magn_methy == 4)
6475 {
6476 /* Replicate nearest */
6477 if (i <= ((m+1) << 1))
cristy16ea1392012-03-21 20:38:41 +00006478 SetPixelAlpha(large_image,GetPixelAlpha(image,
6479 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006480 else
cristy16ea1392012-03-21 20:38:41 +00006481 SetPixelAlpha(large_image,GetPixelAlpha(image,
6482 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006483 }
6484 }
glennrp47b9dd52010-11-24 18:12:06 +00006485
cristy3ed852e2009-09-05 21:47:34 +00006486 else /* if (magn_methy == 3 || magn_methy == 5) */
6487 {
6488 /* Replicate nearest */
6489 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006490 {
cristy16ea1392012-03-21 20:38:41 +00006491 SetPixelRed(large_image,GetPixelRed(image,
6492 pixels),q);
6493 SetPixelGreen(large_image,GetPixelGreen(image,
6494 pixels),q);
6495 SetPixelBlue(large_image,GetPixelBlue(image,
6496 pixels),q);
6497 SetPixelAlpha(large_image,GetPixelAlpha(image,
6498 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006499 }
glennrp47b9dd52010-11-24 18:12:06 +00006500
cristy3ed852e2009-09-05 21:47:34 +00006501 else
glennrpbb4f99d2011-05-22 11:13:17 +00006502 {
cristy16ea1392012-03-21 20:38:41 +00006503 SetPixelRed(large_image,GetPixelRed(image,n),q);
6504 SetPixelGreen(large_image,GetPixelGreen(image,n),
6505 q);
6506 SetPixelBlue(large_image,GetPixelBlue(image,n),
6507 q);
6508 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6509 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006510 }
glennrp47b9dd52010-11-24 18:12:06 +00006511
cristy3ed852e2009-09-05 21:47:34 +00006512 if (magn_methy == 5)
6513 {
cristy16ea1392012-03-21 20:38:41 +00006514 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6515 (GetPixelAlpha(image,n)
6516 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006517 +m))/((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006518 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006519 }
6520 }
cristy16ea1392012-03-21 20:38:41 +00006521 n+=GetPixelChannels(image);
6522 q+=GetPixelChannels(large_image);
6523 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006524 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006525
cristy3ed852e2009-09-05 21:47:34 +00006526 if (SyncAuthenticPixels(large_image,exception) == 0)
6527 break;
glennrp47b9dd52010-11-24 18:12:06 +00006528
cristy3ed852e2009-09-05 21:47:34 +00006529 } /* i */
6530 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006531
cristy16ea1392012-03-21 20:38:41 +00006532 prev=(Quantum *) RelinquishMagickMemory(prev);
6533 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006534
6535 length=image->columns;
6536
6537 if (logging != MagickFalse)
6538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6539 " Delete original image");
6540
6541 DeleteImageFromList(&image);
6542
6543 image=large_image;
6544
6545 mng_info->image=image;
6546
6547 /* magnify the columns */
6548 if (logging != MagickFalse)
6549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006550 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006551
cristybb503372010-05-27 20:51:26 +00006552 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006553 {
cristy16ea1392012-03-21 20:38:41 +00006554 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006555 *pixels;
6556
6557 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00006558 pixels=q+(image->columns-length)*GetPixelChannels(image);
6559 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006560
cristybb503372010-05-27 20:51:26 +00006561 for (x=(ssize_t) (image->columns-length);
6562 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006563 {
cristy16ea1392012-03-21 20:38:41 +00006564 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006565
cristybb503372010-05-27 20:51:26 +00006566 if (x == (ssize_t) (image->columns-length))
6567 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006568
cristybb503372010-05-27 20:51:26 +00006569 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6570 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006571
cristybb503372010-05-27 20:51:26 +00006572 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6573 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006574
cristybb503372010-05-27 20:51:26 +00006575 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006576 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006577
cristy3ed852e2009-09-05 21:47:34 +00006578 else
cristybb503372010-05-27 20:51:26 +00006579 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006580
cristy3ed852e2009-09-05 21:47:34 +00006581 for (i=0; i < m; i++)
6582 {
6583 if (magn_methx <= 1)
6584 {
6585 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006586 SetPixelRed(image,GetPixelRed(image,pixels),q);
6587 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6588 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6589 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006590 }
glennrp47b9dd52010-11-24 18:12:06 +00006591
cristy3ed852e2009-09-05 21:47:34 +00006592 else if (magn_methx == 2 || magn_methx == 4)
6593 {
6594 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006595 {
cristy16ea1392012-03-21 20:38:41 +00006596 SetPixelRed(image,GetPixelRed(image,pixels),q);
6597 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6598 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6599 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006600 }
glennrp47b9dd52010-11-24 18:12:06 +00006601
cristy16ea1392012-03-21 20:38:41 +00006602 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006603 else
6604 {
6605 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006606 SetPixelRed(image,(QM) ((2*i*(
6607 GetPixelRed(image,n)
6608 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006609 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006610 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006611
cristy16ea1392012-03-21 20:38:41 +00006612 SetPixelGreen(image,(QM) ((2*i*(
6613 GetPixelGreen(image,n)
6614 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006615 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006616 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006617
cristy16ea1392012-03-21 20:38:41 +00006618 SetPixelBlue(image,(QM) ((2*i*(
6619 GetPixelBlue(image,n)
6620 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006621 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006622 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006623 if (image->matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006624 SetPixelAlpha(image,(QM) ((2*i*(
6625 GetPixelAlpha(image,n)
6626 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006627 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006628 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006629 }
glennrp47b9dd52010-11-24 18:12:06 +00006630
cristy3ed852e2009-09-05 21:47:34 +00006631 if (magn_methx == 4)
6632 {
6633 /* Replicate nearest */
6634 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006635 {
cristy16ea1392012-03-21 20:38:41 +00006636 SetPixelAlpha(image,
6637 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006638 }
cristy3ed852e2009-09-05 21:47:34 +00006639 else
glennrpbb4f99d2011-05-22 11:13:17 +00006640 {
cristy16ea1392012-03-21 20:38:41 +00006641 SetPixelAlpha(image,
6642 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006643 }
cristy3ed852e2009-09-05 21:47:34 +00006644 }
6645 }
glennrp47b9dd52010-11-24 18:12:06 +00006646
cristy3ed852e2009-09-05 21:47:34 +00006647 else /* if (magn_methx == 3 || magn_methx == 5) */
6648 {
6649 /* Replicate nearest */
6650 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006651 {
cristy16ea1392012-03-21 20:38:41 +00006652 SetPixelRed(image,GetPixelRed(image,pixels),q);
6653 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6654 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6655 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006656 }
glennrp47b9dd52010-11-24 18:12:06 +00006657
cristy3ed852e2009-09-05 21:47:34 +00006658 else
glennrpbb4f99d2011-05-22 11:13:17 +00006659 {
cristy16ea1392012-03-21 20:38:41 +00006660 SetPixelRed(image,GetPixelRed(image,n),q);
6661 SetPixelGreen(image,GetPixelGreen(image,n),q);
6662 SetPixelBlue(image,GetPixelBlue(image,n),q);
6663 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006664 }
glennrp47b9dd52010-11-24 18:12:06 +00006665
cristy3ed852e2009-09-05 21:47:34 +00006666 if (magn_methx == 5)
6667 {
6668 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006669 SetPixelAlpha(image,
6670 (QM) ((2*i*( GetPixelAlpha(image,n)
6671 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006672 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006673 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006674 }
6675 }
cristy16ea1392012-03-21 20:38:41 +00006676 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006677 }
cristy16ea1392012-03-21 20:38:41 +00006678 n+=GetPixelChannels(image);
6679 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006680 }
glennrp47b9dd52010-11-24 18:12:06 +00006681
cristy3ed852e2009-09-05 21:47:34 +00006682 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6683 break;
6684 }
glennrp3faa9a32011-04-23 14:00:25 +00006685#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006686 if (magn_methx != 1 || magn_methy != 1)
6687 {
6688 /*
6689 Rescale pixels to Quantum
6690 */
cristybb503372010-05-27 20:51:26 +00006691 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006692 {
6693 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006694
cristybb503372010-05-27 20:51:26 +00006695 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006696 {
cristy16ea1392012-03-21 20:38:41 +00006697 SetPixelRed(image,ScaleShortToQuantum(
6698 GetPixelRed(image,q)),q);
6699 SetPixelGreen(image,ScaleShortToQuantum(
6700 GetPixelGreen(image,q)),q);
6701 SetPixelBlue(image,ScaleShortToQuantum(
6702 GetPixelBlue(image,q)),q);
6703 SetPixelAlpha(image,ScaleShortToQuantum(
6704 GetPixelAlpha(image,q)),q);
6705 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006706 }
glennrp47b9dd52010-11-24 18:12:06 +00006707
cristy3ed852e2009-09-05 21:47:34 +00006708 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6709 break;
6710 }
6711 }
6712#endif
6713 if (logging != MagickFalse)
6714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6715 " Finished MAGN processing");
6716 }
6717 }
6718
6719 /*
6720 Crop_box is with respect to the upper left corner of the MNG.
6721 */
6722 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6723 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6724 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6725 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6726 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6727 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6728 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6729 if ((crop_box.left != (mng_info->image_box.left
6730 +mng_info->x_off[object_id])) ||
6731 (crop_box.right != (mng_info->image_box.right
6732 +mng_info->x_off[object_id])) ||
6733 (crop_box.top != (mng_info->image_box.top
6734 +mng_info->y_off[object_id])) ||
6735 (crop_box.bottom != (mng_info->image_box.bottom
6736 +mng_info->y_off[object_id])))
6737 {
6738 if (logging != MagickFalse)
6739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6740 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006741
cristy3ed852e2009-09-05 21:47:34 +00006742 if ((crop_box.left < crop_box.right) &&
6743 (crop_box.top < crop_box.bottom))
6744 {
6745 Image
6746 *im;
6747
6748 RectangleInfo
6749 crop_info;
6750
6751 /*
6752 Crop_info is with respect to the upper left corner of
6753 the image.
6754 */
6755 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6756 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006757 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6758 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006759 image->page.width=image->columns;
6760 image->page.height=image->rows;
6761 image->page.x=0;
6762 image->page.y=0;
6763 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006764
cristy3ed852e2009-09-05 21:47:34 +00006765 if (im != (Image *) NULL)
6766 {
6767 image->columns=im->columns;
6768 image->rows=im->rows;
6769 im=DestroyImage(im);
6770 image->page.width=image->columns;
6771 image->page.height=image->rows;
6772 image->page.x=crop_box.left;
6773 image->page.y=crop_box.top;
6774 }
6775 }
glennrp47b9dd52010-11-24 18:12:06 +00006776
cristy3ed852e2009-09-05 21:47:34 +00006777 else
6778 {
6779 /*
6780 No pixels in crop area. The MNG spec still requires
6781 a layer, though, so make a single transparent pixel in
6782 the top left corner.
6783 */
6784 image->columns=1;
6785 image->rows=1;
6786 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00006787 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006788 image->page.width=1;
6789 image->page.height=1;
6790 image->page.x=0;
6791 image->page.y=0;
6792 }
6793 }
6794#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6795 image=mng_info->image;
6796#endif
6797 }
6798
glennrp2b013e42010-11-24 16:55:50 +00006799#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6800 /* PNG does not handle depths greater than 16 so reduce it even
cristy16ea1392012-03-21 20:38:41 +00006801 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00006802 */
6803 if (image->depth > 16)
6804 image->depth=16;
6805#endif
6806
glennrp3faa9a32011-04-23 14:00:25 +00006807#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00006808 if (image->depth > 8)
6809 {
6810 /* To do: fill low byte properly */
6811 image->depth=16;
6812 }
6813
cristy16ea1392012-03-21 20:38:41 +00006814 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006815 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006816#endif
glennrpd6afd542010-11-19 01:53:05 +00006817
cristy3ed852e2009-09-05 21:47:34 +00006818 if (image_info->number_scenes != 0)
6819 {
6820 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006821 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006822 break;
6823 }
glennrpd6afd542010-11-19 01:53:05 +00006824
cristy3ed852e2009-09-05 21:47:34 +00006825 if (logging != MagickFalse)
6826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6827 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006828
cristy3ed852e2009-09-05 21:47:34 +00006829 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006830
cristy3ed852e2009-09-05 21:47:34 +00006831 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006832
cristy3ed852e2009-09-05 21:47:34 +00006833 if (logging != MagickFalse)
6834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6835 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006836
cristy3ed852e2009-09-05 21:47:34 +00006837#if defined(MNG_INSERT_LAYERS)
6838 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6839 (mng_info->mng_height))
6840 {
6841 /*
6842 Insert a background layer if nothing else was found.
6843 */
6844 if (logging != MagickFalse)
6845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6846 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006847
cristy16ea1392012-03-21 20:38:41 +00006848 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006849 {
6850 /*
6851 Allocate next image structure.
6852 */
cristy16ea1392012-03-21 20:38:41 +00006853 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006854 if (GetNextImageInList(image) == (Image *) NULL)
6855 {
6856 image=DestroyImageList(image);
6857 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006858
cristy3ed852e2009-09-05 21:47:34 +00006859 if (logging != MagickFalse)
6860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6861 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006862
cristy3ed852e2009-09-05 21:47:34 +00006863 return((Image *) NULL);
6864 }
6865 image=SyncNextImageInList(image);
6866 }
6867 image->columns=mng_info->mng_width;
6868 image->rows=mng_info->mng_height;
6869 image->page.width=mng_info->mng_width;
6870 image->page.height=mng_info->mng_height;
6871 image->page.x=0;
6872 image->page.y=0;
6873 image->background_color=mng_background_color;
6874 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006875
cristy3ed852e2009-09-05 21:47:34 +00006876 if (image_info->ping == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006877 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006878
cristy3ed852e2009-09-05 21:47:34 +00006879 mng_info->image_found++;
6880 }
6881#endif
6882 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006883
cristy3ed852e2009-09-05 21:47:34 +00006884 if (mng_iterations == 1)
6885 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006886
cristy3ed852e2009-09-05 21:47:34 +00006887 while (GetPreviousImageInList(image) != (Image *) NULL)
6888 {
6889 image_count++;
6890 if (image_count > 10*mng_info->image_found)
6891 {
6892 if (logging != MagickFalse)
6893 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006894
cristy16ea1392012-03-21 20:38:41 +00006895 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006896 CoderError,"Linked list is corrupted, beginning of list not found",
6897 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006898
cristy3ed852e2009-09-05 21:47:34 +00006899 return((Image *) NULL);
6900 }
glennrp0fe50b42010-11-16 03:52:51 +00006901
cristy3ed852e2009-09-05 21:47:34 +00006902 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006903
cristy3ed852e2009-09-05 21:47:34 +00006904 if (GetNextImageInList(image) == (Image *) NULL)
6905 {
6906 if (logging != MagickFalse)
6907 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006908
cristy16ea1392012-03-21 20:38:41 +00006909 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006910 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6911 image_info->filename);
6912 }
6913 }
glennrp47b9dd52010-11-24 18:12:06 +00006914
cristy3ed852e2009-09-05 21:47:34 +00006915 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6916 GetNextImageInList(image) ==
6917 (Image *) NULL)
6918 {
6919 if (logging != MagickFalse)
6920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6921 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006922
cristy16ea1392012-03-21 20:38:41 +00006923 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006924 CoderError,"image->next for first image is NULL but shouldn't be.",
6925 "`%s'",image_info->filename);
6926 }
glennrp47b9dd52010-11-24 18:12:06 +00006927
cristy3ed852e2009-09-05 21:47:34 +00006928 if (mng_info->image_found == 0)
6929 {
6930 if (logging != MagickFalse)
6931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6932 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006933
cristy16ea1392012-03-21 20:38:41 +00006934 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006935 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006936
cristy3ed852e2009-09-05 21:47:34 +00006937 if (image != (Image *) NULL)
6938 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006939
cristy3ed852e2009-09-05 21:47:34 +00006940 MngInfoFreeStruct(mng_info,&have_mng_structure);
6941 return((Image *) NULL);
6942 }
6943
6944 if (mng_info->ticks_per_second)
6945 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6946 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006947
cristy3ed852e2009-09-05 21:47:34 +00006948 else
6949 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006950
cristy3ed852e2009-09-05 21:47:34 +00006951 /* Find final nonzero image delay */
6952 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006953
cristy3ed852e2009-09-05 21:47:34 +00006954 while (GetNextImageInList(image) != (Image *) NULL)
6955 {
6956 if (image->delay)
6957 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006958
cristy3ed852e2009-09-05 21:47:34 +00006959 image=GetNextImageInList(image);
6960 }
glennrp0fe50b42010-11-16 03:52:51 +00006961
cristy3ed852e2009-09-05 21:47:34 +00006962 if (final_delay < final_image_delay)
6963 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006964
cristy3ed852e2009-09-05 21:47:34 +00006965 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006966
cristy3ed852e2009-09-05 21:47:34 +00006967 if (logging != MagickFalse)
6968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006969 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6970 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006971
cristy3ed852e2009-09-05 21:47:34 +00006972 if (logging != MagickFalse)
6973 {
6974 int
6975 scene;
6976
6977 scene=0;
6978 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006979
cristy3ed852e2009-09-05 21:47:34 +00006980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6981 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006982
cristy3ed852e2009-09-05 21:47:34 +00006983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006984 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006985
cristy3ed852e2009-09-05 21:47:34 +00006986 while (GetNextImageInList(image) != (Image *) NULL)
6987 {
6988 image=GetNextImageInList(image);
6989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006990 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006991 }
6992 }
6993
6994 image=GetFirstImageInList(image);
6995#ifdef MNG_COALESCE_LAYERS
6996 if (insert_layers)
6997 {
6998 Image
6999 *next_image,
7000 *next;
7001
cristybb503372010-05-27 20:51:26 +00007002 size_t
cristy3ed852e2009-09-05 21:47:34 +00007003 scene;
7004
7005 if (logging != MagickFalse)
7006 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00007007
cristy3ed852e2009-09-05 21:47:34 +00007008 scene=image->scene;
cristy16ea1392012-03-21 20:38:41 +00007009 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00007010
cristy3ed852e2009-09-05 21:47:34 +00007011 if (next_image == (Image *) NULL)
7012 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00007013
cristy3ed852e2009-09-05 21:47:34 +00007014 image=DestroyImageList(image);
7015 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00007016
cristy3ed852e2009-09-05 21:47:34 +00007017 for (next=image; next != (Image *) NULL; next=next_image)
7018 {
7019 next->page.width=mng_info->mng_width;
7020 next->page.height=mng_info->mng_height;
7021 next->page.x=0;
7022 next->page.y=0;
7023 next->scene=scene++;
7024 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00007025
cristy3ed852e2009-09-05 21:47:34 +00007026 if (next_image == (Image *) NULL)
7027 break;
glennrp47b9dd52010-11-24 18:12:06 +00007028
cristy3ed852e2009-09-05 21:47:34 +00007029 if (next->delay == 0)
7030 {
7031 scene--;
7032 next_image->previous=GetPreviousImageInList(next);
7033 if (GetPreviousImageInList(next) == (Image *) NULL)
7034 image=next_image;
7035 else
7036 next->previous->next=next_image;
7037 next=DestroyImage(next);
7038 }
7039 }
7040 }
7041#endif
7042
7043 while (GetNextImageInList(image) != (Image *) NULL)
7044 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007045
cristy3ed852e2009-09-05 21:47:34 +00007046 image->dispose=BackgroundDispose;
7047
7048 if (logging != MagickFalse)
7049 {
7050 int
7051 scene;
7052
7053 scene=0;
7054 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007055
cristy3ed852e2009-09-05 21:47:34 +00007056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7057 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007058
cristy3ed852e2009-09-05 21:47:34 +00007059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007060 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7061 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007062
cristy3ed852e2009-09-05 21:47:34 +00007063 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007064 {
7065 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007066
cristyf2faecf2010-05-28 19:19:36 +00007067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007068 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7069 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007070 }
7071 }
glennrp47b9dd52010-11-24 18:12:06 +00007072
cristy3ed852e2009-09-05 21:47:34 +00007073 image=GetFirstImageInList(image);
7074 MngInfoFreeStruct(mng_info,&have_mng_structure);
7075 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007076
cristy3ed852e2009-09-05 21:47:34 +00007077 if (logging != MagickFalse)
7078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007079
cristy3ed852e2009-09-05 21:47:34 +00007080 return(GetFirstImageInList(image));
7081}
glennrp25c1e2b2010-03-25 01:39:56 +00007082#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007083static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7084{
7085 printf("Your PNG library is too old: You have libpng-%s\n",
7086 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007087
cristy3ed852e2009-09-05 21:47:34 +00007088 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7089 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007090
cristy3ed852e2009-09-05 21:47:34 +00007091 return(Image *) NULL;
7092}
glennrp47b9dd52010-11-24 18:12:06 +00007093
cristy3ed852e2009-09-05 21:47:34 +00007094static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7095{
7096 return(ReadPNGImage(image_info,exception));
7097}
glennrp25c1e2b2010-03-25 01:39:56 +00007098#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007099#endif
7100
7101/*
7102%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7103% %
7104% %
7105% %
7106% R e g i s t e r P N G I m a g e %
7107% %
7108% %
7109% %
7110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7111%
7112% RegisterPNGImage() adds properties for the PNG image format to
7113% the list of supported formats. The properties include the image format
7114% tag, a method to read and/or write the format, whether the format
7115% supports the saving of more than one frame to the same file or blob,
7116% whether the format supports native in-memory I/O, and a brief
7117% description of the format.
7118%
7119% The format of the RegisterPNGImage method is:
7120%
cristybb503372010-05-27 20:51:26 +00007121% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007122%
7123*/
cristybb503372010-05-27 20:51:26 +00007124ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007125{
7126 char
7127 version[MaxTextExtent];
7128
7129 MagickInfo
7130 *entry;
7131
7132 static const char
7133 *PNGNote=
7134 {
7135 "See http://www.libpng.org/ for details about the PNG format."
7136 },
glennrp47b9dd52010-11-24 18:12:06 +00007137
cristy3ed852e2009-09-05 21:47:34 +00007138 *JNGNote=
7139 {
7140 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7141 "format."
7142 },
glennrp47b9dd52010-11-24 18:12:06 +00007143
cristy3ed852e2009-09-05 21:47:34 +00007144 *MNGNote=
7145 {
7146 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7147 "format."
7148 };
7149
7150 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007151
cristy3ed852e2009-09-05 21:47:34 +00007152#if defined(PNG_LIBPNG_VER_STRING)
7153 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7154 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007155
cristy3ed852e2009-09-05 21:47:34 +00007156 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7157 {
7158 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7159 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7160 MaxTextExtent);
7161 }
7162#endif
glennrp47b9dd52010-11-24 18:12:06 +00007163
cristy3ed852e2009-09-05 21:47:34 +00007164 entry=SetMagickInfo("MNG");
7165 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007166
cristy3ed852e2009-09-05 21:47:34 +00007167#if defined(MAGICKCORE_PNG_DELEGATE)
7168 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7169 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7170#endif
glennrp47b9dd52010-11-24 18:12:06 +00007171
cristy3ed852e2009-09-05 21:47:34 +00007172 entry->magick=(IsImageFormatHandler *) IsMNG;
7173 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007174
cristy3ed852e2009-09-05 21:47:34 +00007175 if (*version != '\0')
7176 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007177
cristy3ed852e2009-09-05 21:47:34 +00007178 entry->module=ConstantString("PNG");
7179 entry->note=ConstantString(MNGNote);
7180 (void) RegisterMagickInfo(entry);
7181
7182 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007183
cristy3ed852e2009-09-05 21:47:34 +00007184#if defined(MAGICKCORE_PNG_DELEGATE)
7185 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7186 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7187#endif
glennrp47b9dd52010-11-24 18:12:06 +00007188
cristy3ed852e2009-09-05 21:47:34 +00007189 entry->magick=(IsImageFormatHandler *) IsPNG;
7190 entry->adjoin=MagickFalse;
7191 entry->description=ConstantString("Portable Network Graphics");
7192 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007193
cristy3ed852e2009-09-05 21:47:34 +00007194 if (*version != '\0')
7195 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007196
cristy3ed852e2009-09-05 21:47:34 +00007197 entry->note=ConstantString(PNGNote);
7198 (void) RegisterMagickInfo(entry);
7199
7200 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007201
cristy3ed852e2009-09-05 21:47:34 +00007202#if defined(MAGICKCORE_PNG_DELEGATE)
7203 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7204 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7205#endif
glennrp47b9dd52010-11-24 18:12:06 +00007206
cristy3ed852e2009-09-05 21:47:34 +00007207 entry->magick=(IsImageFormatHandler *) IsPNG;
7208 entry->adjoin=MagickFalse;
7209 entry->description=ConstantString(
7210 "8-bit indexed with optional binary transparency");
7211 entry->module=ConstantString("PNG");
7212 (void) RegisterMagickInfo(entry);
7213
7214 entry=SetMagickInfo("PNG24");
7215 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007216
cristy3ed852e2009-09-05 21:47:34 +00007217#if defined(ZLIB_VERSION)
7218 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7219 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007220
cristy3ed852e2009-09-05 21:47:34 +00007221 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7222 {
7223 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7224 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7225 }
7226#endif
glennrp47b9dd52010-11-24 18:12:06 +00007227
cristy3ed852e2009-09-05 21:47:34 +00007228 if (*version != '\0')
7229 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007230
cristy3ed852e2009-09-05 21:47:34 +00007231#if defined(MAGICKCORE_PNG_DELEGATE)
7232 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7233 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7234#endif
glennrp47b9dd52010-11-24 18:12:06 +00007235
cristy3ed852e2009-09-05 21:47:34 +00007236 entry->magick=(IsImageFormatHandler *) IsPNG;
7237 entry->adjoin=MagickFalse;
7238 entry->description=ConstantString("opaque 24-bit RGB");
7239 entry->module=ConstantString("PNG");
7240 (void) RegisterMagickInfo(entry);
7241
7242 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007243
cristy3ed852e2009-09-05 21:47:34 +00007244#if defined(MAGICKCORE_PNG_DELEGATE)
7245 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7246 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7247#endif
glennrp47b9dd52010-11-24 18:12:06 +00007248
cristy3ed852e2009-09-05 21:47:34 +00007249 entry->magick=(IsImageFormatHandler *) IsPNG;
7250 entry->adjoin=MagickFalse;
7251 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7252 entry->module=ConstantString("PNG");
7253 (void) RegisterMagickInfo(entry);
7254
7255 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007256
cristy3ed852e2009-09-05 21:47:34 +00007257#if defined(JNG_SUPPORTED)
7258#if defined(MAGICKCORE_PNG_DELEGATE)
7259 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7260 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7261#endif
7262#endif
glennrp47b9dd52010-11-24 18:12:06 +00007263
cristy3ed852e2009-09-05 21:47:34 +00007264 entry->magick=(IsImageFormatHandler *) IsJNG;
7265 entry->adjoin=MagickFalse;
7266 entry->description=ConstantString("JPEG Network Graphics");
7267 entry->module=ConstantString("PNG");
7268 entry->note=ConstantString(JNGNote);
7269 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007270
glennrpedaa0382012-04-12 14:16:21 +00007271#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007272 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007273#endif
glennrp47b9dd52010-11-24 18:12:06 +00007274
cristy3ed852e2009-09-05 21:47:34 +00007275 return(MagickImageCoderSignature);
7276}
7277
7278/*
7279%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7280% %
7281% %
7282% %
7283% U n r e g i s t e r P N G I m a g e %
7284% %
7285% %
7286% %
7287%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7288%
7289% UnregisterPNGImage() removes format registrations made by the
7290% PNG module from the list of supported formats.
7291%
7292% The format of the UnregisterPNGImage method is:
7293%
7294% UnregisterPNGImage(void)
7295%
7296*/
7297ModuleExport void UnregisterPNGImage(void)
7298{
7299 (void) UnregisterMagickInfo("MNG");
7300 (void) UnregisterMagickInfo("PNG");
7301 (void) UnregisterMagickInfo("PNG8");
7302 (void) UnregisterMagickInfo("PNG24");
7303 (void) UnregisterMagickInfo("PNG32");
7304 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007305
glennrpedaa0382012-04-12 14:16:21 +00007306#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007307 if (ping_semaphore != (SemaphoreInfo *) NULL)
7308 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007309#endif
7310}
7311
7312#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007313#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007314/*
7315%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7316% %
7317% %
7318% %
7319% W r i t e M N G I m a g e %
7320% %
7321% %
7322% %
7323%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7324%
7325% WriteMNGImage() writes an image in the Portable Network Graphics
7326% Group's "Multiple-image Network Graphics" encoded image format.
7327%
7328% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7329%
7330% The format of the WriteMNGImage method is:
7331%
cristy16ea1392012-03-21 20:38:41 +00007332% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7333% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007334%
7335% A description of each parameter follows.
7336%
7337% o image_info: the image info.
7338%
7339% o image: The image.
7340%
cristy16ea1392012-03-21 20:38:41 +00007341% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007342%
7343% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7344% "To do" under ReadPNGImage):
7345%
cristy3ed852e2009-09-05 21:47:34 +00007346% Preserve all unknown and not-yet-handled known chunks found in input
7347% PNG file and copy them into output PNG files according to the PNG
7348% copying rules.
7349%
7350% Write the iCCP chunk at MNG level when (icc profile length > 0)
7351%
7352% Improve selection of color type (use indexed-colour or indexed-colour
7353% with tRNS when 256 or fewer unique RGBA values are present).
7354%
7355% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7356% This will be complicated if we limit ourselves to generating MNG-LC
7357% files. For now we ignore disposal method 3 and simply overlay the next
7358% image on it.
7359%
7360% Check for identical PLTE's or PLTE/tRNS combinations and use a
7361% global MNG PLTE or PLTE/tRNS combination when appropriate.
7362% [mostly done 15 June 1999 but still need to take care of tRNS]
7363%
7364% Check for identical sRGB and replace with a global sRGB (and remove
7365% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7366% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7367% local gAMA/cHRM with local sRGB if appropriate).
7368%
7369% Check for identical sBIT chunks and write global ones.
7370%
7371% Provide option to skip writing the signature tEXt chunks.
7372%
7373% Use signatures to detect identical objects and reuse the first
7374% instance of such objects instead of writing duplicate objects.
7375%
7376% Use a smaller-than-32k value of compression window size when
7377% appropriate.
7378%
7379% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7380% ancillary text chunks and save profiles.
7381%
7382% Provide an option to force LC files (to ensure exact framing rate)
7383% instead of VLC.
7384%
7385% Provide an option to force VLC files instead of LC, even when offsets
7386% are present. This will involve expanding the embedded images with a
7387% transparent region at the top and/or left.
7388*/
7389
cristy3ed852e2009-09-05 21:47:34 +00007390static void
glennrpcf002022011-01-30 02:38:15 +00007391Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007392 png_info *ping_info, unsigned char *profile_type, unsigned char
7393 *profile_description, unsigned char *profile_data, png_uint_32 length)
7394{
cristy3ed852e2009-09-05 21:47:34 +00007395 png_textp
7396 text;
7397
cristybb503372010-05-27 20:51:26 +00007398 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007399 i;
7400
7401 unsigned char
7402 *sp;
7403
7404 png_charp
7405 dp;
7406
7407 png_uint_32
7408 allocated_length,
7409 description_length;
7410
7411 unsigned char
7412 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007413
cristy3ed852e2009-09-05 21:47:34 +00007414 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7415 return;
7416
7417 if (image_info->verbose)
7418 {
glennrp0fe50b42010-11-16 03:52:51 +00007419 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7420 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007421 }
glennrp0fe50b42010-11-16 03:52:51 +00007422
cristy3ed852e2009-09-05 21:47:34 +00007423 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7424 description_length=(png_uint_32) strlen((const char *) profile_description);
7425 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7426 + description_length);
7427 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7428 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7429 text[0].key[0]='\0';
7430 (void) ConcatenateMagickString(text[0].key,
7431 "Raw profile type ",MaxTextExtent);
7432 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7433 sp=profile_data;
7434 dp=text[0].text;
7435 *dp++='\n';
7436 (void) CopyMagickString(dp,(const char *) profile_description,
7437 allocated_length);
7438 dp+=description_length;
7439 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007440 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007441 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007442 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007443
cristybb503372010-05-27 20:51:26 +00007444 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007445 {
7446 if (i%36 == 0)
7447 *dp++='\n';
7448 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7449 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7450 }
glennrp47b9dd52010-11-24 18:12:06 +00007451
cristy3ed852e2009-09-05 21:47:34 +00007452 *dp++='\n';
7453 *dp='\0';
7454 text[0].text_length=(png_size_t) (dp-text[0].text);
7455 text[0].compression=image_info->compression == NoCompression ||
7456 (image_info->compression == UndefinedCompression &&
7457 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007458
cristy3ed852e2009-09-05 21:47:34 +00007459 if (text[0].text_length <= allocated_length)
7460 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007461
cristy3ed852e2009-09-05 21:47:34 +00007462 png_free(ping,text[0].text);
7463 png_free(ping,text[0].key);
7464 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007465}
7466
glennrpcf002022011-01-30 02:38:15 +00007467static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007468 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007469{
7470 char
7471 *name;
7472
7473 const StringInfo
7474 *profile;
7475
7476 unsigned char
7477 *data;
7478
7479 png_uint_32 length;
7480
7481 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007482
7483 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7484 {
cristy3ed852e2009-09-05 21:47:34 +00007485 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007486
cristy3ed852e2009-09-05 21:47:34 +00007487 if (profile != (const StringInfo *) NULL)
7488 {
7489 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007490 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007491
glennrp47b9dd52010-11-24 18:12:06 +00007492 if (LocaleNCompare(name,string,11) == 0)
7493 {
7494 if (logging != MagickFalse)
7495 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7496 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007497
glennrpcf002022011-01-30 02:38:15 +00007498 ping_profile=CloneStringInfo(profile);
7499 data=GetStringInfoDatum(ping_profile),
7500 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007501 data[4]=data[3];
7502 data[3]=data[2];
7503 data[2]=data[1];
7504 data[1]=data[0];
7505 (void) WriteBlobMSBULong(image,length-5); /* data length */
7506 (void) WriteBlob(image,length-1,data+1);
7507 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007508 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007509 }
cristy3ed852e2009-09-05 21:47:34 +00007510 }
glennrp47b9dd52010-11-24 18:12:06 +00007511
cristy3ed852e2009-09-05 21:47:34 +00007512 name=GetNextImageProfile(image);
7513 }
glennrp47b9dd52010-11-24 18:12:06 +00007514
cristy3ed852e2009-09-05 21:47:34 +00007515 return(MagickTrue);
7516}
7517
glennrpb9cfe272010-12-21 15:08:06 +00007518
cristy3ed852e2009-09-05 21:47:34 +00007519/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007520static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +00007521 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007522{
cristy16ea1392012-03-21 20:38:41 +00007523 Image
7524 *image;
7525
7526 ImageInfo
7527 *image_info;
7528
cristy3ed852e2009-09-05 21:47:34 +00007529 char
7530 s[2];
7531
7532 const char
7533 *name,
7534 *property,
7535 *value;
7536
7537 const StringInfo
7538 *profile;
7539
cristy3ed852e2009-09-05 21:47:34 +00007540 int
cristy3ed852e2009-09-05 21:47:34 +00007541 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007542 pass;
7543
glennrpe9c26dc2010-05-30 01:56:35 +00007544 png_byte
7545 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007546
glennrp39992b42010-11-14 00:03:43 +00007547 png_color
7548 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007549
glennrp5af765f2010-03-30 11:12:18 +00007550 png_color_16
7551 ping_background,
7552 ping_trans_color;
7553
cristy3ed852e2009-09-05 21:47:34 +00007554 png_info
7555 *ping_info;
7556
7557 png_struct
7558 *ping;
7559
glennrp5af765f2010-03-30 11:12:18 +00007560 png_uint_32
7561 ping_height,
7562 ping_width;
7563
cristybb503372010-05-27 20:51:26 +00007564 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007565 y;
7566
7567 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007568 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007569 logging,
glennrp58e01762011-01-07 15:28:54 +00007570 matte,
7571
glennrpda8f3a72011-02-27 23:54:12 +00007572 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007573 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007574 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007575 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007576 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007577 ping_have_bKGD,
7578 ping_have_pHYs,
7579 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007580
7581 ping_exclude_bKGD,
7582 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007583 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007584 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007585 ping_exclude_gAMA,
7586 ping_exclude_iCCP,
7587 /* ping_exclude_iTXt, */
7588 ping_exclude_oFFs,
7589 ping_exclude_pHYs,
7590 ping_exclude_sRGB,
7591 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007592 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007593 ping_exclude_vpAg,
7594 ping_exclude_zCCP, /* hex-encoded iCCP */
7595 ping_exclude_zTXt,
7596
glennrp8d3d6e52011-04-19 04:39:51 +00007597 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007598 ping_need_colortype_warning,
7599
glennrp82b3c532011-03-22 19:20:54 +00007600 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007601 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007602 tried_333,
7603 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007604
7605 QuantumInfo
7606 *quantum_info;
7607
cristy16ea1392012-03-21 20:38:41 +00007608 PNGErrorInfo
7609 error_info;
7610
cristybb503372010-05-27 20:51:26 +00007611 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007612 i,
7613 x;
7614
7615 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007616 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007617
glennrp5af765f2010-03-30 11:12:18 +00007618 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007619 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007620 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007621 ping_color_type,
7622 ping_interlace_method,
7623 ping_compression_method,
7624 ping_filter_method,
7625 ping_num_trans;
7626
cristybb503372010-05-27 20:51:26 +00007627 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007628 image_depth,
7629 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007630
cristybb503372010-05-27 20:51:26 +00007631 size_t
cristy3ed852e2009-09-05 21:47:34 +00007632 quality,
7633 rowbytes,
7634 save_image_depth;
7635
glennrpdfd70802010-11-14 01:23:35 +00007636 int
glennrpfd05d622011-02-25 04:10:33 +00007637 j,
glennrpf09bded2011-01-08 01:15:59 +00007638 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007639 number_opaque,
7640 number_semitransparent,
7641 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007642 ping_pHYs_unit_type;
7643
7644 png_uint_32
7645 ping_pHYs_x_resolution,
7646 ping_pHYs_y_resolution;
7647
cristy3ed852e2009-09-05 21:47:34 +00007648 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007649 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007650
cristy16ea1392012-03-21 20:38:41 +00007651 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7652 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7653 if (image_info == (ImageInfo *) NULL)
7654 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007655
glennrp5af765f2010-03-30 11:12:18 +00007656 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007657 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007658 ping_color_type=0,
7659 ping_interlace_method=0,
7660 ping_compression_method=0,
7661 ping_filter_method=0,
7662 ping_num_trans = 0;
7663
7664 ping_background.red = 0;
7665 ping_background.green = 0;
7666 ping_background.blue = 0;
7667 ping_background.gray = 0;
7668 ping_background.index = 0;
7669
7670 ping_trans_color.red=0;
7671 ping_trans_color.green=0;
7672 ping_trans_color.blue=0;
7673 ping_trans_color.gray=0;
7674
glennrpdfd70802010-11-14 01:23:35 +00007675 ping_pHYs_unit_type = 0;
7676 ping_pHYs_x_resolution = 0;
7677 ping_pHYs_y_resolution = 0;
7678
glennrpda8f3a72011-02-27 23:54:12 +00007679 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007680 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007681 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007682 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007683 ping_have_bKGD=MagickFalse;
7684 ping_have_pHYs=MagickFalse;
7685 ping_have_tRNS=MagickFalse;
7686
glennrp0e8ea192010-12-24 18:00:33 +00007687 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7688 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007689 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007690 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007691 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007692 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7693 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7694 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7695 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7696 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7697 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007698 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007699 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7700 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7701 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7702
glennrp8d3d6e52011-04-19 04:39:51 +00007703 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007704 ping_need_colortype_warning = MagickFalse;
7705
cristy0d57eec2011-09-04 22:13:56 +00007706 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7707 * i.e., eliminate the ICC profile and set image->rendering_intent.
7708 * Note that this will not involve any changes to the actual pixels
7709 * but merely passes information to applications that read the resulting
7710 * PNG image.
7711 */
7712 if (ping_exclude_sRGB == MagickFalse)
7713 {
7714 char
7715 *name;
7716
7717 const StringInfo
7718 *profile;
7719
7720 ResetImageProfileIterator(image);
7721 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7722 {
7723 profile=GetImageProfile(image,name);
7724
7725 if (profile != (StringInfo *) NULL)
7726 {
7727 if ((LocaleCompare(name,"ICC") == 0) ||
cristy16ea1392012-03-21 20:38:41 +00007728 (LocaleCompare(name,"ICM") == 0))
7729 {
glennrpee7b4c02011-10-04 01:21:09 +00007730 int
7731 icheck;
7732
7733 /* 0: not a known sRGB profile
7734 * 1: HP-Microsoft sRGB v2
7735 * 2: ICC sRGB v4 perceptual
7736 * 3: ICC sRGB v2 perceptual no black-compensation
7737 */
7738 png_uint_32
7739 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7740 check_len[4] = {0, 3144, 60960, 3052};
7741
7742 png_uint_32
7743 length,
7744 profile_crc;
7745
cristy0d57eec2011-09-04 22:13:56 +00007746 unsigned char
7747 *data;
7748
glennrp29a106e2011-09-06 17:11:42 +00007749 length=(png_uint_32) GetStringInfoLength(profile);
7750
glennrpee7b4c02011-10-04 01:21:09 +00007751 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007752 {
glennrpee7b4c02011-10-04 01:21:09 +00007753 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007754 {
glennrpee7b4c02011-10-04 01:21:09 +00007755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7756 " Got a %lu-byte ICC profile (potentially sRGB)",
7757 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007758
glennrpee7b4c02011-10-04 01:21:09 +00007759 data=GetStringInfoDatum(profile);
7760 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007761
glennrpee7b4c02011-10-04 01:21:09 +00007762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007763 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007764
7765 if (profile_crc == check_crc[icheck])
7766 {
7767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7768 " It is sRGB.");
7769 if (image->rendering_intent==UndefinedIntent)
7770 image->rendering_intent=PerceptualIntent;
7771 break;
7772 }
glennrp29a106e2011-09-06 17:11:42 +00007773 }
glennrp29a106e2011-09-06 17:11:42 +00007774 }
glennrpee7b4c02011-10-04 01:21:09 +00007775 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007777 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007778 (unsigned long) length);
7779 }
cristy0d57eec2011-09-04 22:13:56 +00007780 }
7781 name=GetNextImageProfile(image);
7782 }
7783 }
7784
glennrp8bb3a022010-12-13 20:40:04 +00007785 number_opaque = 0;
7786 number_semitransparent = 0;
7787 number_transparent = 0;
7788
glennrpfd05d622011-02-25 04:10:33 +00007789 if (logging != MagickFalse)
7790 {
7791 if (image->storage_class == UndefinedClass)
7792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7793 " storage_class=UndefinedClass");
7794 if (image->storage_class == DirectClass)
7795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7796 " storage_class=DirectClass");
7797 if (image->storage_class == PseudoClass)
7798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7799 " storage_class=PseudoClass");
7800 }
glennrp28af3712011-04-06 18:07:30 +00007801
glennrp750105b2012-04-25 16:20:45 +00007802 if (image->storage_class == PseudoClass &&
glennrp7e65e932011-08-19 02:31:16 +00007803 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7804 (mng_info->write_png_colortype != 0 &&
7805 mng_info->write_png_colortype != 4)))
7806 {
cristy16ea1392012-03-21 20:38:41 +00007807 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007808 image->storage_class = DirectClass;
7809 }
7810
glennrpc6c391a2011-04-27 02:23:56 +00007811 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007812 {
glennrpc6c391a2011-04-27 02:23:56 +00007813 if (image->storage_class != PseudoClass && image->colormap != NULL)
7814 {
7815 /* Free the bogus colormap; it can cause trouble later */
7816 if (logging != MagickFalse)
7817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7818 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007819 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007820 image->colormap=NULL;
7821 }
glennrp28af3712011-04-06 18:07:30 +00007822 }
glennrpbb4f99d2011-05-22 11:13:17 +00007823
cristyef6a54d2012-06-17 17:01:44 +00007824 if ((IssRGBColorspace(image->colorspace) == MagickFalse) &&
7825 (IsImageGray(image,exception) == MagickFalse))
cristy16ea1392012-03-21 20:38:41 +00007826 (void) TransformImageColorspace(image,sRGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007827
glennrp3241bd02010-12-12 04:36:28 +00007828 /*
7829 Sometimes we get PseudoClass images whose RGB values don't match
7830 the colors in the colormap. This code syncs the RGB values.
7831 */
7832 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristy16ea1392012-03-21 20:38:41 +00007833 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007834
glennrpa6a06632011-01-19 15:15:34 +00007835#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7836 if (image->depth > 8)
7837 {
7838 if (logging != MagickFalse)
7839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7840 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7841
7842 image->depth=8;
7843 }
7844#endif
7845
glennrp8e58efd2011-05-20 12:16:29 +00007846 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007847 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7848 {
cristy16ea1392012-03-21 20:38:41 +00007849 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007850 *r;
7851
glennrp8e58efd2011-05-20 12:16:29 +00007852 if (image->depth > 8)
7853 {
7854#if MAGICKCORE_QUANTUM_DEPTH > 16
7855 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007856 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007857
7858 for (y=0; y < (ssize_t) image->rows; y++)
7859 {
cristy16ea1392012-03-21 20:38:41 +00007860 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007861
cristy16ea1392012-03-21 20:38:41 +00007862 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007863 break;
7864
7865 for (x=0; x < (ssize_t) image->columns; x++)
7866 {
cristy16ea1392012-03-21 20:38:41 +00007867 LBR16PixelRGBA(r);
7868 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007869 }
glennrpbb4f99d2011-05-22 11:13:17 +00007870
glennrp8e58efd2011-05-20 12:16:29 +00007871 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7872 break;
7873 }
7874
7875 if (image->storage_class == PseudoClass && image->colormap != NULL)
7876 {
cristy3e08f112011-05-24 13:19:30 +00007877 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007878 {
glennrp91d99252011-06-25 14:30:13 +00007879 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007880 }
7881 }
7882#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7883 }
7884
7885 else if (image->depth > 4)
7886 {
7887#if MAGICKCORE_QUANTUM_DEPTH > 8
7888 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007889 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007890
7891 for (y=0; y < (ssize_t) image->rows; y++)
7892 {
cristy16ea1392012-03-21 20:38:41 +00007893 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007894
cristy16ea1392012-03-21 20:38:41 +00007895 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007896 break;
7897
7898 for (x=0; x < (ssize_t) image->columns; x++)
7899 {
cristy16ea1392012-03-21 20:38:41 +00007900 LBR08PixelRGBA(r);
7901 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007902 }
glennrpbb4f99d2011-05-22 11:13:17 +00007903
glennrp8e58efd2011-05-20 12:16:29 +00007904 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7905 break;
7906 }
7907
7908 if (image->storage_class == PseudoClass && image->colormap != NULL)
7909 {
cristy3e08f112011-05-24 13:19:30 +00007910 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007911 {
glennrp91d99252011-06-25 14:30:13 +00007912 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007913 }
7914 }
7915#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7916 }
7917 else
7918 if (image->depth > 2)
7919 {
7920 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007921 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007922
7923 for (y=0; y < (ssize_t) image->rows; y++)
7924 {
cristy16ea1392012-03-21 20:38:41 +00007925 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007926
cristy16ea1392012-03-21 20:38:41 +00007927 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007928 break;
7929
7930 for (x=0; x < (ssize_t) image->columns; x++)
7931 {
cristy16ea1392012-03-21 20:38:41 +00007932 LBR04PixelRGBA(r);
7933 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007934 }
glennrpbb4f99d2011-05-22 11:13:17 +00007935
glennrp8e58efd2011-05-20 12:16:29 +00007936 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7937 break;
7938 }
7939
7940 if (image->storage_class == PseudoClass && image->colormap != NULL)
7941 {
cristy3e08f112011-05-24 13:19:30 +00007942 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007943 {
glennrp91d99252011-06-25 14:30:13 +00007944 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007945 }
7946 }
7947 }
7948
7949 else if (image->depth > 1)
7950 {
7951 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007952 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007953
7954 for (y=0; y < (ssize_t) image->rows; y++)
7955 {
cristy16ea1392012-03-21 20:38:41 +00007956 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007957
cristy16ea1392012-03-21 20:38:41 +00007958 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007959 break;
7960
7961 for (x=0; x < (ssize_t) image->columns; x++)
7962 {
cristy16ea1392012-03-21 20:38:41 +00007963 LBR02PixelRGBA(r);
7964 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007965 }
glennrpbb4f99d2011-05-22 11:13:17 +00007966
glennrp8e58efd2011-05-20 12:16:29 +00007967 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7968 break;
7969 }
7970
7971 if (image->storage_class == PseudoClass && image->colormap != NULL)
7972 {
cristy3e08f112011-05-24 13:19:30 +00007973 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007974 {
glennrp91d99252011-06-25 14:30:13 +00007975 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007976 }
7977 }
7978 }
7979 else
7980 {
7981 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007982 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007983
7984 for (y=0; y < (ssize_t) image->rows; y++)
7985 {
cristy16ea1392012-03-21 20:38:41 +00007986 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007987
cristy16ea1392012-03-21 20:38:41 +00007988 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007989 break;
7990
7991 for (x=0; x < (ssize_t) image->columns; x++)
7992 {
cristy16ea1392012-03-21 20:38:41 +00007993 LBR01PixelRGBA(r);
7994 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007995 }
glennrpbb4f99d2011-05-22 11:13:17 +00007996
glennrp8e58efd2011-05-20 12:16:29 +00007997 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7998 break;
7999 }
8000
8001 if (image->storage_class == PseudoClass && image->colormap != NULL)
8002 {
cristy3e08f112011-05-24 13:19:30 +00008003 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008004 {
glennrp91d99252011-06-25 14:30:13 +00008005 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008006 }
8007 }
8008 }
glennrp9d0ea4d2011-04-22 18:35:57 +00008009 }
8010
glennrp67b9c1a2011-04-22 18:47:36 +00008011 /* To do: set to next higher multiple of 8 */
8012 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00008013 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00008014
glennrp2b013e42010-11-24 16:55:50 +00008015#if (MAGICKCORE_QUANTUM_DEPTH > 16)
8016 /* PNG does not handle depths greater than 16 so reduce it even
8017 * if lossy
8018 */
glennrp8e58efd2011-05-20 12:16:29 +00008019 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00008020 image->depth=16;
8021#endif
8022
glennrp3faa9a32011-04-23 14:00:25 +00008023#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00008024 if (image->depth > 8)
8025 {
8026 /* To do: fill low byte properly */
8027 image->depth=16;
8028 }
8029
glennrpc722dd82011-02-24 05:13:21 +00008030 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristy16ea1392012-03-21 20:38:41 +00008031 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00008032 image->depth = 8;
8033#endif
8034
glennrpc8c2f062011-02-25 19:00:33 +00008035 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00008036 * we reduce the transparency to binary and run again, then if there
8037 * 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 +00008038 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8039 * palette. Then (To do) we take care of a final reduction that is only
8040 * needed if there are still 256 colors present and one of them has both
8041 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00008042 */
glennrp82b3c532011-03-22 19:20:54 +00008043
glennrp8ca51ad2011-05-12 21:22:32 +00008044 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008045 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00008046 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008047
glennrp8ca51ad2011-05-12 21:22:32 +00008048 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00008049 {
8050 /* BUILD_PALETTE
8051 *
8052 * Sometimes we get DirectClass images that have 256 colors or fewer.
8053 * This code will build a colormap.
8054 *
8055 * Also, sometimes we get PseudoClass images with an out-of-date
8056 * colormap. This code will replace the colormap with a new one.
8057 * Sometimes we get PseudoClass images that have more than 256 colors.
8058 * This code will delete the colormap and change the image to
8059 * DirectClass.
8060 *
cristy16ea1392012-03-21 20:38:41 +00008061 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008062 * even though it sometimes contains left-over non-opaque values.
8063 *
8064 * Also we gather some information (number of opaque, transparent,
8065 * and semitransparent pixels, and whether the image has any non-gray
8066 * pixels or only black-and-white pixels) that we might need later.
8067 *
8068 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8069 * we need to check for bogus non-opaque values, at least.
8070 */
glennrp3c218112010-11-27 15:31:26 +00008071
glennrpd71e86a2011-02-24 01:28:37 +00008072 int
8073 n;
glennrp3c218112010-11-27 15:31:26 +00008074
cristy16ea1392012-03-21 20:38:41 +00008075 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008076 opaque[260],
8077 semitransparent[260],
8078 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008079
cristy16ea1392012-03-21 20:38:41 +00008080 register const Quantum
8081 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008082
cristy16ea1392012-03-21 20:38:41 +00008083 register Quantum
8084 *q,
glennrpfd05d622011-02-25 04:10:33 +00008085 *r;
8086
glennrpd71e86a2011-02-24 01:28:37 +00008087 if (logging != MagickFalse)
8088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8089 " Enter BUILD_PALETTE:");
8090
8091 if (logging != MagickFalse)
8092 {
glennrp03812ae2010-12-24 01:31:34 +00008093 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008094 " image->columns=%.20g",(double) image->columns);
8095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8096 " image->rows=%.20g",(double) image->rows);
8097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8098 " image->matte=%.20g",(double) image->matte);
8099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8100 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008101
glennrpfd05d622011-02-25 04:10:33 +00008102 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008103 {
8104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008105 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008107 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008108
glennrpd71e86a2011-02-24 01:28:37 +00008109 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008110 {
glennrpd71e86a2011-02-24 01:28:37 +00008111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8112 " %d (%d,%d,%d,%d)",
8113 (int) i,
8114 (int) image->colormap[i].red,
8115 (int) image->colormap[i].green,
8116 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008117 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008118 }
glennrp2cc891a2010-12-24 13:44:32 +00008119
glennrpd71e86a2011-02-24 01:28:37 +00008120 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8121 {
8122 if (i > 255)
8123 {
8124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8125 " %d (%d,%d,%d,%d)",
8126 (int) i,
8127 (int) image->colormap[i].red,
8128 (int) image->colormap[i].green,
8129 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008130 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008131 }
8132 }
glennrp03812ae2010-12-24 01:31:34 +00008133 }
glennrp7ddcc222010-12-11 05:01:05 +00008134
glennrpd71e86a2011-02-24 01:28:37 +00008135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8136 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008137
glennrpd71e86a2011-02-24 01:28:37 +00008138 if (image->colors == 0)
cristy16ea1392012-03-21 20:38:41 +00008139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8140 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008141
glennrp8d3d6e52011-04-19 04:39:51 +00008142 if (ping_preserve_colormap == MagickFalse)
8143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8144 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008145 }
8146
glennrpd71e86a2011-02-24 01:28:37 +00008147 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008148 number_opaque = 0;
8149 number_semitransparent = 0;
8150 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008151
8152 for (y=0; y < (ssize_t) image->rows; y++)
8153 {
8154 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8155
cristy16ea1392012-03-21 20:38:41 +00008156 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008157 break;
8158
8159 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008160 {
glennrp4737d522011-04-29 03:33:42 +00008161 if (image->matte == MagickFalse ||
cristy16ea1392012-03-21 20:38:41 +00008162 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008163 {
8164 if (number_opaque < 259)
8165 {
8166 if (number_opaque == 0)
8167 {
cristy16ea1392012-03-21 20:38:41 +00008168 GetPixelInfoPixel(image, q, opaque);
8169 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008170 number_opaque=1;
8171 }
glennrp2cc891a2010-12-24 13:44:32 +00008172
glennrpd71e86a2011-02-24 01:28:37 +00008173 for (i=0; i< (ssize_t) number_opaque; i++)
8174 {
cristy16ea1392012-03-21 20:38:41 +00008175 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008176 break;
8177 }
glennrp7ddcc222010-12-11 05:01:05 +00008178
cristy16ea1392012-03-21 20:38:41 +00008179 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008180 {
8181 number_opaque++;
cristy16ea1392012-03-21 20:38:41 +00008182 GetPixelInfoPixel(image, q, opaque+i);
8183 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008184 }
8185 }
8186 }
cristy16ea1392012-03-21 20:38:41 +00008187 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008188 {
8189 if (number_transparent < 259)
8190 {
8191 if (number_transparent == 0)
8192 {
cristy16ea1392012-03-21 20:38:41 +00008193 GetPixelInfoPixel(image, q, transparent);
8194 ping_trans_color.red=(unsigned short)
8195 GetPixelRed(image,q);
8196 ping_trans_color.green=(unsigned short)
8197 GetPixelGreen(image,q);
8198 ping_trans_color.blue=(unsigned short)
8199 GetPixelBlue(image,q);
8200 ping_trans_color.gray=(unsigned short)
8201 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008202 number_transparent = 1;
8203 }
8204
8205 for (i=0; i< (ssize_t) number_transparent; i++)
8206 {
cristy16ea1392012-03-21 20:38:41 +00008207 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008208 break;
8209 }
8210
8211 if (i == (ssize_t) number_transparent &&
8212 number_transparent < 259)
8213 {
8214 number_transparent++;
cristy16ea1392012-03-21 20:38:41 +00008215 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008216 }
8217 }
8218 }
8219 else
8220 {
8221 if (number_semitransparent < 259)
8222 {
8223 if (number_semitransparent == 0)
8224 {
cristy16ea1392012-03-21 20:38:41 +00008225 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008226 number_semitransparent = 1;
8227 }
8228
8229 for (i=0; i< (ssize_t) number_semitransparent; i++)
8230 {
cristy16ea1392012-03-21 20:38:41 +00008231 if (IsPixelEquivalent(image,q, semitransparent+i)
8232 && GetPixelAlpha(image,q) ==
8233 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008234 break;
8235 }
8236
8237 if (i == (ssize_t) number_semitransparent &&
8238 number_semitransparent < 259)
8239 {
8240 number_semitransparent++;
cristy16ea1392012-03-21 20:38:41 +00008241 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008242 }
8243 }
8244 }
cristy16ea1392012-03-21 20:38:41 +00008245 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008246 }
8247 }
8248
cristy4054bfb2011-08-29 23:41:39 +00008249 if (mng_info->write_png8 == MagickFalse &&
8250 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008251 {
8252 /* Add the background color to the palette, if it
8253 * isn't already there.
8254 */
glennrpc6c391a2011-04-27 02:23:56 +00008255 if (logging != MagickFalse)
8256 {
8257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8258 " Check colormap for background (%d,%d,%d)",
8259 (int) image->background_color.red,
8260 (int) image->background_color.green,
8261 (int) image->background_color.blue);
8262 }
glennrpd71e86a2011-02-24 01:28:37 +00008263 for (i=0; i<number_opaque; i++)
8264 {
glennrpca7ad3a2011-04-26 04:44:54 +00008265 if (opaque[i].red == image->background_color.red &&
8266 opaque[i].green == image->background_color.green &&
8267 opaque[i].blue == image->background_color.blue)
8268 break;
glennrpd71e86a2011-02-24 01:28:37 +00008269 }
glennrpd71e86a2011-02-24 01:28:37 +00008270 if (number_opaque < 259 && i == number_opaque)
8271 {
glennrp8e045c82011-04-27 16:40:27 +00008272 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008273 ping_background.index = i;
8274 if (logging != MagickFalse)
8275 {
8276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8277 " background_color index is %d",(int) i);
8278 }
8279
glennrpd71e86a2011-02-24 01:28:37 +00008280 }
glennrpa080bc32011-03-11 18:03:44 +00008281 else if (logging != MagickFalse)
8282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8283 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008284 }
8285
8286 image_colors=number_opaque+number_transparent+number_semitransparent;
8287
glennrpa080bc32011-03-11 18:03:44 +00008288 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8289 {
8290 /* No room for the background color; remove it. */
8291 number_opaque--;
8292 image_colors--;
8293 }
8294
glennrpd71e86a2011-02-24 01:28:37 +00008295 if (logging != MagickFalse)
8296 {
8297 if (image_colors > 256)
8298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8299 " image has more than 256 colors");
8300
8301 else
8302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8303 " image has %d colors",image_colors);
8304 }
8305
glennrp8d3d6e52011-04-19 04:39:51 +00008306 if (ping_preserve_colormap != MagickFalse)
8307 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008308
glennrpfd05d622011-02-25 04:10:33 +00008309 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008310 {
8311 ping_have_color=MagickFalse;
8312 ping_have_non_bw=MagickFalse;
8313
8314 if(image_colors > 256)
8315 {
8316 for (y=0; y < (ssize_t) image->rows; y++)
8317 {
8318 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8319
cristy16ea1392012-03-21 20:38:41 +00008320 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008321 break;
8322
glennrpe5e6b802011-07-20 14:44:40 +00008323 s=q;
8324 for (x=0; x < (ssize_t) image->columns; x++)
8325 {
cristy16ea1392012-03-21 20:38:41 +00008326 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8327 GetPixelRed(image,s) != GetPixelBlue(image,s))
glennrpe5e6b802011-07-20 14:44:40 +00008328 {
8329 ping_have_color=MagickTrue;
8330 ping_have_non_bw=MagickTrue;
8331 break;
8332 }
cristy16ea1392012-03-21 20:38:41 +00008333 s+=GetPixelChannels(image);
glennrpe5e6b802011-07-20 14:44:40 +00008334 }
8335
8336 if (ping_have_color != MagickFalse)
8337 break;
8338
glennrpd71e86a2011-02-24 01:28:37 +00008339 /* Worst case is black-and-white; we are looking at every
8340 * pixel twice.
8341 */
8342
glennrpd71e86a2011-02-24 01:28:37 +00008343 if (ping_have_non_bw == MagickFalse)
8344 {
8345 s=q;
8346 for (x=0; x < (ssize_t) image->columns; x++)
8347 {
cristy16ea1392012-03-21 20:38:41 +00008348 if (GetPixelRed(image,s) != 0 &&
8349 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008350 {
8351 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008352 break;
glennrpd71e86a2011-02-24 01:28:37 +00008353 }
cristy16ea1392012-03-21 20:38:41 +00008354 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008355 }
glennrpe5e6b802011-07-20 14:44:40 +00008356 }
glennrpd71e86a2011-02-24 01:28:37 +00008357 }
glennrpbb4f99d2011-05-22 11:13:17 +00008358 }
8359 }
glennrpd71e86a2011-02-24 01:28:37 +00008360
8361 if (image_colors < 257)
8362 {
cristy16ea1392012-03-21 20:38:41 +00008363 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008364 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008365
glennrpd71e86a2011-02-24 01:28:37 +00008366 /*
8367 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008368 */
8369
glennrpd71e86a2011-02-24 01:28:37 +00008370 if (logging != MagickFalse)
8371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8372 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008373
glennrpd71e86a2011-02-24 01:28:37 +00008374 /* Sort palette, transparent first */;
8375
8376 n = 0;
8377
8378 for (i=0; i<number_transparent; i++)
8379 colormap[n++] = transparent[i];
8380
8381 for (i=0; i<number_semitransparent; i++)
8382 colormap[n++] = semitransparent[i];
8383
8384 for (i=0; i<number_opaque; i++)
8385 colormap[n++] = opaque[i];
8386
glennrpc6c391a2011-04-27 02:23:56 +00008387 ping_background.index +=
8388 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008389
glennrpd71e86a2011-02-24 01:28:37 +00008390 /* image_colors < 257; search the colormap instead of the pixels
8391 * to get ping_have_color and ping_have_non_bw
8392 */
8393 for (i=0; i<n; i++)
8394 {
8395 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008396 {
glennrpd71e86a2011-02-24 01:28:37 +00008397 if (colormap[i].red != colormap[i].green ||
8398 colormap[i].red != colormap[i].blue)
8399 {
8400 ping_have_color=MagickTrue;
8401 ping_have_non_bw=MagickTrue;
8402 break;
8403 }
8404 }
8405
8406 if (ping_have_non_bw == MagickFalse)
8407 {
8408 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008409 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008410 }
glennrp8bb3a022010-12-13 20:40:04 +00008411 }
8412
glennrpd71e86a2011-02-24 01:28:37 +00008413 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8414 (number_transparent == 0 && number_semitransparent == 0)) &&
8415 (((mng_info->write_png_colortype-1) ==
8416 PNG_COLOR_TYPE_PALETTE) ||
8417 (mng_info->write_png_colortype == 0)))
8418 {
glennrp6185c532011-01-14 17:58:40 +00008419 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008420 {
glennrpd71e86a2011-02-24 01:28:37 +00008421 if (n != (ssize_t) image_colors)
8422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8423 " image_colors (%d) and n (%d) don't match",
8424 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008425
glennrpd71e86a2011-02-24 01:28:37 +00008426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8427 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008428 }
glennrp03812ae2010-12-24 01:31:34 +00008429
glennrpd71e86a2011-02-24 01:28:37 +00008430 image->colors = image_colors;
8431
cristy16ea1392012-03-21 20:38:41 +00008432 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008433 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008434 ThrowWriterException(ResourceLimitError,
8435 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008436
8437 for (i=0; i< (ssize_t) image_colors; i++)
8438 image->colormap[i] = colormap[i];
8439
8440 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008441 {
glennrpd71e86a2011-02-24 01:28:37 +00008442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8443 " image->colors=%d (%d)",
8444 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008445
glennrpd71e86a2011-02-24 01:28:37 +00008446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " Update the pixel indexes");
8448 }
glennrp6185c532011-01-14 17:58:40 +00008449
glennrpfd05d622011-02-25 04:10:33 +00008450 /* Sync the pixel indices with the new colormap */
8451
glennrpd71e86a2011-02-24 01:28:37 +00008452 for (y=0; y < (ssize_t) image->rows; y++)
8453 {
cristy16ea1392012-03-21 20:38:41 +00008454 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008455
cristy16ea1392012-03-21 20:38:41 +00008456 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008457 break;
glennrp6185c532011-01-14 17:58:40 +00008458
glennrpd71e86a2011-02-24 01:28:37 +00008459 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008460 {
glennrpd71e86a2011-02-24 01:28:37 +00008461 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008462 {
glennrpd71e86a2011-02-24 01:28:37 +00008463 if ((image->matte == MagickFalse ||
cristy16ea1392012-03-21 20:38:41 +00008464 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8465 image->colormap[i].red == GetPixelRed(image,q) &&
8466 image->colormap[i].green == GetPixelGreen(image,q) &&
8467 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008468 {
cristy16ea1392012-03-21 20:38:41 +00008469 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008470 break;
glennrp6185c532011-01-14 17:58:40 +00008471 }
glennrp6185c532011-01-14 17:58:40 +00008472 }
cristy16ea1392012-03-21 20:38:41 +00008473 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008474 }
glennrp6185c532011-01-14 17:58:40 +00008475
glennrpd71e86a2011-02-24 01:28:37 +00008476 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8477 break;
8478 }
8479 }
8480 }
8481
8482 if (logging != MagickFalse)
8483 {
8484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " image->colors=%d", (int) image->colors);
8486
8487 if (image->colormap != NULL)
8488 {
8489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008490 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008491
8492 for (i=0; i < (ssize_t) image->colors; i++)
8493 {
cristy72988482011-03-29 16:34:38 +00008494 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008495 {
8496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8497 " %d (%d,%d,%d,%d)",
8498 (int) i,
8499 (int) image->colormap[i].red,
8500 (int) image->colormap[i].green,
8501 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008502 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008503 }
glennrp6185c532011-01-14 17:58:40 +00008504 }
8505 }
glennrp03812ae2010-12-24 01:31:34 +00008506
glennrpd71e86a2011-02-24 01:28:37 +00008507 if (number_transparent < 257)
8508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8509 " number_transparent = %d",
8510 number_transparent);
8511 else
glennrp03812ae2010-12-24 01:31:34 +00008512
glennrpd71e86a2011-02-24 01:28:37 +00008513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8514 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008515
glennrpd71e86a2011-02-24 01:28:37 +00008516 if (number_opaque < 257)
8517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8518 " number_opaque = %d",
8519 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008520
glennrpd71e86a2011-02-24 01:28:37 +00008521 else
8522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8523 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008524
glennrpd71e86a2011-02-24 01:28:37 +00008525 if (number_semitransparent < 257)
8526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8527 " number_semitransparent = %d",
8528 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008529
glennrpd71e86a2011-02-24 01:28:37 +00008530 else
8531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8532 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008533
glennrpd71e86a2011-02-24 01:28:37 +00008534 if (ping_have_non_bw == MagickFalse)
8535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8536 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008537
glennrpd71e86a2011-02-24 01:28:37 +00008538 else if (ping_have_color == MagickFalse)
8539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8540 " All pixels and the background are gray");
8541
8542 else
8543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8544 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008545
glennrp03812ae2010-12-24 01:31:34 +00008546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8547 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008548 }
glennrpfd05d622011-02-25 04:10:33 +00008549
glennrpc8c2f062011-02-25 19:00:33 +00008550 if (mng_info->write_png8 == MagickFalse)
8551 break;
glennrpfd05d622011-02-25 04:10:33 +00008552
glennrpc8c2f062011-02-25 19:00:33 +00008553 /* Make any reductions necessary for the PNG8 format */
8554 if (image_colors <= 256 &&
8555 image_colors != 0 && image->colormap != NULL &&
8556 number_semitransparent == 0 &&
8557 number_transparent <= 1)
8558 break;
8559
8560 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008561 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8562 * transparent color so if more than one is transparent we merge
8563 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008564 */
glennrp130fc452011-08-20 03:43:18 +00008565 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008566 {
8567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8568 " Thresholding the alpha channel to binary");
8569
8570 for (y=0; y < (ssize_t) image->rows; y++)
8571 {
cristy16ea1392012-03-21 20:38:41 +00008572 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008573
cristy16ea1392012-03-21 20:38:41 +00008574 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008575 break;
8576
8577 for (x=0; x < (ssize_t) image->columns; x++)
8578 {
cristy16ea1392012-03-21 20:38:41 +00008579 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008580 {
cristy16ea1392012-03-21 20:38:41 +00008581 SetPixelInfoPixel(image,&image->background_color,r);
8582 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008583 }
8584 else
cristy16ea1392012-03-21 20:38:41 +00008585 SetPixelAlpha(image,OpaqueAlpha,r);
8586 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008587 }
glennrpbb4f99d2011-05-22 11:13:17 +00008588
glennrpc8c2f062011-02-25 19:00:33 +00008589 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8590 break;
8591
8592 if (image_colors != 0 && image_colors <= 256 &&
8593 image->colormap != NULL)
8594 for (i=0; i<image_colors; i++)
cristy16ea1392012-03-21 20:38:41 +00008595 image->colormap[i].alpha =
8596 (image->colormap[i].alpha > TransparentAlpha/2 ?
8597 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008598 }
8599 continue;
8600 }
8601
8602 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008603 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8604 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8605 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008606 */
glennrpd3371642011-03-22 19:42:23 +00008607 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8608 {
8609 if (logging != MagickFalse)
8610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8611 " Quantizing the background color to 4-4-4");
8612
8613 tried_444 = MagickTrue;
8614
glennrp91d99252011-06-25 14:30:13 +00008615 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008616
8617 if (logging != MagickFalse)
8618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8619 " Quantizing the pixel colors to 4-4-4");
8620
8621 if (image->colormap == NULL)
8622 {
8623 for (y=0; y < (ssize_t) image->rows; y++)
8624 {
cristy16ea1392012-03-21 20:38:41 +00008625 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00008626
cristy16ea1392012-03-21 20:38:41 +00008627 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008628 break;
8629
8630 for (x=0; x < (ssize_t) image->columns; x++)
8631 {
cristy16ea1392012-03-21 20:38:41 +00008632 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008633 LBR04PixelRGB(r);
cristy16ea1392012-03-21 20:38:41 +00008634 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00008635 }
glennrpbb4f99d2011-05-22 11:13:17 +00008636
glennrpd3371642011-03-22 19:42:23 +00008637 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8638 break;
8639 }
8640 }
8641
8642 else /* Should not reach this; colormap already exists and
8643 must be <= 256 */
8644 {
8645 if (logging != MagickFalse)
8646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8647 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008648
glennrpd3371642011-03-22 19:42:23 +00008649 for (i=0; i<image_colors; i++)
8650 {
glennrp91d99252011-06-25 14:30:13 +00008651 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008652 }
8653 }
8654 continue;
8655 }
8656
glennrp82b3c532011-03-22 19:20:54 +00008657 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8658 {
8659 if (logging != MagickFalse)
8660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8661 " Quantizing the background color to 3-3-3");
8662
8663 tried_333 = MagickTrue;
8664
glennrp91d99252011-06-25 14:30:13 +00008665 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008666
8667 if (logging != MagickFalse)
8668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008669 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008670
8671 if (image->colormap == NULL)
8672 {
8673 for (y=0; y < (ssize_t) image->rows; y++)
8674 {
cristy16ea1392012-03-21 20:38:41 +00008675 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00008676
cristy16ea1392012-03-21 20:38:41 +00008677 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008678 break;
8679
8680 for (x=0; x < (ssize_t) image->columns; x++)
8681 {
cristy16ea1392012-03-21 20:38:41 +00008682 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8683 LBR03RGB(r);
8684 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00008685 }
glennrpbb4f99d2011-05-22 11:13:17 +00008686
glennrp82b3c532011-03-22 19:20:54 +00008687 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8688 break;
8689 }
8690 }
8691
8692 else /* Should not reach this; colormap already exists and
8693 must be <= 256 */
8694 {
8695 if (logging != MagickFalse)
8696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008697 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008698 for (i=0; i<image_colors; i++)
8699 {
glennrp91d99252011-06-25 14:30:13 +00008700 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008701 }
glennrpd3371642011-03-22 19:42:23 +00008702 }
8703 continue;
glennrp82b3c532011-03-22 19:20:54 +00008704 }
glennrpc8c2f062011-02-25 19:00:33 +00008705
glennrp8ca51ad2011-05-12 21:22:32 +00008706 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008707 {
8708 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008710 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008711
glennrp8ca51ad2011-05-12 21:22:32 +00008712 tried_332 = MagickTrue;
8713
glennrp3faa9a32011-04-23 14:00:25 +00008714 /* Red and green were already done so we only quantize the blue
8715 * channel
8716 */
8717
glennrp91d99252011-06-25 14:30:13 +00008718 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008719
glennrpc8c2f062011-02-25 19:00:33 +00008720 if (logging != MagickFalse)
8721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008722 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008723
glennrpc8c2f062011-02-25 19:00:33 +00008724 if (image->colormap == NULL)
8725 {
8726 for (y=0; y < (ssize_t) image->rows; y++)
8727 {
cristy16ea1392012-03-21 20:38:41 +00008728 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008729
cristy16ea1392012-03-21 20:38:41 +00008730 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008731 break;
8732
8733 for (x=0; x < (ssize_t) image->columns; x++)
8734 {
cristy16ea1392012-03-21 20:38:41 +00008735 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008736 LBR02PixelBlue(r);
cristy16ea1392012-03-21 20:38:41 +00008737 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008738 }
glennrpbb4f99d2011-05-22 11:13:17 +00008739
glennrpc8c2f062011-02-25 19:00:33 +00008740 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8741 break;
8742 }
8743 }
glennrpfd05d622011-02-25 04:10:33 +00008744
glennrpc8c2f062011-02-25 19:00:33 +00008745 else /* Should not reach this; colormap already exists and
8746 must be <= 256 */
8747 {
8748 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008750 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008751 for (i=0; i<image_colors; i++)
8752 {
glennrp91d99252011-06-25 14:30:13 +00008753 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008754 }
8755 }
8756 continue;
8757 }
8758 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008759
8760 if (image_colors == 0 || image_colors > 256)
8761 {
8762 /* Take care of special case with 256 colors + 1 transparent
8763 * color. We don't need to quantize to 2-3-2-1; we only need to
8764 * eliminate one color, so we'll merge the two darkest red
8765 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8766 */
8767 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8768 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8769 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8770 {
8771 image->background_color.red=ScaleCharToQuantum(0x24);
8772 }
glennrpbb4f99d2011-05-22 11:13:17 +00008773
glennrp8ca51ad2011-05-12 21:22:32 +00008774 if (image->colormap == NULL)
8775 {
8776 for (y=0; y < (ssize_t) image->rows; y++)
8777 {
cristy16ea1392012-03-21 20:38:41 +00008778 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008779
cristy16ea1392012-03-21 20:38:41 +00008780 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008781 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008782
glennrp8ca51ad2011-05-12 21:22:32 +00008783 for (x=0; x < (ssize_t) image->columns; x++)
8784 {
cristy16ea1392012-03-21 20:38:41 +00008785 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8786 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8787 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8788 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008789 {
cristy16ea1392012-03-21 20:38:41 +00008790 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008791 }
cristy16ea1392012-03-21 20:38:41 +00008792 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008793 }
glennrpbb4f99d2011-05-22 11:13:17 +00008794
glennrp8ca51ad2011-05-12 21:22:32 +00008795 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8796 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008797
glennrp8ca51ad2011-05-12 21:22:32 +00008798 }
8799 }
8800
8801 else
8802 {
8803 for (i=0; i<image_colors; i++)
8804 {
8805 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8806 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8807 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8808 {
8809 image->colormap[i].red=ScaleCharToQuantum(0x24);
8810 }
8811 }
8812 }
8813 }
glennrpd71e86a2011-02-24 01:28:37 +00008814 }
glennrpfd05d622011-02-25 04:10:33 +00008815 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008816
glennrpfd05d622011-02-25 04:10:33 +00008817 /* If we are excluding the tRNS chunk and there is transparency,
8818 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8819 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008820 */
glennrp0e8ea192010-12-24 18:00:33 +00008821 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8822 (number_transparent != 0 || number_semitransparent != 0))
8823 {
glennrpd17915c2011-04-29 14:24:22 +00008824 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008825
8826 if (ping_have_color == MagickFalse)
8827 mng_info->write_png_colortype = 5;
8828
8829 else
8830 mng_info->write_png_colortype = 7;
8831
glennrp8d579662011-02-23 02:05:02 +00008832 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008833 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008834 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008835
glennrp0e8ea192010-12-24 18:00:33 +00008836 }
8837
glennrpfd05d622011-02-25 04:10:33 +00008838 /* See if cheap transparency is possible. It is only possible
8839 * when there is a single transparent color, no semitransparent
8840 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008841 * as the transparent color. We only need this information if
8842 * we are writing a PNG with colortype 0 or 2, and we have not
8843 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008844 */
glennrp5a39f372011-02-25 04:52:16 +00008845 if (number_transparent == 1 &&
8846 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008847 {
8848 ping_have_cheap_transparency = MagickTrue;
8849
8850 if (number_semitransparent != 0)
8851 ping_have_cheap_transparency = MagickFalse;
8852
8853 else if (image_colors == 0 || image_colors > 256 ||
8854 image->colormap == NULL)
8855 {
cristy16ea1392012-03-21 20:38:41 +00008856 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008857 *q;
8858
glennrpfd05d622011-02-25 04:10:33 +00008859 for (y=0; y < (ssize_t) image->rows; y++)
8860 {
8861 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8862
cristy16ea1392012-03-21 20:38:41 +00008863 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008864 break;
8865
8866 for (x=0; x < (ssize_t) image->columns; x++)
8867 {
cristy16ea1392012-03-21 20:38:41 +00008868 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8869 (unsigned short) GetPixelRed(image,q) ==
8870 ping_trans_color.red &&
8871 (unsigned short) GetPixelGreen(image,q) ==
8872 ping_trans_color.green &&
8873 (unsigned short) GetPixelBlue(image,q) ==
8874 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008875 {
8876 ping_have_cheap_transparency = MagickFalse;
8877 break;
8878 }
8879
cristy16ea1392012-03-21 20:38:41 +00008880 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008881 }
glennrpbb4f99d2011-05-22 11:13:17 +00008882
glennrpfd05d622011-02-25 04:10:33 +00008883 if (ping_have_cheap_transparency == MagickFalse)
8884 break;
8885 }
8886 }
8887 else
8888 {
glennrp67b9c1a2011-04-22 18:47:36 +00008889 /* Assuming that image->colormap[0] is the one transparent color
8890 * and that all others are opaque.
8891 */
glennrpfd05d622011-02-25 04:10:33 +00008892 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008893 for (i=1; i<image_colors; i++)
8894 if (image->colormap[i].red == image->colormap[0].red &&
8895 image->colormap[i].green == image->colormap[0].green &&
8896 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008897 {
glennrp67b9c1a2011-04-22 18:47:36 +00008898 ping_have_cheap_transparency = MagickFalse;
8899 break;
glennrpfd05d622011-02-25 04:10:33 +00008900 }
8901 }
glennrpbb4f99d2011-05-22 11:13:17 +00008902
glennrpfd05d622011-02-25 04:10:33 +00008903 if (logging != MagickFalse)
8904 {
8905 if (ping_have_cheap_transparency == MagickFalse)
8906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8907 " Cheap transparency is not possible.");
8908
8909 else
8910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8911 " Cheap transparency is possible.");
8912 }
8913 }
8914 else
8915 ping_have_cheap_transparency = MagickFalse;
8916
glennrp8640fb52010-11-23 15:48:26 +00008917 image_depth=image->depth;
8918
glennrp26c990a2010-11-23 02:23:20 +00008919 quantum_info = (QuantumInfo *) NULL;
8920 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008921 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008922 image_matte=image->matte;
8923
glennrp0fe50b42010-11-16 03:52:51 +00008924 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008925 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008926
glennrp52a479c2011-02-26 21:14:38 +00008927 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8928 (image->colors == 0 || image->colormap == NULL))
8929 {
cristy16ea1392012-03-21 20:38:41 +00008930 image_info=DestroyImageInfo(image_info);
8931 image=DestroyImage(image);
8932 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00008933 "Cannot write PNG8 or color-type 3; colormap is NULL",
cristy16ea1392012-03-21 20:38:41 +00008934 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008935 return(MagickFalse);
8936 }
8937
cristy3ed852e2009-09-05 21:47:34 +00008938 /*
8939 Allocate the PNG structures
8940 */
8941#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00008942 error_info.image=image;
8943 error_info.exception=exception;
8944 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008945 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8946 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008947
cristy3ed852e2009-09-05 21:47:34 +00008948#else
cristy16ea1392012-03-21 20:38:41 +00008949 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008950 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008951
cristy3ed852e2009-09-05 21:47:34 +00008952#endif
8953 if (ping == (png_struct *) NULL)
8954 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008955
cristy3ed852e2009-09-05 21:47:34 +00008956 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008957
cristy3ed852e2009-09-05 21:47:34 +00008958 if (ping_info == (png_info *) NULL)
8959 {
8960 png_destroy_write_struct(&ping,(png_info **) NULL);
8961 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8962 }
glennrp0fe50b42010-11-16 03:52:51 +00008963
cristy3ed852e2009-09-05 21:47:34 +00008964 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008965 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008966
glennrp5af765f2010-03-30 11:12:18 +00008967 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008968 {
8969 /*
8970 PNG write failed.
8971 */
8972#ifdef PNG_DEBUG
8973 if (image_info->verbose)
8974 (void) printf("PNG write has failed.\n");
8975#endif
8976 png_destroy_write_struct(&ping,&ping_info);
glennrpedaa0382012-04-12 14:16:21 +00008977#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00008978 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008979#endif
glennrpedaa0382012-04-12 14:16:21 +00008980
8981 if (ping_pixels != (unsigned char *) NULL)
8982 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
8983
8984 if (quantum_info != (QuantumInfo *) NULL)
8985 quantum_info=DestroyQuantumInfo(quantum_info);
8986
cristy16ea1392012-03-21 20:38:41 +00008987 if (ping_have_blob != MagickFalse)
8988 (void) CloseBlob(image);
8989 image_info=DestroyImageInfo(image_info);
8990 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008991 return(MagickFalse);
8992 }
glennrpedaa0382012-04-12 14:16:21 +00008993
8994 /* { For navigation to end of SETJMP-protected block. Within this
8995 * block, use png_error() instead of Throwing an Exception, to ensure
8996 * that libpng is able to clean up, and that the semaphore is unlocked.
8997 */
8998
8999#ifdef PNG_SETJMP_NOT_THREAD_SAFE
9000 LockSemaphoreInfo(ping_semaphore);
9001#endif
9002
cristy3ed852e2009-09-05 21:47:34 +00009003 /*
9004 Prepare PNG for writing.
9005 */
glennrp9bf97b62012-06-06 21:03:14 +00009006
cristy3ed852e2009-09-05 21:47:34 +00009007#if defined(PNG_MNG_FEATURES_SUPPORTED)
9008 if (mng_info->write_mng)
glennrp25024a62012-06-07 11:38:34 +00009009 {
cristy3ed852e2009-09-05 21:47:34 +00009010 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp25024a62012-06-07 11:38:34 +00009011# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9012 /* Disable new libpng-1.5.10 feature when writing a MNG because
9013 * zero-length PLTE is OK
9014 */
9015 png_set_check_for_invalid_index (ping, 0);
9016# endif
9017 }
glennrp2b013e42010-11-24 16:55:50 +00009018
cristy3ed852e2009-09-05 21:47:34 +00009019#else
9020# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9021 if (mng_info->write_mng)
9022 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00009023
cristy3ed852e2009-09-05 21:47:34 +00009024# endif
9025#endif
glennrp2b013e42010-11-24 16:55:50 +00009026
cristy3ed852e2009-09-05 21:47:34 +00009027 x=0;
glennrp2b013e42010-11-24 16:55:50 +00009028
cristy4e5bc842010-06-09 13:56:01 +00009029 ping_width=(png_uint_32) image->columns;
9030 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00009031
cristy3ed852e2009-09-05 21:47:34 +00009032 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9033 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009034
cristy3ed852e2009-09-05 21:47:34 +00009035 if (mng_info->write_png_depth != 0)
9036 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009037
cristy3ed852e2009-09-05 21:47:34 +00009038 /* Adjust requested depth to next higher valid depth if necessary */
9039 if (image_depth > 8)
9040 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009041
cristy3ed852e2009-09-05 21:47:34 +00009042 if ((image_depth > 4) && (image_depth < 8))
9043 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009044
cristy3ed852e2009-09-05 21:47:34 +00009045 if (image_depth == 3)
9046 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00009047
cristy3ed852e2009-09-05 21:47:34 +00009048 if (logging != MagickFalse)
9049 {
9050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009051 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00009052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009053 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00009054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009055 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00009056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009057 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00009058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009059 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00009060 }
glennrp8640fb52010-11-23 15:48:26 +00009061
cristy3ed852e2009-09-05 21:47:34 +00009062 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00009063 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00009064
glennrp26f37912010-12-23 16:22:42 +00009065
cristy3ed852e2009-09-05 21:47:34 +00009066#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00009067 if (ping_exclude_pHYs == MagickFalse)
9068 {
cristy16ea1392012-03-21 20:38:41 +00009069 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00009070 (!mng_info->write_mng || !mng_info->equal_physs))
9071 {
glennrp0fe50b42010-11-16 03:52:51 +00009072 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9074 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00009075
9076 if (image->units == PixelsPerInchResolution)
9077 {
glennrpdfd70802010-11-14 01:23:35 +00009078 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009079 ping_pHYs_x_resolution=
cristy16ea1392012-03-21 20:38:41 +00009080 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00009081 ping_pHYs_y_resolution=
cristy16ea1392012-03-21 20:38:41 +00009082 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009083 }
glennrpdfd70802010-11-14 01:23:35 +00009084
cristy3ed852e2009-09-05 21:47:34 +00009085 else if (image->units == PixelsPerCentimeterResolution)
9086 {
glennrpdfd70802010-11-14 01:23:35 +00009087 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy16ea1392012-03-21 20:38:41 +00009088 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9089 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009090 }
glennrp991d11d2010-11-12 21:55:28 +00009091
cristy3ed852e2009-09-05 21:47:34 +00009092 else
9093 {
glennrpdfd70802010-11-14 01:23:35 +00009094 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy16ea1392012-03-21 20:38:41 +00009095 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9096 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009097 }
glennrp991d11d2010-11-12 21:55:28 +00009098
glennrp823b55c2011-03-14 18:46:46 +00009099 if (logging != MagickFalse)
9100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9101 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9102 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9103 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009104 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009105 }
glennrp26f37912010-12-23 16:22:42 +00009106 }
cristy3ed852e2009-09-05 21:47:34 +00009107#endif
glennrpa521b2f2010-10-29 04:11:03 +00009108
glennrp26f37912010-12-23 16:22:42 +00009109 if (ping_exclude_bKGD == MagickFalse)
9110 {
glennrpa521b2f2010-10-29 04:11:03 +00009111 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009112 {
glennrpa521b2f2010-10-29 04:11:03 +00009113 unsigned int
9114 mask;
cristy3ed852e2009-09-05 21:47:34 +00009115
glennrpa521b2f2010-10-29 04:11:03 +00009116 mask=0xffff;
9117 if (ping_bit_depth == 8)
9118 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009119
glennrpa521b2f2010-10-29 04:11:03 +00009120 if (ping_bit_depth == 4)
9121 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009122
glennrpa521b2f2010-10-29 04:11:03 +00009123 if (ping_bit_depth == 2)
9124 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009125
glennrpa521b2f2010-10-29 04:11:03 +00009126 if (ping_bit_depth == 1)
9127 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009128
glennrpa521b2f2010-10-29 04:11:03 +00009129 ping_background.red=(png_uint_16)
9130 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009131
glennrpa521b2f2010-10-29 04:11:03 +00009132 ping_background.green=(png_uint_16)
9133 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009134
glennrpa521b2f2010-10-29 04:11:03 +00009135 ping_background.blue=(png_uint_16)
9136 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009137
9138 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009139 }
cristy3ed852e2009-09-05 21:47:34 +00009140
glennrp0fe50b42010-11-16 03:52:51 +00009141 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009142 {
9143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9144 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009145 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9146 " background_color index is %d",
9147 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009148
9149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9150 " ping_bit_depth=%d",ping_bit_depth);
9151 }
glennrp0fe50b42010-11-16 03:52:51 +00009152
9153 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009154 }
glennrp0fe50b42010-11-16 03:52:51 +00009155
cristy3ed852e2009-09-05 21:47:34 +00009156 /*
9157 Select the color type.
9158 */
9159 matte=image_matte;
9160 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009161
glennrp1273f7b2011-02-24 03:20:30 +00009162 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009163 {
glennrp0fe50b42010-11-16 03:52:51 +00009164
glennrpfd05d622011-02-25 04:10:33 +00009165 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009166 for reducing the sample depth from 8. */
9167
glennrp0fe50b42010-11-16 03:52:51 +00009168 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009169
glennrp8bb3a022010-12-13 20:40:04 +00009170 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009171
9172 /*
9173 Set image palette.
9174 */
9175 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9176
glennrp0fe50b42010-11-16 03:52:51 +00009177 if (logging != MagickFalse)
9178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9179 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009180 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009181
9182 for (i=0; i < (ssize_t) number_colors; i++)
9183 {
9184 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9185 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9186 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9187 if (logging != MagickFalse)
9188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009189#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009190 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009191#else
9192 " %5ld (%5d,%5d,%5d)",
9193#endif
glennrp0fe50b42010-11-16 03:52:51 +00009194 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9195
9196 }
glennrp2b013e42010-11-24 16:55:50 +00009197
glennrp8bb3a022010-12-13 20:40:04 +00009198 ping_have_PLTE=MagickTrue;
9199 image_depth=ping_bit_depth;
9200 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009201
glennrp58e01762011-01-07 15:28:54 +00009202 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009203 {
glennrp0fe50b42010-11-16 03:52:51 +00009204 /*
9205 Identify which colormap entry is transparent.
9206 */
9207 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009208 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009209
glennrp8bb3a022010-12-13 20:40:04 +00009210 for (i=0; i < (ssize_t) number_transparent; i++)
9211 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009212
glennrp0fe50b42010-11-16 03:52:51 +00009213
glennrp2cc891a2010-12-24 13:44:32 +00009214 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009215 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009216
9217 if (ping_num_trans == 0)
9218 ping_have_tRNS=MagickFalse;
9219
glennrp8bb3a022010-12-13 20:40:04 +00009220 else
9221 ping_have_tRNS=MagickTrue;
9222 }
glennrp0fe50b42010-11-16 03:52:51 +00009223
glennrp1273f7b2011-02-24 03:20:30 +00009224 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009225 {
glennrp1273f7b2011-02-24 03:20:30 +00009226 /*
9227 * Identify which colormap entry is the background color.
9228 */
9229
glennrp4f25bd02011-01-01 18:51:28 +00009230 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9231 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9232 break;
glennrp0fe50b42010-11-16 03:52:51 +00009233
glennrp4f25bd02011-01-01 18:51:28 +00009234 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009235
9236 if (logging != MagickFalse)
9237 {
9238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9239 " background_color index is %d",
9240 (int) ping_background.index);
9241 }
glennrp4f25bd02011-01-01 18:51:28 +00009242 }
cristy3ed852e2009-09-05 21:47:34 +00009243 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009244
glennrp7e65e932011-08-19 02:31:16 +00009245 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009246 {
9247 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009248 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009249 }
glennrp0fe50b42010-11-16 03:52:51 +00009250
glennrp7e65e932011-08-19 02:31:16 +00009251 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009252 {
9253 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009254 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009255 }
glennrp0fe50b42010-11-16 03:52:51 +00009256
glennrp8bb3a022010-12-13 20:40:04 +00009257 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009258 {
glennrp5af765f2010-03-30 11:12:18 +00009259 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009260
glennrp8bb3a022010-12-13 20:40:04 +00009261 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009262 {
glennrp5af765f2010-03-30 11:12:18 +00009263 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009264
glennrp5af765f2010-03-30 11:12:18 +00009265 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9266 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009267 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009268
glennrp8bb3a022010-12-13 20:40:04 +00009269 else
9270 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009271
9272 if (logging != MagickFalse)
9273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9274 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009275 }
glennrp0fe50b42010-11-16 03:52:51 +00009276
glennrp7c4c9e62011-03-21 20:23:32 +00009277 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009278 {
9279 if (logging != MagickFalse)
9280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009281 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009282
glennrpd6bf1612010-12-17 17:28:54 +00009283 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009284 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009285
glennrpd6bf1612010-12-17 17:28:54 +00009286 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009287 {
glennrp5af765f2010-03-30 11:12:18 +00009288 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009289 image_matte=MagickFalse;
9290 }
glennrp0fe50b42010-11-16 03:52:51 +00009291
glennrpd6bf1612010-12-17 17:28:54 +00009292 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009293 {
glennrp5af765f2010-03-30 11:12:18 +00009294 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009295 image_matte=MagickTrue;
9296 }
glennrp0fe50b42010-11-16 03:52:51 +00009297
glennrp5aa37f62011-01-02 03:07:57 +00009298 if (image_info->type == PaletteType ||
9299 image_info->type == PaletteMatteType)
9300 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9301
glennrp7c4c9e62011-03-21 20:23:32 +00009302 if (mng_info->write_png_colortype == 0 &&
9303 (image_info->type == UndefinedType ||
9304 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009305 {
glennrp5aa37f62011-01-02 03:07:57 +00009306 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009307 {
glennrp5aa37f62011-01-02 03:07:57 +00009308 if (image_matte == MagickFalse)
9309 {
9310 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9311 image_matte=MagickFalse;
9312 }
glennrp0fe50b42010-11-16 03:52:51 +00009313
glennrp0b206f52011-01-07 04:55:32 +00009314 else
glennrp5aa37f62011-01-02 03:07:57 +00009315 {
9316 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9317 image_matte=MagickTrue;
9318 }
9319 }
9320 else
glennrp8bb3a022010-12-13 20:40:04 +00009321 {
glennrp5aa37f62011-01-02 03:07:57 +00009322 if (image_matte == MagickFalse)
9323 {
9324 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9325 image_matte=MagickFalse;
9326 }
glennrp8bb3a022010-12-13 20:40:04 +00009327
glennrp0b206f52011-01-07 04:55:32 +00009328 else
glennrp5aa37f62011-01-02 03:07:57 +00009329 {
9330 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9331 image_matte=MagickTrue;
9332 }
9333 }
glennrp0fe50b42010-11-16 03:52:51 +00009334 }
glennrp5aa37f62011-01-02 03:07:57 +00009335
cristy3ed852e2009-09-05 21:47:34 +00009336 }
glennrp0fe50b42010-11-16 03:52:51 +00009337
cristy3ed852e2009-09-05 21:47:34 +00009338 if (logging != MagickFalse)
9339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009340 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009341
glennrp5af765f2010-03-30 11:12:18 +00009342 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009343 {
9344 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9345 ping_color_type == PNG_COLOR_TYPE_RGB ||
9346 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9347 ping_bit_depth=8;
9348 }
cristy3ed852e2009-09-05 21:47:34 +00009349
glennrpd6bf1612010-12-17 17:28:54 +00009350 old_bit_depth=ping_bit_depth;
9351
glennrp5af765f2010-03-30 11:12:18 +00009352 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009353 {
glennrp8d579662011-02-23 02:05:02 +00009354 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9355 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009356 }
glennrp8640fb52010-11-23 15:48:26 +00009357
glennrp5af765f2010-03-30 11:12:18 +00009358 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009359 {
cristy35ef8242010-06-03 16:24:13 +00009360 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009361 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009362
9363 if (image->colors == 0)
9364 {
glennrp0fe50b42010-11-16 03:52:51 +00009365 /* DO SOMETHING */
glennrpedaa0382012-04-12 14:16:21 +00009366 png_error(ping,"image has 0 colors");
glennrp0f111982010-07-07 20:18:33 +00009367 }
9368
cristy35ef8242010-06-03 16:24:13 +00009369 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009370 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009371 }
glennrp2b013e42010-11-24 16:55:50 +00009372
glennrpd6bf1612010-12-17 17:28:54 +00009373 if (logging != MagickFalse)
9374 {
9375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9376 " Number of colors: %.20g",(double) image_colors);
9377
9378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9379 " Tentative PNG bit depth: %d",ping_bit_depth);
9380 }
9381
9382 if (ping_bit_depth < (int) mng_info->write_png_depth)
9383 ping_bit_depth = mng_info->write_png_depth;
9384 }
glennrp2cc891a2010-12-24 13:44:32 +00009385
glennrp5af765f2010-03-30 11:12:18 +00009386 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009387
cristy3ed852e2009-09-05 21:47:34 +00009388 if (logging != MagickFalse)
9389 {
9390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +00009391 " Tentative PNG color type: %s (%.20g)",
9392 PngColorTypeToString(ping_color_type),
9393 (double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009394
cristy3ed852e2009-09-05 21:47:34 +00009395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009396 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009397
cristy3ed852e2009-09-05 21:47:34 +00009398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009399 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009400
cristy3ed852e2009-09-05 21:47:34 +00009401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009402
glennrp8640fb52010-11-23 15:48:26 +00009403 " image->depth: %.20g",(double) image->depth);
9404
9405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009406 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009407 }
9408
glennrp58e01762011-01-07 15:28:54 +00009409 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009410 {
glennrp4f25bd02011-01-01 18:51:28 +00009411 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009412 {
glennrp7c4c9e62011-03-21 20:23:32 +00009413 if (mng_info->write_png_colortype == 0)
9414 {
9415 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009416
glennrp7c4c9e62011-03-21 20:23:32 +00009417 if (ping_have_color != MagickFalse)
9418 ping_color_type=PNG_COLOR_TYPE_RGBA;
9419 }
glennrp4f25bd02011-01-01 18:51:28 +00009420
9421 /*
9422 * Determine if there is any transparent color.
9423 */
9424 if (number_transparent + number_semitransparent == 0)
9425 {
9426 /*
9427 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9428 */
glennrpa6a06632011-01-19 15:15:34 +00009429
glennrp4f25bd02011-01-01 18:51:28 +00009430 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009431
9432 if (mng_info->write_png_colortype == 0)
9433 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009434 }
9435
9436 else
9437 {
9438 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009439 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009440
9441 mask=0xffff;
9442
9443 if (ping_bit_depth == 8)
9444 mask=0x00ff;
9445
9446 if (ping_bit_depth == 4)
9447 mask=0x000f;
9448
9449 if (ping_bit_depth == 2)
9450 mask=0x0003;
9451
9452 if (ping_bit_depth == 1)
9453 mask=0x0001;
9454
9455 ping_trans_color.red=(png_uint_16)
9456 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9457
9458 ping_trans_color.green=(png_uint_16)
9459 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9460
9461 ping_trans_color.blue=(png_uint_16)
9462 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9463
9464 ping_trans_color.gray=(png_uint_16)
cristy16ea1392012-03-21 20:38:41 +00009465 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009466 image->colormap)) & mask);
9467
9468 ping_trans_color.index=(png_byte) 0;
9469
9470 ping_have_tRNS=MagickTrue;
9471 }
9472
9473 if (ping_have_tRNS != MagickFalse)
9474 {
9475 /*
glennrpfd05d622011-02-25 04:10:33 +00009476 * Determine if there is one and only one transparent color
9477 * and if so if it is fully transparent.
9478 */
9479 if (ping_have_cheap_transparency == MagickFalse)
9480 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009481 }
9482
9483 if (ping_have_tRNS != MagickFalse)
9484 {
glennrp7c4c9e62011-03-21 20:23:32 +00009485 if (mng_info->write_png_colortype == 0)
9486 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009487
9488 if (image_depth == 8)
9489 {
9490 ping_trans_color.red&=0xff;
9491 ping_trans_color.green&=0xff;
9492 ping_trans_color.blue&=0xff;
9493 ping_trans_color.gray&=0xff;
9494 }
9495 }
9496 }
cristy3ed852e2009-09-05 21:47:34 +00009497 else
9498 {
cristy3ed852e2009-09-05 21:47:34 +00009499 if (image_depth == 8)
9500 {
glennrp5af765f2010-03-30 11:12:18 +00009501 ping_trans_color.red&=0xff;
9502 ping_trans_color.green&=0xff;
9503 ping_trans_color.blue&=0xff;
9504 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009505 }
9506 }
9507 }
glennrp8640fb52010-11-23 15:48:26 +00009508
cristy3ed852e2009-09-05 21:47:34 +00009509 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009510
glennrp2e09f552010-11-14 00:38:48 +00009511 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009512 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009513
glennrp39992b42010-11-14 00:03:43 +00009514 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009515 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009516 ping_have_color == MagickFalse &&
9517 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009518 {
cristy35ef8242010-06-03 16:24:13 +00009519 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009520
cristy3ed852e2009-09-05 21:47:34 +00009521 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009522 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009523
glennrp7c4c9e62011-03-21 20:23:32 +00009524 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009525 {
glennrp5af765f2010-03-30 11:12:18 +00009526 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009527
cristy3ed852e2009-09-05 21:47:34 +00009528 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009529 {
9530 if (logging != MagickFalse)
9531 {
9532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9533 " Scaling ping_trans_color (0)");
9534 }
9535 ping_trans_color.gray*=0x0101;
9536 }
cristy3ed852e2009-09-05 21:47:34 +00009537 }
glennrp0fe50b42010-11-16 03:52:51 +00009538
cristy3ed852e2009-09-05 21:47:34 +00009539 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9540 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009541
glennrp136ee3a2011-04-27 15:47:45 +00009542 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009543 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009544 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009545
cristy3ed852e2009-09-05 21:47:34 +00009546 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009547 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009548
cristy3ed852e2009-09-05 21:47:34 +00009549 else
9550 {
glennrp5af765f2010-03-30 11:12:18 +00009551 ping_bit_depth=8;
9552 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009553 {
9554 if(!mng_info->write_png_depth)
9555 {
glennrp5af765f2010-03-30 11:12:18 +00009556 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009557
cristy35ef8242010-06-03 16:24:13 +00009558 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009559 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009560 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009561 }
9562 }
glennrp2b013e42010-11-24 16:55:50 +00009563
glennrp0fe50b42010-11-16 03:52:51 +00009564 else if (ping_color_type ==
9565 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009566 mng_info->IsPalette)
9567 {
cristy3ed852e2009-09-05 21:47:34 +00009568 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009569
cristy3ed852e2009-09-05 21:47:34 +00009570 int
9571 depth_4_ok=MagickTrue,
9572 depth_2_ok=MagickTrue,
9573 depth_1_ok=MagickTrue;
9574
cristybb503372010-05-27 20:51:26 +00009575 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009576 {
9577 unsigned char
9578 intensity;
9579
9580 intensity=ScaleQuantumToChar(image->colormap[i].red);
9581
9582 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9583 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9584 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9585 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009586 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009587 depth_1_ok=MagickFalse;
9588 }
glennrp2b013e42010-11-24 16:55:50 +00009589
cristy3ed852e2009-09-05 21:47:34 +00009590 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009591 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009592
cristy3ed852e2009-09-05 21:47:34 +00009593 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009594 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009595
cristy3ed852e2009-09-05 21:47:34 +00009596 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009597 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009598 }
9599 }
glennrp2b013e42010-11-24 16:55:50 +00009600
glennrp5af765f2010-03-30 11:12:18 +00009601 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009602 }
glennrp0fe50b42010-11-16 03:52:51 +00009603
cristy3ed852e2009-09-05 21:47:34 +00009604 else
glennrp0fe50b42010-11-16 03:52:51 +00009605
cristy3ed852e2009-09-05 21:47:34 +00009606 if (mng_info->IsPalette)
9607 {
glennrp17a14852010-05-10 03:01:59 +00009608 number_colors=image_colors;
9609
cristy3ed852e2009-09-05 21:47:34 +00009610 if (image_depth <= 8)
9611 {
cristy3ed852e2009-09-05 21:47:34 +00009612 /*
9613 Set image palette.
9614 */
glennrp5af765f2010-03-30 11:12:18 +00009615 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009616
glennrp58e01762011-01-07 15:28:54 +00009617 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009618 {
glennrp9c1eb072010-06-06 22:19:15 +00009619 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009620
glennrp3b51f0e2010-11-27 18:14:08 +00009621 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9623 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009624 }
glennrp0fe50b42010-11-16 03:52:51 +00009625
cristy3ed852e2009-09-05 21:47:34 +00009626 else
9627 {
cristybb503372010-05-27 20:51:26 +00009628 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009629 {
9630 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9631 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9632 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9633 }
glennrp0fe50b42010-11-16 03:52:51 +00009634
glennrp3b51f0e2010-11-27 18:14:08 +00009635 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009637 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009638 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009639
glennrp39992b42010-11-14 00:03:43 +00009640 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009641 }
glennrp0fe50b42010-11-16 03:52:51 +00009642
cristy3ed852e2009-09-05 21:47:34 +00009643 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009644 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009645 {
cristybefe4d22010-06-07 01:18:58 +00009646 size_t
9647 one;
9648
glennrp5af765f2010-03-30 11:12:18 +00009649 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009650 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009651
cristy16ea1392012-03-21 20:38:41 +00009652 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009653 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009654 }
glennrp0fe50b42010-11-16 03:52:51 +00009655
glennrp5af765f2010-03-30 11:12:18 +00009656 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009657
glennrp58e01762011-01-07 15:28:54 +00009658 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009659 {
glennrp0fe50b42010-11-16 03:52:51 +00009660 /*
glennrpd6bf1612010-12-17 17:28:54 +00009661 * Set up trans_colors array.
9662 */
glennrp0fe50b42010-11-16 03:52:51 +00009663 assert(number_colors <= 256);
9664
glennrpd6bf1612010-12-17 17:28:54 +00009665 ping_num_trans=(unsigned short) (number_transparent +
9666 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009667
9668 if (ping_num_trans == 0)
9669 ping_have_tRNS=MagickFalse;
9670
glennrpd6bf1612010-12-17 17:28:54 +00009671 else
glennrp0fe50b42010-11-16 03:52:51 +00009672 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009673 if (logging != MagickFalse)
9674 {
9675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9676 " Scaling ping_trans_color (1)");
9677 }
glennrpd6bf1612010-12-17 17:28:54 +00009678 ping_have_tRNS=MagickTrue;
9679
9680 for (i=0; i < ping_num_trans; i++)
9681 {
glennrp750105b2012-04-25 16:20:45 +00009682 ping_trans_alpha[i]= (png_byte)
cristy16ea1392012-03-21 20:38:41 +00009683 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009684 }
glennrp0fe50b42010-11-16 03:52:51 +00009685 }
9686 }
cristy3ed852e2009-09-05 21:47:34 +00009687 }
9688 }
glennrp0fe50b42010-11-16 03:52:51 +00009689
cristy3ed852e2009-09-05 21:47:34 +00009690 else
9691 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009692
cristy3ed852e2009-09-05 21:47:34 +00009693 if (image_depth < 8)
9694 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009695
cristy3ed852e2009-09-05 21:47:34 +00009696 if ((save_image_depth == 16) && (image_depth == 8))
9697 {
glennrp4f25bd02011-01-01 18:51:28 +00009698 if (logging != MagickFalse)
9699 {
9700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9701 " Scaling ping_trans_color from (%d,%d,%d)",
9702 (int) ping_trans_color.red,
9703 (int) ping_trans_color.green,
9704 (int) ping_trans_color.blue);
9705 }
9706
glennrp5af765f2010-03-30 11:12:18 +00009707 ping_trans_color.red*=0x0101;
9708 ping_trans_color.green*=0x0101;
9709 ping_trans_color.blue*=0x0101;
9710 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009711
9712 if (logging != MagickFalse)
9713 {
9714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9715 " to (%d,%d,%d)",
9716 (int) ping_trans_color.red,
9717 (int) ping_trans_color.green,
9718 (int) ping_trans_color.blue);
9719 }
cristy3ed852e2009-09-05 21:47:34 +00009720 }
9721 }
9722
cristy4383ec82011-01-05 15:42:32 +00009723 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9724 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009725
cristy3ed852e2009-09-05 21:47:34 +00009726 /*
9727 Adjust background and transparency samples in sub-8-bit grayscale files.
9728 */
glennrp5af765f2010-03-30 11:12:18 +00009729 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009730 PNG_COLOR_TYPE_GRAY)
9731 {
9732 png_uint_16
9733 maxval;
9734
cristy35ef8242010-06-03 16:24:13 +00009735 size_t
9736 one=1;
9737
cristy22ffd972010-06-03 16:51:47 +00009738 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009739
glennrp4f25bd02011-01-01 18:51:28 +00009740 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009741 {
cristy3ed852e2009-09-05 21:47:34 +00009742
cristy16ea1392012-03-21 20:38:41 +00009743 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9744 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9745 &image->background_color))) +.5)));
9746
cristy3ed852e2009-09-05 21:47:34 +00009747 if (logging != MagickFalse)
9748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00009749 " Setting up bKGD chunk (2)");
9750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9751 " background_color index is %d",
9752 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009753
glennrp991d11d2010-11-12 21:55:28 +00009754 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009755 }
cristy3ed852e2009-09-05 21:47:34 +00009756
glennrp3e3e20f2011-06-09 04:21:43 +00009757 if (logging != MagickFalse)
9758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9759 " Scaling ping_trans_color.gray from %d",
9760 (int)ping_trans_color.gray);
9761
glennrp9be9b1c2011-06-09 12:21:45 +00009762 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009763 ping_trans_color.gray)+.5);
9764
9765 if (logging != MagickFalse)
9766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9767 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009768 }
glennrp17a14852010-05-10 03:01:59 +00009769
glennrp26f37912010-12-23 16:22:42 +00009770 if (ping_exclude_bKGD == MagickFalse)
9771 {
glennrp1273f7b2011-02-24 03:20:30 +00009772 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009773 {
9774 /*
9775 Identify which colormap entry is the background color.
9776 */
9777
glennrp17a14852010-05-10 03:01:59 +00009778 number_colors=image_colors;
9779
glennrpa521b2f2010-10-29 04:11:03 +00009780 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9781 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009782 break;
9783
9784 ping_background.index=(png_byte) i;
9785
glennrp3b51f0e2010-11-27 18:14:08 +00009786 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009787 {
9788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009789 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009790 }
glennrp0fe50b42010-11-16 03:52:51 +00009791
cristy13d07042010-11-21 20:56:18 +00009792 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009793 {
9794 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009795
9796 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009797 {
9798 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9799 " background =(%d,%d,%d)",
9800 (int) ping_background.red,
9801 (int) ping_background.green,
9802 (int) ping_background.blue);
9803 }
9804 }
glennrpa521b2f2010-10-29 04:11:03 +00009805
glennrpd6bf1612010-12-17 17:28:54 +00009806 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009807 {
glennrp3b51f0e2010-11-27 18:14:08 +00009808 if (logging != MagickFalse)
9809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9810 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009811 ping_have_bKGD = MagickFalse;
9812 }
glennrp17a14852010-05-10 03:01:59 +00009813 }
glennrp26f37912010-12-23 16:22:42 +00009814 }
glennrp17a14852010-05-10 03:01:59 +00009815
cristy3ed852e2009-09-05 21:47:34 +00009816 if (logging != MagickFalse)
9817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +00009818 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
9819 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009820 /*
9821 Initialize compression level and filtering.
9822 */
9823 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009824 {
9825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9826 " Setting up deflate compression");
9827
9828 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9829 " Compression buffer size: 32768");
9830 }
9831
cristy3ed852e2009-09-05 21:47:34 +00009832 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009833
cristy3ed852e2009-09-05 21:47:34 +00009834 if (logging != MagickFalse)
9835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9836 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009837
cristy4054bfb2011-08-29 23:41:39 +00009838 png_set_compression_mem_level(ping, 9);
9839
glennrp10d739e2011-06-29 18:00:52 +00009840 /* Untangle the "-quality" setting:
9841
9842 Undefined is 0; the default is used.
9843 Default is 75
9844
9845 10's digit:
9846
9847 0: Use Z_HUFFMAN_ONLY strategy with the
9848 zlib default compression level
9849
9850 1-9: the zlib compression level
9851
9852 1's digit:
9853
9854 0-4: the PNG filter method
9855
9856 5: libpng adaptive filtering if compression level > 5
9857 libpng filter type "none" if compression level <= 5
9858 or if image is grayscale or palette
glennrp750105b2012-04-25 16:20:45 +00009859
glennrp10d739e2011-06-29 18:00:52 +00009860 6: libpng adaptive filtering
9861
9862 7: "LOCO" filtering (intrapixel differing) if writing
9863 a MNG, othewise "none". Did not work in IM-6.7.0-9
9864 and earlier because of a missing "else".
9865
9866 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009867 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009868
9869 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009870 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009871
9872 Note that using the -quality option, not all combinations of
9873 PNG filter type, zlib compression level, and zlib compression
cristy16ea1392012-03-21 20:38:41 +00009874 strategy are possible. This will be addressed soon in a
9875 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009876
9877 */
9878
cristy3ed852e2009-09-05 21:47:34 +00009879 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9880 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009881
glennrp18682582011-06-30 18:11:47 +00009882 if (quality <= 9)
9883 {
9884 if (mng_info->write_png_compression_strategy == 0)
9885 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9886 }
glennrp750105b2012-04-25 16:20:45 +00009887
glennrp18682582011-06-30 18:11:47 +00009888 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009889 {
9890 int
9891 level;
9892
cristybb503372010-05-27 20:51:26 +00009893 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009894
glennrp18682582011-06-30 18:11:47 +00009895 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009896 }
glennrp0fe50b42010-11-16 03:52:51 +00009897
glennrp18682582011-06-30 18:11:47 +00009898 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009899 {
glennrp18682582011-06-30 18:11:47 +00009900 if ((quality %10) == 8 || (quality %10) == 9)
9901 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009902 }
glennrp0fe50b42010-11-16 03:52:51 +00009903
glennrp18682582011-06-30 18:11:47 +00009904 if (mng_info->write_png_compression_filter == 0)
9905 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9906
cristy3ed852e2009-09-05 21:47:34 +00009907 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009908 {
glennrp18682582011-06-30 18:11:47 +00009909 if (mng_info->write_png_compression_level)
9910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9911 " Compression level: %d",
9912 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009913
glennrp18682582011-06-30 18:11:47 +00009914 if (mng_info->write_png_compression_strategy)
9915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9916 " Compression strategy: %d",
9917 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009918
glennrp18682582011-06-30 18:11:47 +00009919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9920 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009921
cristy4054bfb2011-08-29 23:41:39 +00009922 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9924 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009925 else if (mng_info->write_png_compression_filter == 0 ||
9926 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9928 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009929 else
9930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9931 " Base filter method: %d",
9932 (int) mng_info->write_png_compression_filter-1);
9933 }
glennrp2b013e42010-11-24 16:55:50 +00009934
glennrp18682582011-06-30 18:11:47 +00009935 if (mng_info->write_png_compression_level != 0)
9936 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9937
9938 if (mng_info->write_png_compression_filter == 6)
9939 {
9940 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9941 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9942 (quality < 50))
9943 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9944 else
9945 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9946 }
cristy4054bfb2011-08-29 23:41:39 +00009947 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009948 mng_info->write_png_compression_filter == 10)
9949 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9950
9951 else if (mng_info->write_png_compression_filter == 8)
9952 {
9953#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9954 if (mng_info->write_mng)
9955 {
9956 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9957 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9958 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9959 }
9960#endif
cristy4054bfb2011-08-29 23:41:39 +00009961 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009962 }
9963
9964 else if (mng_info->write_png_compression_filter == 9)
9965 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9966
9967 else if (mng_info->write_png_compression_filter != 0)
9968 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9969 mng_info->write_png_compression_filter-1);
9970
9971 if (mng_info->write_png_compression_strategy != 0)
9972 png_set_compression_strategy(ping,
9973 mng_info->write_png_compression_strategy-1);
9974
cristy0d57eec2011-09-04 22:13:56 +00009975 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9976 if (ping_exclude_sRGB != MagickFalse ||
9977 (image->rendering_intent == UndefinedIntent))
9978 {
9979 if ((ping_exclude_tEXt == MagickFalse ||
9980 ping_exclude_zTXt == MagickFalse) &&
9981 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009982 {
9983 ResetImageProfileIterator(image);
9984 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009985 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009986 profile=GetImageProfile(image,name);
9987
9988 if (profile != (StringInfo *) NULL)
9989 {
glennrp5af765f2010-03-30 11:12:18 +00009990#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009991 if ((LocaleCompare(name,"ICC") == 0) ||
9992 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009993 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009994
9995 if (ping_exclude_iCCP == MagickFalse)
9996 {
cristy16ea1392012-03-21 20:38:41 +00009997 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009998#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009999 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +000010000#else
10001 (png_const_bytep) GetStringInfoDatum(profile),
10002#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010003 (png_uint_32) GetStringInfoLength(profile));
10004 }
glennrp26f37912010-12-23 16:22:42 +000010005 }
glennrp0fe50b42010-11-16 03:52:51 +000010006
glennrpc8cbc5d2011-01-01 00:12:34 +000010007 else
cristy3ed852e2009-09-05 21:47:34 +000010008#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010009 if (ping_exclude_zCCP == MagickFalse)
10010 {
glennrpcf002022011-01-30 02:38:15 +000010011 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +000010012 (unsigned char *) name,(unsigned char *) name,
10013 GetStringInfoDatum(profile),
10014 (png_uint_32) GetStringInfoLength(profile));
10015 }
10016 }
glennrp0b206f52011-01-07 04:55:32 +000010017
glennrpc8cbc5d2011-01-01 00:12:34 +000010018 if (logging != MagickFalse)
10019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10020 " Setting up text chunk with %s profile",name);
10021
10022 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +000010023 }
cristy0d57eec2011-09-04 22:13:56 +000010024 }
cristy3ed852e2009-09-05 21:47:34 +000010025 }
10026
10027#if defined(PNG_WRITE_sRGB_SUPPORTED)
10028 if ((mng_info->have_write_global_srgb == 0) &&
glennrp5f11bf92012-05-05 03:21:03 +000010029 (image->rendering_intent != UndefinedIntent))
cristy3ed852e2009-09-05 21:47:34 +000010030 {
glennrp26f37912010-12-23 16:22:42 +000010031 if (ping_exclude_sRGB == MagickFalse)
10032 {
10033 /*
10034 Note image rendering intent.
10035 */
10036 if (logging != MagickFalse)
10037 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10038 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +000010039
glennrp26f37912010-12-23 16:22:42 +000010040 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +000010041 Magick_RenderingIntent_to_PNG_RenderingIntent(
10042 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +000010043 }
cristy3ed852e2009-09-05 21:47:34 +000010044 }
glennrp26f37912010-12-23 16:22:42 +000010045
glennrp5af765f2010-03-30 11:12:18 +000010046 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +000010047#endif
10048 {
glennrp2cc891a2010-12-24 13:44:32 +000010049 if (ping_exclude_gAMA == MagickFalse &&
10050 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +000010051 (image->gamma < .45 || image->gamma > .46)))
10052 {
cristy3ed852e2009-09-05 21:47:34 +000010053 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10054 {
10055 /*
10056 Note image gamma.
10057 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10058 */
10059 if (logging != MagickFalse)
10060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10061 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010062
cristy3ed852e2009-09-05 21:47:34 +000010063 png_set_gAMA(ping,ping_info,image->gamma);
10064 }
glennrp26f37912010-12-23 16:22:42 +000010065 }
glennrp2b013e42010-11-24 16:55:50 +000010066
glennrp26f37912010-12-23 16:22:42 +000010067 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010068 {
glennrp26f37912010-12-23 16:22:42 +000010069 if ((mng_info->have_write_global_chrm == 0) &&
10070 (image->chromaticity.red_primary.x != 0.0))
10071 {
10072 /*
10073 Note image chromaticity.
10074 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10075 */
10076 PrimaryInfo
10077 bp,
10078 gp,
10079 rp,
10080 wp;
cristy3ed852e2009-09-05 21:47:34 +000010081
glennrp26f37912010-12-23 16:22:42 +000010082 wp=image->chromaticity.white_point;
10083 rp=image->chromaticity.red_primary;
10084 gp=image->chromaticity.green_primary;
10085 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010086
glennrp26f37912010-12-23 16:22:42 +000010087 if (logging != MagickFalse)
10088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10089 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010090
glennrp26f37912010-12-23 16:22:42 +000010091 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10092 bp.x,bp.y);
10093 }
10094 }
cristy3ed852e2009-09-05 21:47:34 +000010095 }
glennrpdfd70802010-11-14 01:23:35 +000010096
glennrp5af765f2010-03-30 11:12:18 +000010097 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010098
10099 if (mng_info->write_mng)
10100 png_set_sig_bytes(ping,8);
10101
cristy5d6fc9c2011-12-27 03:10:42 +000010102 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
cristy3ed852e2009-09-05 21:47:34 +000010103
glennrpd6bf1612010-12-17 17:28:54 +000010104 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010105 {
10106 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010107 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010108 {
glennrp5af765f2010-03-30 11:12:18 +000010109 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010110
glennrp5af765f2010-03-30 11:12:18 +000010111 if (ping_bit_depth < 8)
10112 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010113 }
glennrp0fe50b42010-11-16 03:52:51 +000010114
cristy3ed852e2009-09-05 21:47:34 +000010115 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010116 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010117 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010118 }
10119
glennrp0e8ea192010-12-24 18:00:33 +000010120 if (ping_need_colortype_warning != MagickFalse ||
10121 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010122 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010123 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010124 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010125 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010126 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010127 {
10128 if (logging != MagickFalse)
10129 {
glennrp0e8ea192010-12-24 18:00:33 +000010130 if (ping_need_colortype_warning != MagickFalse)
10131 {
10132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10133 " Image has transparency but tRNS chunk was excluded");
10134 }
10135
cristy3ed852e2009-09-05 21:47:34 +000010136 if (mng_info->write_png_depth)
10137 {
10138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010139 " Defined png:bit-depth=%u, Computed depth=%u",
cristy3ed852e2009-09-05 21:47:34 +000010140 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010141 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010142 }
glennrp0e8ea192010-12-24 18:00:33 +000010143
cristy3ed852e2009-09-05 21:47:34 +000010144 if (mng_info->write_png_colortype)
10145 {
10146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010147 " Defined png:color-type=%u, Computed color type=%u",
cristy3ed852e2009-09-05 21:47:34 +000010148 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010149 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010150 }
10151 }
glennrp0e8ea192010-12-24 18:00:33 +000010152
glennrp3bd2e412010-08-10 13:34:52 +000010153 png_warning(ping,
cristy5d6fc9c2011-12-27 03:10:42 +000010154 "Cannot write image with defined png:bit-depth or png:color-type.");
cristy3ed852e2009-09-05 21:47:34 +000010155 }
10156
glennrp58e01762011-01-07 15:28:54 +000010157 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010158 {
10159 /* Add an opaque matte channel */
10160 image->matte = MagickTrue;
cristy16ea1392012-03-21 20:38:41 +000010161 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010162
glennrpb4a13412010-05-05 12:47:19 +000010163 if (logging != MagickFalse)
10164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10165 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010166 }
10167
glennrp0e319732011-01-25 21:53:13 +000010168 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010169 {
glennrp991d11d2010-11-12 21:55:28 +000010170 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010171 {
glennrp991d11d2010-11-12 21:55:28 +000010172 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010173 if (logging != MagickFalse)
10174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10175 " Setting ping_have_tRNS=MagickTrue.");
10176 }
glennrpe9c26dc2010-05-30 01:56:35 +000010177 }
10178
cristy3ed852e2009-09-05 21:47:34 +000010179 if (logging != MagickFalse)
10180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10181 " Writing PNG header chunks");
10182
glennrp5af765f2010-03-30 11:12:18 +000010183 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10184 ping_bit_depth,ping_color_type,
10185 ping_interlace_method,ping_compression_method,
10186 ping_filter_method);
10187
glennrp39992b42010-11-14 00:03:43 +000010188 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10189 {
glennrpf09bded2011-01-08 01:15:59 +000010190 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010191
glennrp3b51f0e2010-11-27 18:14:08 +000010192 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010193 {
glennrp8640fb52010-11-23 15:48:26 +000010194 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010195 {
glennrpd6bf1612010-12-17 17:28:54 +000010196 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010198 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10199 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010200 (int) palette[i].red,
10201 (int) palette[i].green,
10202 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010203 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010204 (int) ping_trans_alpha[i]);
10205 else
10206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010207 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010208 (int) i,
10209 (int) palette[i].red,
10210 (int) palette[i].green,
10211 (int) palette[i].blue);
10212 }
glennrp39992b42010-11-14 00:03:43 +000010213 }
glennrp39992b42010-11-14 00:03:43 +000010214 }
10215
glennrp26f37912010-12-23 16:22:42 +000010216 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010217 {
glennrp26f37912010-12-23 16:22:42 +000010218 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010219 {
glennrp26f37912010-12-23 16:22:42 +000010220 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010221 if (logging)
10222 {
10223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10224 " Setting up bKGD chunk");
10225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10226 " background color = (%d,%d,%d)",
10227 (int) ping_background.red,
10228 (int) ping_background.green,
10229 (int) ping_background.blue);
10230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10231 " index = %d, gray=%d",
10232 (int) ping_background.index,
10233 (int) ping_background.gray);
10234 }
10235 }
glennrp26f37912010-12-23 16:22:42 +000010236 }
10237
10238 if (ping_exclude_pHYs == MagickFalse)
10239 {
10240 if (ping_have_pHYs != MagickFalse)
10241 {
10242 png_set_pHYs(ping,ping_info,
10243 ping_pHYs_x_resolution,
10244 ping_pHYs_y_resolution,
10245 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010246
10247 if (logging)
10248 {
10249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10250 " Setting up pHYs chunk");
10251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10252 " x_resolution=%lu",
10253 (unsigned long) ping_pHYs_x_resolution);
10254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10255 " y_resolution=%lu",
10256 (unsigned long) ping_pHYs_y_resolution);
10257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10258 " unit_type=%lu",
10259 (unsigned long) ping_pHYs_unit_type);
10260 }
glennrp26f37912010-12-23 16:22:42 +000010261 }
glennrpdfd70802010-11-14 01:23:35 +000010262 }
10263
10264#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010265 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010266 {
glennrp26f37912010-12-23 16:22:42 +000010267 if (image->page.x || image->page.y)
10268 {
10269 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10270 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010271
glennrp26f37912010-12-23 16:22:42 +000010272 if (logging != MagickFalse)
10273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10274 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10275 (int) image->page.x, (int) image->page.y);
10276 }
glennrpdfd70802010-11-14 01:23:35 +000010277 }
10278#endif
10279
glennrpda8f3a72011-02-27 23:54:12 +000010280 if (mng_info->need_blob != MagickFalse)
10281 {
cristy16ea1392012-03-21 20:38:41 +000010282 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010283 MagickFalse)
10284 png_error(ping,"WriteBlob Failed");
10285
10286 ping_have_blob=MagickTrue;
10287 }
10288
cristy3ed852e2009-09-05 21:47:34 +000010289 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010290
glennrp39992b42010-11-14 00:03:43 +000010291 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010292 {
glennrp3b51f0e2010-11-27 18:14:08 +000010293 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010294 {
10295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10296 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10297 }
10298
10299 if (ping_color_type == 3)
10300 (void) png_set_tRNS(ping, ping_info,
10301 ping_trans_alpha,
10302 ping_num_trans,
10303 NULL);
10304
10305 else
10306 {
10307 (void) png_set_tRNS(ping, ping_info,
10308 NULL,
10309 0,
10310 &ping_trans_color);
10311
glennrp3b51f0e2010-11-27 18:14:08 +000010312 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010313 {
10314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010315 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010316 (int) ping_trans_color.red,
10317 (int) ping_trans_color.green,
10318 (int) ping_trans_color.blue);
10319 }
10320 }
glennrp991d11d2010-11-12 21:55:28 +000010321 }
10322
cristy3ed852e2009-09-05 21:47:34 +000010323 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010324 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010325
cristy3ed852e2009-09-05 21:47:34 +000010326 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010327
cristy3ed852e2009-09-05 21:47:34 +000010328 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010329 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010330
glennrp26f37912010-12-23 16:22:42 +000010331 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010332 {
glennrp4f25bd02011-01-01 18:51:28 +000010333 if ((image->page.width != 0 && image->page.width != image->columns) ||
10334 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010335 {
10336 unsigned char
10337 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010338
glennrp26f37912010-12-23 16:22:42 +000010339 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10340 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010341 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010342 PNGLong(chunk+4,(png_uint_32) image->page.width);
10343 PNGLong(chunk+8,(png_uint_32) image->page.height);
10344 chunk[12]=0; /* unit = pixels */
10345 (void) WriteBlob(image,13,chunk);
10346 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10347 }
cristy3ed852e2009-09-05 21:47:34 +000010348 }
10349
10350#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010351 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010352#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010353 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010354#undef PNG_HAVE_IDAT
10355#endif
10356
10357 png_set_packing(ping);
10358 /*
10359 Allocate memory.
10360 */
10361 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010362 if (image_depth > 8)
10363 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010364 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010365 {
glennrpb4a13412010-05-05 12:47:19 +000010366 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010367 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010368 break;
glennrp0fe50b42010-11-16 03:52:51 +000010369
glennrpb4a13412010-05-05 12:47:19 +000010370 case PNG_COLOR_TYPE_GRAY_ALPHA:
10371 rowbytes*=2;
10372 break;
glennrp0fe50b42010-11-16 03:52:51 +000010373
glennrpb4a13412010-05-05 12:47:19 +000010374 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010375 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010376 break;
glennrp0fe50b42010-11-16 03:52:51 +000010377
glennrpb4a13412010-05-05 12:47:19 +000010378 default:
10379 break;
cristy3ed852e2009-09-05 21:47:34 +000010380 }
glennrp3b51f0e2010-11-27 18:14:08 +000010381
10382 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010383 {
10384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10385 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010386
glennrpb4a13412010-05-05 12:47:19 +000010387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010388 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010389 }
glennrpcf002022011-01-30 02:38:15 +000010390 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10391 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010392
glennrpcf002022011-01-30 02:38:15 +000010393 if (ping_pixels == (unsigned char *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010394 png_error(ping,"Allocation of memory for pixels failed");
glennrp0fe50b42010-11-16 03:52:51 +000010395
cristy3ed852e2009-09-05 21:47:34 +000010396 /*
10397 Initialize image scanlines.
10398 */
cristyed552522009-10-16 14:04:35 +000010399 quantum_info=AcquireQuantumInfo(image_info,image);
10400 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010401 png_error(ping,"Memory allocation for quantum_info failed");
cristy3ed852e2009-09-05 21:47:34 +000010402 quantum_info->format=UndefinedQuantumFormat;
10403 quantum_info->depth=image_depth;
10404 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010405
cristy3ed852e2009-09-05 21:47:34 +000010406 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010407 !mng_info->write_png32) &&
10408 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010409 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010410 image_matte == MagickFalse &&
10411 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010412 {
glennrp8bb3a022010-12-13 20:40:04 +000010413 /* Palette, Bilevel, or Opaque Monochrome */
cristy16ea1392012-03-21 20:38:41 +000010414 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010415 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010416
cristy3ed852e2009-09-05 21:47:34 +000010417 quantum_info->depth=8;
10418 for (pass=0; pass < num_passes; pass++)
10419 {
10420 /*
10421 Convert PseudoClass image to a PNG monochrome image.
10422 */
cristybb503372010-05-27 20:51:26 +000010423 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010424 {
glennrpd71e86a2011-02-24 01:28:37 +000010425 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10427 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010428
cristy16ea1392012-03-21 20:38:41 +000010429 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010430
cristy16ea1392012-03-21 20:38:41 +000010431 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010432 break;
glennrp0fe50b42010-11-16 03:52:51 +000010433
cristy3ed852e2009-09-05 21:47:34 +000010434 if (mng_info->IsPalette)
10435 {
cristy16ea1392012-03-21 20:38:41 +000010436 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10437 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010438 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10439 mng_info->write_png_depth &&
10440 mng_info->write_png_depth != old_bit_depth)
10441 {
10442 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010443 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010444 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010445 >> (8-old_bit_depth));
10446 }
10447 }
glennrp0fe50b42010-11-16 03:52:51 +000010448
cristy3ed852e2009-09-05 21:47:34 +000010449 else
10450 {
cristy16ea1392012-03-21 20:38:41 +000010451 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10452 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010453 }
glennrp0fe50b42010-11-16 03:52:51 +000010454
cristy3ed852e2009-09-05 21:47:34 +000010455 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010456 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010457 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010458 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010459
glennrp3b51f0e2010-11-27 18:14:08 +000010460 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10462 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010463
glennrpcf002022011-01-30 02:38:15 +000010464 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010465 }
10466 if (image->previous == (Image *) NULL)
10467 {
10468 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10469 if (status == MagickFalse)
10470 break;
10471 }
10472 }
10473 }
glennrp0fe50b42010-11-16 03:52:51 +000010474
glennrp8bb3a022010-12-13 20:40:04 +000010475 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010476 {
glennrp0fe50b42010-11-16 03:52:51 +000010477 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010478 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010479 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010480 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010481 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010482 {
cristy16ea1392012-03-21 20:38:41 +000010483 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010484 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010485
glennrp8bb3a022010-12-13 20:40:04 +000010486 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010487 {
glennrp8bb3a022010-12-13 20:40:04 +000010488
cristybb503372010-05-27 20:51:26 +000010489 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010490 {
cristy16ea1392012-03-21 20:38:41 +000010491 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010492
cristy16ea1392012-03-21 20:38:41 +000010493 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010494 break;
glennrp2cc891a2010-12-24 13:44:32 +000010495
glennrp5af765f2010-03-30 11:12:18 +000010496 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010497 {
glennrp8bb3a022010-12-13 20:40:04 +000010498 if (mng_info->IsPalette)
cristy16ea1392012-03-21 20:38:41 +000010499 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10500 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010501
glennrp8bb3a022010-12-13 20:40:04 +000010502 else
cristy16ea1392012-03-21 20:38:41 +000010503 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10504 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010505
glennrp3b51f0e2010-11-27 18:14:08 +000010506 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010508 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010509 }
glennrp2cc891a2010-12-24 13:44:32 +000010510
glennrp8bb3a022010-12-13 20:40:04 +000010511 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10512 {
10513 if (logging != MagickFalse && y == 0)
10514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10515 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010516
cristy16ea1392012-03-21 20:38:41 +000010517 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10518 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010519 }
glennrp2cc891a2010-12-24 13:44:32 +000010520
glennrp3b51f0e2010-11-27 18:14:08 +000010521 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010522 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010523 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010524
glennrpcf002022011-01-30 02:38:15 +000010525 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010526 }
glennrp2cc891a2010-12-24 13:44:32 +000010527
glennrp8bb3a022010-12-13 20:40:04 +000010528 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010529 {
glennrp8bb3a022010-12-13 20:40:04 +000010530 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10531 if (status == MagickFalse)
10532 break;
cristy3ed852e2009-09-05 21:47:34 +000010533 }
cristy3ed852e2009-09-05 21:47:34 +000010534 }
10535 }
glennrp8bb3a022010-12-13 20:40:04 +000010536
10537 else
10538 {
cristy16ea1392012-03-21 20:38:41 +000010539 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010540 *p;
10541
10542 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010543 {
glennrp8bb3a022010-12-13 20:40:04 +000010544 if ((image_depth > 8) || (mng_info->write_png24 ||
10545 mng_info->write_png32 ||
10546 (!mng_info->write_png8 && !mng_info->IsPalette)))
10547 {
10548 for (y=0; y < (ssize_t) image->rows; y++)
10549 {
cristy862a33c2012-05-17 22:49:37 +000010550 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010551
cristy16ea1392012-03-21 20:38:41 +000010552 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010553 break;
glennrp2cc891a2010-12-24 13:44:32 +000010554
glennrp8bb3a022010-12-13 20:40:04 +000010555 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10556 {
10557 if (image->storage_class == DirectClass)
cristy16ea1392012-03-21 20:38:41 +000010558 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10559 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010560
glennrp8bb3a022010-12-13 20:40:04 +000010561 else
cristy16ea1392012-03-21 20:38:41 +000010562 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10563 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010564 }
glennrp2cc891a2010-12-24 13:44:32 +000010565
glennrp8bb3a022010-12-13 20:40:04 +000010566 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10567 {
cristy16ea1392012-03-21 20:38:41 +000010568 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010569 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000010570 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010571
glennrp8bb3a022010-12-13 20:40:04 +000010572 if (logging != MagickFalse && y == 0)
10573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10574 " Writing GRAY_ALPHA PNG pixels (3)");
10575 }
glennrp2cc891a2010-12-24 13:44:32 +000010576
glennrp8bb3a022010-12-13 20:40:04 +000010577 else if (image_matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +000010578 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10579 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010580
glennrp8bb3a022010-12-13 20:40:04 +000010581 else
cristy16ea1392012-03-21 20:38:41 +000010582 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10583 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010584
glennrp8bb3a022010-12-13 20:40:04 +000010585 if (logging != MagickFalse && y == 0)
10586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10587 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010588
glennrpcf002022011-01-30 02:38:15 +000010589 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010590 }
10591 }
glennrp2cc891a2010-12-24 13:44:32 +000010592
glennrp8bb3a022010-12-13 20:40:04 +000010593 else
10594 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10595 mng_info->write_png32 ||
10596 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10597 {
10598 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10599 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10600 {
10601 if (logging != MagickFalse)
10602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10603 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010604
glennrp8bb3a022010-12-13 20:40:04 +000010605 quantum_info->depth=8;
10606 image_depth=8;
10607 }
glennrp2cc891a2010-12-24 13:44:32 +000010608
glennrp8bb3a022010-12-13 20:40:04 +000010609 for (y=0; y < (ssize_t) image->rows; y++)
10610 {
10611 if (logging != MagickFalse && y == 0)
10612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10613 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010614
cristy16ea1392012-03-21 20:38:41 +000010615 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010616
cristy16ea1392012-03-21 20:38:41 +000010617 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010618 break;
glennrp2cc891a2010-12-24 13:44:32 +000010619
glennrp8bb3a022010-12-13 20:40:04 +000010620 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010621 {
glennrp4bf89732011-03-21 13:48:28 +000010622 quantum_info->depth=image->depth;
10623
cristy16ea1392012-03-21 20:38:41 +000010624 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10625 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010626 }
glennrp2cc891a2010-12-24 13:44:32 +000010627
glennrp8bb3a022010-12-13 20:40:04 +000010628 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10629 {
10630 if (logging != MagickFalse && y == 0)
10631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10632 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010633
cristy16ea1392012-03-21 20:38:41 +000010634 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010635 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000010636 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010637 }
glennrp2cc891a2010-12-24 13:44:32 +000010638
glennrp8bb3a022010-12-13 20:40:04 +000010639 else
glennrp8bb3a022010-12-13 20:40:04 +000010640 {
cristy16ea1392012-03-21 20:38:41 +000010641 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10642 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010643
10644 if (logging != MagickFalse && y <= 2)
10645 {
10646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010647 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010648
10649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10650 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10651 (int)ping_pixels[0],(int)ping_pixels[1]);
10652 }
glennrp8bb3a022010-12-13 20:40:04 +000010653 }
glennrpcf002022011-01-30 02:38:15 +000010654 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010655 }
10656 }
glennrp2cc891a2010-12-24 13:44:32 +000010657
glennrp8bb3a022010-12-13 20:40:04 +000010658 if (image->previous == (Image *) NULL)
10659 {
10660 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10661 if (status == MagickFalse)
10662 break;
10663 }
cristy3ed852e2009-09-05 21:47:34 +000010664 }
glennrp8bb3a022010-12-13 20:40:04 +000010665 }
10666 }
10667
cristyb32b90a2009-09-07 21:45:48 +000010668 if (quantum_info != (QuantumInfo *) NULL)
10669 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010670
10671 if (logging != MagickFalse)
10672 {
10673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010674 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010675
cristy3ed852e2009-09-05 21:47:34 +000010676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010677 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010678
cristy3ed852e2009-09-05 21:47:34 +000010679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010680 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010681
cristy3ed852e2009-09-05 21:47:34 +000010682 if (mng_info->write_png_depth)
10683 {
10684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010685 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010686 }
glennrp0fe50b42010-11-16 03:52:51 +000010687
cristy3ed852e2009-09-05 21:47:34 +000010688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010689 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010690
cristy3ed852e2009-09-05 21:47:34 +000010691 if (mng_info->write_png_colortype)
10692 {
10693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010694 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010695 }
glennrp0fe50b42010-11-16 03:52:51 +000010696
cristy3ed852e2009-09-05 21:47:34 +000010697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010698 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010699
cristy3ed852e2009-09-05 21:47:34 +000010700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010701 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010702 }
10703 /*
glennrpa0ed0092011-04-18 16:36:29 +000010704 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010705 */
glennrp823b55c2011-03-14 18:46:46 +000010706 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010707 {
glennrp26f37912010-12-23 16:22:42 +000010708 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010709 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010710 while (property != (const char *) NULL)
10711 {
10712 png_textp
10713 text;
glennrp2cc891a2010-12-24 13:44:32 +000010714
cristy16ea1392012-03-21 20:38:41 +000010715 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010716
10717 /* Don't write any "png:" properties; those are just for "identify" */
10718 if (LocaleNCompare(property,"png:",4) != 0 &&
10719
10720 /* Suppress density and units if we wrote a pHYs chunk */
10721 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010722 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010723 LocaleCompare(property,"units") != 0) &&
10724
10725 /* Suppress the IM-generated Date:create and Date:modify */
10726 (ping_exclude_date == MagickFalse ||
10727 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010728 {
glennrpc70af4a2011-03-07 00:08:23 +000010729 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010730 {
glennrpc70af4a2011-03-07 00:08:23 +000010731 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10732 text[0].key=(char *) property;
10733 text[0].text=(char *) value;
10734 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010735
glennrpc70af4a2011-03-07 00:08:23 +000010736 if (ping_exclude_tEXt != MagickFalse)
10737 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10738
10739 else if (ping_exclude_zTXt != MagickFalse)
10740 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10741
10742 else
glennrp26f37912010-12-23 16:22:42 +000010743 {
glennrpc70af4a2011-03-07 00:08:23 +000010744 text[0].compression=image_info->compression == NoCompression ||
10745 (image_info->compression == UndefinedCompression &&
10746 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10747 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010748 }
glennrp2cc891a2010-12-24 13:44:32 +000010749
glennrpc70af4a2011-03-07 00:08:23 +000010750 if (logging != MagickFalse)
10751 {
10752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10753 " Setting up text chunk");
10754
10755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10756 " keyword: %s",text[0].key);
10757 }
10758
10759 png_set_text(ping,ping_info,text,1);
10760 png_free(ping,text);
10761 }
glennrp26f37912010-12-23 16:22:42 +000010762 }
10763 property=GetNextImageProperty(image);
10764 }
cristy3ed852e2009-09-05 21:47:34 +000010765 }
10766
10767 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010768 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010769
10770 if (logging != MagickFalse)
10771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10772 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010773
cristy3ed852e2009-09-05 21:47:34 +000010774 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010775
cristy3ed852e2009-09-05 21:47:34 +000010776 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10777 {
10778 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010779 (ping_width != mng_info->page.width) ||
10780 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010781 {
10782 unsigned char
10783 chunk[32];
10784
10785 /*
10786 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10787 */
10788 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10789 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010790 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010791 chunk[4]=4;
10792 chunk[5]=0; /* frame name separator (no name) */
10793 chunk[6]=1; /* flag for changing delay, for next frame only */
10794 chunk[7]=0; /* flag for changing frame timeout */
10795 chunk[8]=1; /* flag for changing frame clipping for next frame */
10796 chunk[9]=0; /* flag for changing frame sync_id */
10797 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10798 chunk[14]=0; /* clipping boundaries delta type */
10799 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10800 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010801 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010802 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10803 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010804 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010805 (void) WriteBlob(image,31,chunk);
10806 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10807 mng_info->old_framing_mode=4;
10808 mng_info->framing_mode=1;
10809 }
glennrp0fe50b42010-11-16 03:52:51 +000010810
cristy3ed852e2009-09-05 21:47:34 +000010811 else
10812 mng_info->framing_mode=3;
10813 }
10814 if (mng_info->write_mng && !mng_info->need_fram &&
10815 ((int) image->dispose == 3))
glennrpedaa0382012-04-12 14:16:21 +000010816 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
glennrp0fe50b42010-11-16 03:52:51 +000010817
cristy3ed852e2009-09-05 21:47:34 +000010818 /*
10819 Free PNG resources.
10820 */
glennrp5af765f2010-03-30 11:12:18 +000010821
cristy3ed852e2009-09-05 21:47:34 +000010822 png_destroy_write_struct(&ping,&ping_info);
10823
glennrpcf002022011-01-30 02:38:15 +000010824 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010825
cristy16ea1392012-03-21 20:38:41 +000010826 if (ping_have_blob != MagickFalse)
10827 (void) CloseBlob(image);
10828
10829 image_info=DestroyImageInfo(image_info);
10830 image=DestroyImage(image);
10831
glennrpb9cfe272010-12-21 15:08:06 +000010832 /* Store bit depth actually written */
10833 s[0]=(char) ping_bit_depth;
10834 s[1]='\0';
10835
cristy16ea1392012-03-21 20:38:41 +000010836 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010837
cristy3ed852e2009-09-05 21:47:34 +000010838 if (logging != MagickFalse)
10839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10840 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010841
glennrpedaa0382012-04-12 14:16:21 +000010842#ifdef PNG_SETJMP_NOT_THREAD_SAFE
10843 UnlockSemaphoreInfo(ping_semaphore);
10844#endif
10845
10846 /* } for navigation to beginning of SETJMP-protected block. Revert to
10847 * Throwing an Exception when an error occurs.
10848 */
10849
cristy3ed852e2009-09-05 21:47:34 +000010850 return(MagickTrue);
10851/* End write one PNG image */
glennrpedaa0382012-04-12 14:16:21 +000010852
cristy3ed852e2009-09-05 21:47:34 +000010853}
10854
10855/*
10856%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10857% %
10858% %
10859% %
10860% W r i t e P N G I m a g e %
10861% %
10862% %
10863% %
10864%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10865%
10866% WritePNGImage() writes a Portable Network Graphics (PNG) or
10867% Multiple-image Network Graphics (MNG) image file.
10868%
10869% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10870%
10871% The format of the WritePNGImage method is:
10872%
cristy16ea1392012-03-21 20:38:41 +000010873% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10874% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010875%
10876% A description of each parameter follows:
10877%
10878% o image_info: the image info.
10879%
10880% o image: The image.
10881%
cristy16ea1392012-03-21 20:38:41 +000010882% o exception: return any errors or warnings in this structure.
10883%
cristy3ed852e2009-09-05 21:47:34 +000010884% Returns MagickTrue on success, MagickFalse on failure.
10885%
10886% Communicating with the PNG encoder:
10887%
10888% While the datastream written is always in PNG format and normally would
10889% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000010890% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000010891%
glennrp5a39f372011-02-25 04:52:16 +000010892% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10893% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010894% is present, the tRNS chunk must only have values 0 and 255
10895% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010896% transparent). If other values are present they will be
10897% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010898% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010899% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10900% of any resulting fully-transparent pixels is changed to
10901% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010902%
10903% If you want better quantization or dithering of the colors
10904% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010905% PNG encoder. The pixels contain 8-bit indices even if
10906% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010907% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010908% PNG grayscale type might be slightly more efficient. Please
10909% note that writing to the PNG8 format may result in loss
10910% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010911%
10912% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10913% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010914% one of the colors as transparent. The only loss incurred
10915% is reduction of sample depth to 8. If the image has more
10916% than one transparent color, has semitransparent pixels, or
10917% has an opaque pixel with the same RGB components as the
10918% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010919%
10920% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10921% transparency is permitted, i.e., the alpha sample for
10922% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010923% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010924% The only loss in data is the reduction of the sample depth
10925% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010926%
10927% o -define: For more precise control of the PNG output, you can use the
10928% Image options "png:bit-depth" and "png:color-type". These
10929% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010930% from the application programming interfaces. The options
10931% are case-independent and are converted to lowercase before
10932% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010933%
10934% png:color-type can be 0, 2, 3, 4, or 6.
10935%
10936% When png:color-type is 0 (Grayscale), png:bit-depth can
10937% be 1, 2, 4, 8, or 16.
10938%
10939% When png:color-type is 2 (RGB), png:bit-depth can
10940% be 8 or 16.
10941%
10942% When png:color-type is 3 (Indexed), png:bit-depth can
10943% be 1, 2, 4, or 8. This refers to the number of bits
10944% used to store the index. The color samples always have
10945% bit-depth 8 in indexed PNG files.
10946%
10947% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10948% png:bit-depth can be 8 or 16.
10949%
glennrp5a39f372011-02-25 04:52:16 +000010950% If the image cannot be written without loss with the requested bit-depth
10951% and color-type, a PNG file will not be written, and the encoder will
10952% return MagickFalse.
10953%
cristy3ed852e2009-09-05 21:47:34 +000010954% Since image encoders should not be responsible for the "heavy lifting",
10955% the user should make sure that ImageMagick has already reduced the
10956% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010957% transparency prior to attempting to write the image with depth, color,
cristy16ea1392012-03-21 20:38:41 +000010958% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010959%
cristy3ed852e2009-09-05 21:47:34 +000010960% Note that another definition, "png:bit-depth-written" exists, but it
10961% is not intended for external use. It is only used internally by the
10962% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10963%
10964% It is possible to request that the PNG encoder write previously-formatted
10965% ancillary chunks in the output PNG file, using the "-profile" commandline
10966% option as shown below or by setting the profile via a programming
10967% interface:
10968%
10969% -profile PNG-chunk-x:<file>
10970%
10971% where x is a location flag and <file> is a file containing the chunk
10972% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010973% This encoder will compute the chunk length and CRC, so those must not
10974% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010975%
10976% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10977% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10978% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010979% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010980%
glennrpbb8a7332010-11-13 15:17:35 +000010981% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010982%
glennrp3241bd02010-12-12 04:36:28 +000010983% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010984%
glennrpd6afd542010-11-19 01:53:05 +000010985% o 32-bit depth is reduced to 16.
10986% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10987% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010988% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010989% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010990% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010991% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10992% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000010993% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000010994% o If matte channel is present but only one transparent color is
10995% present, RGB+tRNS is written instead of RGBA
10996% o Opaque matte channel is removed (or added, if color-type 4 or 6
10997% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010998%
cristy3ed852e2009-09-05 21:47:34 +000010999%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11000*/
11001static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy16ea1392012-03-21 20:38:41 +000011002 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011003{
11004 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011005 excluding,
11006 logging,
11007 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011008 status;
11009
11010 MngInfo
11011 *mng_info;
11012
11013 const char
11014 *value;
11015
11016 int
glennrp21f0e622011-01-07 16:20:57 +000011017 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000011018 source;
11019
cristy3ed852e2009-09-05 21:47:34 +000011020 /*
11021 Open image file.
11022 */
11023 assert(image_info != (const ImageInfo *) NULL);
11024 assert(image_info->signature == MagickSignature);
11025 assert(image != (Image *) NULL);
11026 assert(image->signature == MagickSignature);
11027 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011028 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011029 /*
11030 Allocate a MngInfo structure.
11031 */
11032 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011033 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000011034
cristy3ed852e2009-09-05 21:47:34 +000011035 if (mng_info == (MngInfo *) NULL)
11036 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011037
cristy3ed852e2009-09-05 21:47:34 +000011038 /*
11039 Initialize members of the MngInfo structure.
11040 */
11041 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11042 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000011043 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011044 have_mng_structure=MagickTrue;
11045
11046 /* See if user has requested a specific PNG subformat */
11047
11048 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11049 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11050 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11051
glennrpb381a262012-02-11 17:49:49 +000011052 value=GetImageOption(image_info,"png:format");
11053
11054 if (value != (char *) NULL)
11055 {
11056 if (LocaleCompare(value,"png8") == 0)
11057 {
11058 mng_info->write_png8 = MagickTrue;
11059 mng_info->write_png24 = MagickFalse;
11060 mng_info->write_png32 = MagickFalse;
11061 }
11062
11063 else if (LocaleCompare(value,"png24") == 0)
11064 {
11065 mng_info->write_png8 = MagickFalse;
11066 mng_info->write_png24 = MagickTrue;
11067 mng_info->write_png32 = MagickFalse;
11068 }
11069
11070 else if (LocaleCompare(value,"png32") == 0)
11071 {
11072 mng_info->write_png8 = MagickFalse;
11073 mng_info->write_png24 = MagickFalse;
11074 mng_info->write_png32 = MagickTrue;
11075 }
11076 }
cristy3ed852e2009-09-05 21:47:34 +000011077 if (mng_info->write_png8)
11078 {
glennrp9c1eb072010-06-06 22:19:15 +000011079 mng_info->write_png_colortype = /* 3 */ 4;
11080 mng_info->write_png_depth = 8;
11081 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011082 }
11083
11084 if (mng_info->write_png24)
11085 {
glennrp9c1eb072010-06-06 22:19:15 +000011086 mng_info->write_png_colortype = /* 2 */ 3;
11087 mng_info->write_png_depth = 8;
11088 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011089
glennrp9c1eb072010-06-06 22:19:15 +000011090 if (image->matte == MagickTrue)
cristy16ea1392012-03-21 20:38:41 +000011091 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011092
glennrp9c1eb072010-06-06 22:19:15 +000011093 else
cristy16ea1392012-03-21 20:38:41 +000011094 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011095
cristy16ea1392012-03-21 20:38:41 +000011096 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011097 }
11098
11099 if (mng_info->write_png32)
11100 {
glennrp9c1eb072010-06-06 22:19:15 +000011101 mng_info->write_png_colortype = /* 6 */ 7;
11102 mng_info->write_png_depth = 8;
11103 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011104
glennrp9c1eb072010-06-06 22:19:15 +000011105 if (image->matte == MagickTrue)
cristy16ea1392012-03-21 20:38:41 +000011106 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011107
glennrp9c1eb072010-06-06 22:19:15 +000011108 else
cristy16ea1392012-03-21 20:38:41 +000011109 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011110
cristy16ea1392012-03-21 20:38:41 +000011111 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011112 }
11113
11114 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011115
cristy3ed852e2009-09-05 21:47:34 +000011116 if (value != (char *) NULL)
11117 {
11118 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011119 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011120
cristy3ed852e2009-09-05 21:47:34 +000011121 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011122 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011123
cristy3ed852e2009-09-05 21:47:34 +000011124 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011125 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011126
cristy3ed852e2009-09-05 21:47:34 +000011127 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011128 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011129
cristy3ed852e2009-09-05 21:47:34 +000011130 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011131 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011132
glennrpbb8a7332010-11-13 15:17:35 +000011133 else
cristy16ea1392012-03-21 20:38:41 +000011134 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011135 GetMagickModule(),CoderWarning,
11136 "ignoring invalid defined png:bit-depth",
11137 "=%s",value);
11138
cristy3ed852e2009-09-05 21:47:34 +000011139 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011141 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011142 }
glennrp0fe50b42010-11-16 03:52:51 +000011143
cristy3ed852e2009-09-05 21:47:34 +000011144 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011145
cristy3ed852e2009-09-05 21:47:34 +000011146 if (value != (char *) NULL)
11147 {
11148 /* We must store colortype+1 because 0 is a valid colortype */
11149 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011150 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011151
cristy16ea1392012-03-21 20:38:41 +000011152 else if (LocaleCompare(value,"1") == 0)
11153 mng_info->write_png_colortype = 2;
11154
cristy3ed852e2009-09-05 21:47:34 +000011155 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011156 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011157
cristy3ed852e2009-09-05 21:47:34 +000011158 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011159 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011160
cristy3ed852e2009-09-05 21:47:34 +000011161 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011162 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011163
cristy3ed852e2009-09-05 21:47:34 +000011164 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011165 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011166
glennrpbb8a7332010-11-13 15:17:35 +000011167 else
cristy16ea1392012-03-21 20:38:41 +000011168 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011169 GetMagickModule(),CoderWarning,
11170 "ignoring invalid defined png:color-type",
11171 "=%s",value);
11172
cristy3ed852e2009-09-05 21:47:34 +000011173 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011175 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011176 }
11177
glennrp0e8ea192010-12-24 18:00:33 +000011178 /* Check for chunks to be excluded:
11179 *
glennrp0dff56c2011-01-29 19:10:02 +000011180 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011181 * listed in the "unused_chunks" array, above.
11182 *
cristy5d6fc9c2011-12-27 03:10:42 +000011183 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011184 * define (in the image properties or in the image artifacts)
11185 * or via a mng_info member. For convenience, in addition
11186 * to or instead of a comma-separated list of chunks, the
11187 * "exclude-chunk" string can be simply "all" or "none".
11188 *
11189 * The exclude-chunk define takes priority over the mng_info.
11190 *
cristy5d6fc9c2011-12-27 03:10:42 +000011191 * A "png:include-chunk" define takes priority over both the
11192 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011193 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011194 * well as a comma-separated list. Chunks that are unknown to
11195 * ImageMagick are always excluded, regardless of their "copy-safe"
11196 * status according to the PNG specification, and even if they
glennrpaa192b12012-01-17 21:35:21 +000011197 * appear in the "include-chunk" list. Such defines appearing among
11198 * the image options take priority over those found among the image
11199 * artifacts.
glennrp0e8ea192010-12-24 18:00:33 +000011200 *
11201 * Finally, all chunks listed in the "unused_chunks" array are
11202 * automatically excluded, regardless of the other instructions
11203 * or lack thereof.
11204 *
11205 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11206 * will not be written and the gAMA chunk will only be written if it
11207 * is not between .45 and .46, or approximately (1.0/2.2).
11208 *
11209 * If you exclude tRNS and the image has transparency, the colortype
11210 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11211 *
11212 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011213 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011214 */
11215
glennrp26f37912010-12-23 16:22:42 +000011216 mng_info->ping_exclude_bKGD=MagickFalse;
11217 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011218 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011219 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11220 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011221 mng_info->ping_exclude_iCCP=MagickFalse;
11222 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11223 mng_info->ping_exclude_oFFs=MagickFalse;
11224 mng_info->ping_exclude_pHYs=MagickFalse;
11225 mng_info->ping_exclude_sRGB=MagickFalse;
11226 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011227 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011228 mng_info->ping_exclude_vpAg=MagickFalse;
11229 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11230 mng_info->ping_exclude_zTXt=MagickFalse;
11231
glennrp8d3d6e52011-04-19 04:39:51 +000011232 mng_info->ping_preserve_colormap=MagickFalse;
11233
11234 value=GetImageArtifact(image,"png:preserve-colormap");
11235 if (value == NULL)
11236 value=GetImageOption(image_info,"png:preserve-colormap");
11237 if (value != NULL)
11238 mng_info->ping_preserve_colormap=MagickTrue;
11239
glennrp18682582011-06-30 18:11:47 +000011240 /* Thes compression-level, compression-strategy, and compression-filter
11241 * defines take precedence over values from the -quality option.
11242 */
11243 value=GetImageArtifact(image,"png:compression-level");
11244 if (value == NULL)
11245 value=GetImageOption(image_info,"png:compression-level");
11246 if (value != NULL)
11247 {
glennrp18682582011-06-30 18:11:47 +000011248 /* We have to add 1 to everything because 0 is a valid input,
11249 * and we want to use 0 (the default) to mean undefined.
11250 */
11251 if (LocaleCompare(value,"0") == 0)
11252 mng_info->write_png_compression_level = 1;
11253
glennrp0ffb95c2012-01-30 21:16:22 +000011254 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011255 mng_info->write_png_compression_level = 2;
11256
11257 else if (LocaleCompare(value,"2") == 0)
11258 mng_info->write_png_compression_level = 3;
11259
11260 else if (LocaleCompare(value,"3") == 0)
11261 mng_info->write_png_compression_level = 4;
11262
11263 else if (LocaleCompare(value,"4") == 0)
11264 mng_info->write_png_compression_level = 5;
11265
11266 else if (LocaleCompare(value,"5") == 0)
11267 mng_info->write_png_compression_level = 6;
11268
11269 else if (LocaleCompare(value,"6") == 0)
11270 mng_info->write_png_compression_level = 7;
11271
11272 else if (LocaleCompare(value,"7") == 0)
11273 mng_info->write_png_compression_level = 8;
11274
11275 else if (LocaleCompare(value,"8") == 0)
11276 mng_info->write_png_compression_level = 9;
11277
11278 else if (LocaleCompare(value,"9") == 0)
11279 mng_info->write_png_compression_level = 10;
11280
11281 else
cristy16ea1392012-03-21 20:38:41 +000011282 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011283 GetMagickModule(),CoderWarning,
11284 "ignoring invalid defined png:compression-level",
11285 "=%s",value);
11286 }
11287
11288 value=GetImageArtifact(image,"png:compression-strategy");
11289 if (value == NULL)
11290 value=GetImageOption(image_info,"png:compression-strategy");
11291 if (value != NULL)
11292 {
11293
11294 if (LocaleCompare(value,"0") == 0)
11295 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11296
11297 else if (LocaleCompare(value,"1") == 0)
11298 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11299
11300 else if (LocaleCompare(value,"2") == 0)
11301 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11302
11303 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011304#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011305 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011306#else
11307 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11308#endif
glennrp18682582011-06-30 18:11:47 +000011309
11310 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011311#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011312 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011313#else
11314 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11315#endif
glennrp18682582011-06-30 18:11:47 +000011316
11317 else
cristy16ea1392012-03-21 20:38:41 +000011318 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011319 GetMagickModule(),CoderWarning,
11320 "ignoring invalid defined png:compression-strategy",
11321 "=%s",value);
11322 }
11323
11324 value=GetImageArtifact(image,"png:compression-filter");
11325 if (value == NULL)
11326 value=GetImageOption(image_info,"png:compression-filter");
11327 if (value != NULL)
11328 {
11329
11330 /* To do: combinations of filters allowed by libpng
11331 * masks 0x08 through 0xf8
11332 *
11333 * Implement this as a comma-separated list of 0,1,2,3,4,5
11334 * where 5 is a special case meaning PNG_ALL_FILTERS.
11335 */
11336
11337 if (LocaleCompare(value,"0") == 0)
11338 mng_info->write_png_compression_filter = 1;
11339
11340 if (LocaleCompare(value,"1") == 0)
11341 mng_info->write_png_compression_filter = 2;
11342
11343 else if (LocaleCompare(value,"2") == 0)
11344 mng_info->write_png_compression_filter = 3;
11345
11346 else if (LocaleCompare(value,"3") == 0)
11347 mng_info->write_png_compression_filter = 4;
11348
11349 else if (LocaleCompare(value,"4") == 0)
11350 mng_info->write_png_compression_filter = 5;
11351
11352 else if (LocaleCompare(value,"5") == 0)
11353 mng_info->write_png_compression_filter = 6;
11354
glennrp18682582011-06-30 18:11:47 +000011355 else
cristy16ea1392012-03-21 20:38:41 +000011356 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011357 GetMagickModule(),CoderWarning,
11358 "ignoring invalid defined png:compression-filter",
11359 "=%s",value);
11360 }
11361
glennrp03812ae2010-12-24 01:31:34 +000011362 excluding=MagickFalse;
11363
glennrp5c7cf4e2010-12-24 00:30:00 +000011364 for (source=0; source<1; source++)
11365 {
11366 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011367 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011368 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011369
11370 if (value == NULL)
11371 value=GetImageArtifact(image,"png:exclude-chunks");
11372 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011373 else
glennrpacba0042010-12-24 14:27:26 +000011374 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011375 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011376
glennrpacba0042010-12-24 14:27:26 +000011377 if (value == NULL)
11378 value=GetImageOption(image_info,"png:exclude-chunks");
11379 }
11380
glennrp03812ae2010-12-24 01:31:34 +000011381 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011382 {
glennrp03812ae2010-12-24 01:31:34 +000011383
11384 size_t
11385 last;
11386
11387 excluding=MagickTrue;
11388
11389 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011390 {
11391 if (source == 0)
11392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11393 " png:exclude-chunk=%s found in image artifacts.\n", value);
11394 else
11395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11396 " png:exclude-chunk=%s found in image properties.\n", value);
11397 }
glennrp03812ae2010-12-24 01:31:34 +000011398
11399 last=strlen(value);
11400
11401 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011402 {
glennrp03812ae2010-12-24 01:31:34 +000011403
11404 if (LocaleNCompare(value+i,"all",3) == 0)
11405 {
11406 mng_info->ping_exclude_bKGD=MagickTrue;
11407 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011408 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011409 mng_info->ping_exclude_EXIF=MagickTrue;
11410 mng_info->ping_exclude_gAMA=MagickTrue;
11411 mng_info->ping_exclude_iCCP=MagickTrue;
11412 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11413 mng_info->ping_exclude_oFFs=MagickTrue;
11414 mng_info->ping_exclude_pHYs=MagickTrue;
11415 mng_info->ping_exclude_sRGB=MagickTrue;
11416 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011417 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011418 mng_info->ping_exclude_vpAg=MagickTrue;
11419 mng_info->ping_exclude_zCCP=MagickTrue;
11420 mng_info->ping_exclude_zTXt=MagickTrue;
11421 i--;
11422 }
glennrp2cc891a2010-12-24 13:44:32 +000011423
glennrp03812ae2010-12-24 01:31:34 +000011424 if (LocaleNCompare(value+i,"none",4) == 0)
11425 {
11426 mng_info->ping_exclude_bKGD=MagickFalse;
11427 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011428 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011429 mng_info->ping_exclude_EXIF=MagickFalse;
11430 mng_info->ping_exclude_gAMA=MagickFalse;
11431 mng_info->ping_exclude_iCCP=MagickFalse;
11432 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11433 mng_info->ping_exclude_oFFs=MagickFalse;
11434 mng_info->ping_exclude_pHYs=MagickFalse;
11435 mng_info->ping_exclude_sRGB=MagickFalse;
11436 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011437 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011438 mng_info->ping_exclude_vpAg=MagickFalse;
11439 mng_info->ping_exclude_zCCP=MagickFalse;
11440 mng_info->ping_exclude_zTXt=MagickFalse;
11441 }
glennrp2cc891a2010-12-24 13:44:32 +000011442
glennrp03812ae2010-12-24 01:31:34 +000011443 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11444 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011445
glennrp03812ae2010-12-24 01:31:34 +000011446 if (LocaleNCompare(value+i,"chrm",4) == 0)
11447 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011448
glennrpa0ed0092011-04-18 16:36:29 +000011449 if (LocaleNCompare(value+i,"date",4) == 0)
11450 mng_info->ping_exclude_date=MagickTrue;
11451
glennrp03812ae2010-12-24 01:31:34 +000011452 if (LocaleNCompare(value+i,"exif",4) == 0)
11453 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011454
glennrp03812ae2010-12-24 01:31:34 +000011455 if (LocaleNCompare(value+i,"gama",4) == 0)
11456 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011457
glennrp03812ae2010-12-24 01:31:34 +000011458 if (LocaleNCompare(value+i,"iccp",4) == 0)
11459 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011460
glennrp03812ae2010-12-24 01:31:34 +000011461 /*
11462 if (LocaleNCompare(value+i,"itxt",4) == 0)
11463 mng_info->ping_exclude_iTXt=MagickTrue;
11464 */
glennrp2cc891a2010-12-24 13:44:32 +000011465
glennrp03812ae2010-12-24 01:31:34 +000011466 if (LocaleNCompare(value+i,"gama",4) == 0)
11467 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011468
glennrp03812ae2010-12-24 01:31:34 +000011469 if (LocaleNCompare(value+i,"offs",4) == 0)
11470 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011471
glennrp03812ae2010-12-24 01:31:34 +000011472 if (LocaleNCompare(value+i,"phys",4) == 0)
11473 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011474
glennrpa1e3b7b2010-12-24 16:37:33 +000011475 if (LocaleNCompare(value+i,"srgb",4) == 0)
11476 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011477
glennrp03812ae2010-12-24 01:31:34 +000011478 if (LocaleNCompare(value+i,"text",4) == 0)
11479 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011480
glennrpa1e3b7b2010-12-24 16:37:33 +000011481 if (LocaleNCompare(value+i,"trns",4) == 0)
11482 mng_info->ping_exclude_tRNS=MagickTrue;
11483
glennrp03812ae2010-12-24 01:31:34 +000011484 if (LocaleNCompare(value+i,"vpag",4) == 0)
11485 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011486
glennrp03812ae2010-12-24 01:31:34 +000011487 if (LocaleNCompare(value+i,"zccp",4) == 0)
11488 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011489
glennrp03812ae2010-12-24 01:31:34 +000011490 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11491 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011492
glennrp03812ae2010-12-24 01:31:34 +000011493 }
glennrpce91ed52010-12-23 22:37:49 +000011494 }
glennrp26f37912010-12-23 16:22:42 +000011495 }
11496
glennrp5c7cf4e2010-12-24 00:30:00 +000011497 for (source=0; source<1; source++)
11498 {
11499 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011500 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011501 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011502
11503 if (value == NULL)
11504 value=GetImageArtifact(image,"png:include-chunks");
11505 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011506 else
glennrpacba0042010-12-24 14:27:26 +000011507 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011508 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011509
glennrpacba0042010-12-24 14:27:26 +000011510 if (value == NULL)
11511 value=GetImageOption(image_info,"png:include-chunks");
11512 }
11513
glennrp03812ae2010-12-24 01:31:34 +000011514 if (value != NULL)
11515 {
11516 size_t
11517 last;
glennrp26f37912010-12-23 16:22:42 +000011518
glennrp03812ae2010-12-24 01:31:34 +000011519 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011520
glennrp03812ae2010-12-24 01:31:34 +000011521 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011522 {
11523 if (source == 0)
11524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11525 " png:include-chunk=%s found in image artifacts.\n", value);
11526 else
11527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11528 " png:include-chunk=%s found in image properties.\n", value);
11529 }
glennrp03812ae2010-12-24 01:31:34 +000011530
11531 last=strlen(value);
11532
11533 for (i=0; i<(int) last; i+=5)
11534 {
11535 if (LocaleNCompare(value+i,"all",3) == 0)
11536 {
11537 mng_info->ping_exclude_bKGD=MagickFalse;
11538 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011539 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011540 mng_info->ping_exclude_EXIF=MagickFalse;
11541 mng_info->ping_exclude_gAMA=MagickFalse;
11542 mng_info->ping_exclude_iCCP=MagickFalse;
11543 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11544 mng_info->ping_exclude_oFFs=MagickFalse;
11545 mng_info->ping_exclude_pHYs=MagickFalse;
11546 mng_info->ping_exclude_sRGB=MagickFalse;
11547 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011548 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011549 mng_info->ping_exclude_vpAg=MagickFalse;
11550 mng_info->ping_exclude_zCCP=MagickFalse;
11551 mng_info->ping_exclude_zTXt=MagickFalse;
11552 i--;
11553 }
glennrp2cc891a2010-12-24 13:44:32 +000011554
glennrp03812ae2010-12-24 01:31:34 +000011555 if (LocaleNCompare(value+i,"none",4) == 0)
11556 {
11557 mng_info->ping_exclude_bKGD=MagickTrue;
11558 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011559 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011560 mng_info->ping_exclude_EXIF=MagickTrue;
11561 mng_info->ping_exclude_gAMA=MagickTrue;
11562 mng_info->ping_exclude_iCCP=MagickTrue;
11563 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11564 mng_info->ping_exclude_oFFs=MagickTrue;
11565 mng_info->ping_exclude_pHYs=MagickTrue;
11566 mng_info->ping_exclude_sRGB=MagickTrue;
11567 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011568 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011569 mng_info->ping_exclude_vpAg=MagickTrue;
11570 mng_info->ping_exclude_zCCP=MagickTrue;
11571 mng_info->ping_exclude_zTXt=MagickTrue;
11572 }
glennrp2cc891a2010-12-24 13:44:32 +000011573
glennrp03812ae2010-12-24 01:31:34 +000011574 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11575 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011576
glennrp03812ae2010-12-24 01:31:34 +000011577 if (LocaleNCompare(value+i,"chrm",4) == 0)
11578 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011579
glennrpa0ed0092011-04-18 16:36:29 +000011580 if (LocaleNCompare(value+i,"date",4) == 0)
11581 mng_info->ping_exclude_date=MagickFalse;
11582
glennrp03812ae2010-12-24 01:31:34 +000011583 if (LocaleNCompare(value+i,"exif",4) == 0)
11584 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011585
glennrp03812ae2010-12-24 01:31:34 +000011586 if (LocaleNCompare(value+i,"gama",4) == 0)
11587 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011588
glennrp03812ae2010-12-24 01:31:34 +000011589 if (LocaleNCompare(value+i,"iccp",4) == 0)
11590 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011591
glennrp03812ae2010-12-24 01:31:34 +000011592 /*
11593 if (LocaleNCompare(value+i,"itxt",4) == 0)
11594 mng_info->ping_exclude_iTXt=MagickFalse;
11595 */
glennrp2cc891a2010-12-24 13:44:32 +000011596
glennrp03812ae2010-12-24 01:31:34 +000011597 if (LocaleNCompare(value+i,"gama",4) == 0)
11598 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011599
glennrp03812ae2010-12-24 01:31:34 +000011600 if (LocaleNCompare(value+i,"offs",4) == 0)
11601 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011602
glennrp03812ae2010-12-24 01:31:34 +000011603 if (LocaleNCompare(value+i,"phys",4) == 0)
11604 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011605
glennrpa1e3b7b2010-12-24 16:37:33 +000011606 if (LocaleNCompare(value+i,"srgb",4) == 0)
11607 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011608
glennrp03812ae2010-12-24 01:31:34 +000011609 if (LocaleNCompare(value+i,"text",4) == 0)
11610 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011611
glennrpa1e3b7b2010-12-24 16:37:33 +000011612 if (LocaleNCompare(value+i,"trns",4) == 0)
11613 mng_info->ping_exclude_tRNS=MagickFalse;
11614
glennrp03812ae2010-12-24 01:31:34 +000011615 if (LocaleNCompare(value+i,"vpag",4) == 0)
11616 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011617
glennrp03812ae2010-12-24 01:31:34 +000011618 if (LocaleNCompare(value+i,"zccp",4) == 0)
11619 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011620
glennrp03812ae2010-12-24 01:31:34 +000011621 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11622 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011623
glennrp03812ae2010-12-24 01:31:34 +000011624 }
glennrpce91ed52010-12-23 22:37:49 +000011625 }
glennrp26f37912010-12-23 16:22:42 +000011626 }
11627
glennrp03812ae2010-12-24 01:31:34 +000011628 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011629 {
11630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011631 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011632 if (mng_info->ping_exclude_bKGD != MagickFalse)
11633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11634 " bKGD");
11635 if (mng_info->ping_exclude_cHRM != MagickFalse)
11636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11637 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011638 if (mng_info->ping_exclude_date != MagickFalse)
11639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11640 " date");
glennrp26f37912010-12-23 16:22:42 +000011641 if (mng_info->ping_exclude_EXIF != MagickFalse)
11642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11643 " EXIF");
11644 if (mng_info->ping_exclude_gAMA != MagickFalse)
11645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11646 " gAMA");
11647 if (mng_info->ping_exclude_iCCP != MagickFalse)
11648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11649 " iCCP");
11650/*
11651 if (mng_info->ping_exclude_iTXt != MagickFalse)
11652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11653 " iTXt");
11654*/
11655 if (mng_info->ping_exclude_oFFs != MagickFalse)
11656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11657 " oFFs");
11658 if (mng_info->ping_exclude_pHYs != MagickFalse)
11659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11660 " pHYs");
11661 if (mng_info->ping_exclude_sRGB != MagickFalse)
11662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11663 " sRGB");
11664 if (mng_info->ping_exclude_tEXt != MagickFalse)
11665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11666 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011667 if (mng_info->ping_exclude_tRNS != MagickFalse)
11668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11669 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011670 if (mng_info->ping_exclude_vpAg != MagickFalse)
11671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11672 " vpAg");
11673 if (mng_info->ping_exclude_zCCP != MagickFalse)
11674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11675 " zCCP");
11676 if (mng_info->ping_exclude_zTXt != MagickFalse)
11677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11678 " zTXt");
11679 }
11680
glennrpb9cfe272010-12-21 15:08:06 +000011681 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011682
cristy16ea1392012-03-21 20:38:41 +000011683 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011684
11685 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011686
cristy3ed852e2009-09-05 21:47:34 +000011687 if (logging != MagickFalse)
11688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011689
cristy3ed852e2009-09-05 21:47:34 +000011690 return(status);
11691}
11692
11693#if defined(JNG_SUPPORTED)
11694
11695/* Write one JNG image */
11696static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +000011697 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011698{
11699 Image
11700 *jpeg_image;
11701
11702 ImageInfo
11703 *jpeg_image_info;
11704
11705 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011706 logging,
cristy3ed852e2009-09-05 21:47:34 +000011707 status;
11708
11709 size_t
11710 length;
11711
11712 unsigned char
11713 *blob,
11714 chunk[80],
11715 *p;
11716
11717 unsigned int
11718 jng_alpha_compression_method,
11719 jng_alpha_sample_depth,
11720 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011721 transparent;
11722
cristybb503372010-05-27 20:51:26 +000011723 size_t
glennrp59575fa2011-12-31 21:31:39 +000011724 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000011725 jng_quality;
11726
11727 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011728 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011729
11730 blob=(unsigned char *) NULL;
11731 jpeg_image=(Image *) NULL;
11732 jpeg_image_info=(ImageInfo *) NULL;
11733
11734 status=MagickTrue;
11735 transparent=image_info->type==GrayscaleMatteType ||
glennrp59575fa2011-12-31 21:31:39 +000011736 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +000011737
glennrp59575fa2011-12-31 21:31:39 +000011738 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11739
11740 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11741
glennrp750105b2012-04-25 16:20:45 +000011742 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
glennrp59575fa2011-12-31 21:31:39 +000011743 image_info->quality;
11744
11745 if (jng_alpha_quality >= 1000)
11746 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000011747
11748 if (transparent)
11749 {
11750 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011751
cristy3ed852e2009-09-05 21:47:34 +000011752 /* Create JPEG blob, image, and image_info */
11753 if (logging != MagickFalse)
11754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +000011755 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011756
cristy3ed852e2009-09-05 21:47:34 +000011757 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011758
cristy3ed852e2009-09-05 21:47:34 +000011759 if (jpeg_image_info == (ImageInfo *) NULL)
11760 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011761
cristy3ed852e2009-09-05 21:47:34 +000011762 if (logging != MagickFalse)
11763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11764 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011765
cristy16ea1392012-03-21 20:38:41 +000011766 jpeg_image=SeparateImage(image,AlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +000011767 if (jpeg_image == (Image *) NULL)
11768 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11769 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000011770 jpeg_image->matte=MagickFalse;
glennrp8f77fdc2012-03-21 15:16:37 +000011771 jpeg_image->quality=jng_alpha_quality;
cristy16ea1392012-03-21 20:38:41 +000011772 jpeg_image_info->type=GrayscaleType;
11773 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011774 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011775 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011776 "%s",jpeg_image->filename);
11777 }
glennrp59575fa2011-12-31 21:31:39 +000011778 else
11779 {
11780 jng_alpha_compression_method=0;
11781 jng_color_type=10;
11782 jng_alpha_sample_depth=0;
11783 }
cristy3ed852e2009-09-05 21:47:34 +000011784
11785 /* To do: check bit depth of PNG alpha channel */
11786
11787 /* Check if image is grayscale. */
11788 if (image_info->type != TrueColorMatteType && image_info->type !=
cristy16ea1392012-03-21 20:38:41 +000011789 TrueColorType && ImageIsGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011790 jng_color_type-=2;
11791
glennrp59575fa2011-12-31 21:31:39 +000011792 if (logging != MagickFalse)
11793 {
11794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11795 " JNG Quality = %d",(int) jng_quality);
11796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11797 " JNG Color Type = %d",jng_color_type);
11798 if (transparent)
11799 {
11800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11801 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11803 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11805 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11806 }
11807 }
11808
cristy3ed852e2009-09-05 21:47:34 +000011809 if (transparent)
11810 {
11811 if (jng_alpha_compression_method==0)
11812 {
11813 const char
11814 *value;
11815
cristy16ea1392012-03-21 20:38:41 +000011816 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011817 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000011818 exception);
cristy3ed852e2009-09-05 21:47:34 +000011819 if (logging != MagickFalse)
11820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11821 " Creating PNG blob.");
11822 length=0;
11823
11824 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11825 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11826 jpeg_image_info->interlace=NoInterlace;
11827
glennrpcc5d45b2012-01-06 04:06:10 +000011828 /* Exclude all ancillary chunks */
11829 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11830
cristy3ed852e2009-09-05 21:47:34 +000011831 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000011832 exception);
cristy3ed852e2009-09-05 21:47:34 +000011833
11834 /* Retrieve sample depth used */
cristy16ea1392012-03-21 20:38:41 +000011835 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011836 if (value != (char *) NULL)
11837 jng_alpha_sample_depth= (unsigned int) value[0];
11838 }
11839 else
11840 {
cristy16ea1392012-03-21 20:38:41 +000011841 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011842
11843 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000011844 exception);
cristy3ed852e2009-09-05 21:47:34 +000011845
11846 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11847 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11848 jpeg_image_info->interlace=NoInterlace;
11849 if (logging != MagickFalse)
11850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11851 " Creating blob.");
11852 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000011853 exception);
cristy3ed852e2009-09-05 21:47:34 +000011854 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011855
cristy3ed852e2009-09-05 21:47:34 +000011856 if (logging != MagickFalse)
11857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011858 " Successfully read jpeg_image into a blob, length=%.20g.",
11859 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011860
11861 }
11862 /* Destroy JPEG image and image_info */
11863 jpeg_image=DestroyImage(jpeg_image);
11864 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11865 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11866 }
11867
11868 /* Write JHDR chunk */
11869 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11870 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011871 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011872 PNGLong(chunk+4,(png_uint_32) image->columns);
11873 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011874 chunk[12]=jng_color_type;
11875 chunk[13]=8; /* sample depth */
11876 chunk[14]=8; /*jng_image_compression_method */
11877 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11878 chunk[16]=jng_alpha_sample_depth;
11879 chunk[17]=jng_alpha_compression_method;
11880 chunk[18]=0; /*jng_alpha_filter_method */
11881 chunk[19]=0; /*jng_alpha_interlace_method */
11882 (void) WriteBlob(image,20,chunk);
11883 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11884 if (logging != MagickFalse)
11885 {
11886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011887 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011888
cristy3ed852e2009-09-05 21:47:34 +000011889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011890 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011891
cristy3ed852e2009-09-05 21:47:34 +000011892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11893 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011894
cristy3ed852e2009-09-05 21:47:34 +000011895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11896 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011897
cristy3ed852e2009-09-05 21:47:34 +000011898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11899 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011900
cristy3ed852e2009-09-05 21:47:34 +000011901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11902 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011903
cristy3ed852e2009-09-05 21:47:34 +000011904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11905 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011906
cristy3ed852e2009-09-05 21:47:34 +000011907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11908 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011909
cristy3ed852e2009-09-05 21:47:34 +000011910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11911 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011912
cristy3ed852e2009-09-05 21:47:34 +000011913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11914 " JNG alpha interlace:%5d",0);
11915 }
11916
glennrp0fe50b42010-11-16 03:52:51 +000011917 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011918 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011919
11920 /*
11921 Write leading ancillary chunks
11922 */
11923
11924 if (transparent)
11925 {
11926 /*
11927 Write JNG bKGD chunk
11928 */
11929
11930 unsigned char
11931 blue,
11932 green,
11933 red;
11934
cristybb503372010-05-27 20:51:26 +000011935 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011936 num_bytes;
11937
11938 if (jng_color_type == 8 || jng_color_type == 12)
11939 num_bytes=6L;
11940 else
11941 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011942 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011943 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011944 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011945 red=ScaleQuantumToChar(image->background_color.red);
11946 green=ScaleQuantumToChar(image->background_color.green);
11947 blue=ScaleQuantumToChar(image->background_color.blue);
11948 *(chunk+4)=0;
11949 *(chunk+5)=red;
11950 *(chunk+6)=0;
11951 *(chunk+7)=green;
11952 *(chunk+8)=0;
11953 *(chunk+9)=blue;
11954 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11955 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11956 }
11957
11958 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11959 {
11960 /*
11961 Write JNG sRGB chunk
11962 */
11963 (void) WriteBlobMSBULong(image,1L);
11964 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011965 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011966
cristy3ed852e2009-09-05 21:47:34 +000011967 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011968 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011969 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011970 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011971
cristy3ed852e2009-09-05 21:47:34 +000011972 else
glennrpe610a072010-08-05 17:08:46 +000011973 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011974 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011975 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011976
cristy3ed852e2009-09-05 21:47:34 +000011977 (void) WriteBlob(image,5,chunk);
11978 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11979 }
11980 else
11981 {
11982 if (image->gamma != 0.0)
11983 {
11984 /*
11985 Write JNG gAMA chunk
11986 */
11987 (void) WriteBlobMSBULong(image,4L);
11988 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011989 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011990 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011991 (void) WriteBlob(image,8,chunk);
11992 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11993 }
glennrp0fe50b42010-11-16 03:52:51 +000011994
cristy3ed852e2009-09-05 21:47:34 +000011995 if ((mng_info->equal_chrms == MagickFalse) &&
11996 (image->chromaticity.red_primary.x != 0.0))
11997 {
11998 PrimaryInfo
11999 primary;
12000
12001 /*
12002 Write JNG cHRM chunk
12003 */
12004 (void) WriteBlobMSBULong(image,32L);
12005 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012006 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012007 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012008 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12009 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012010 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012011 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12012 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012013 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012014 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12015 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012016 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012017 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12018 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012019 (void) WriteBlob(image,36,chunk);
12020 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12021 }
12022 }
glennrp0fe50b42010-11-16 03:52:51 +000012023
cristy16ea1392012-03-21 20:38:41 +000012024 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012025 {
12026 /*
12027 Write JNG pHYs chunk
12028 */
12029 (void) WriteBlobMSBULong(image,9L);
12030 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012031 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000012032 if (image->units == PixelsPerInchResolution)
12033 {
cristy35ef8242010-06-03 16:24:13 +000012034 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012035 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012036
cristy35ef8242010-06-03 16:24:13 +000012037 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012038 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012039
cristy3ed852e2009-09-05 21:47:34 +000012040 chunk[12]=1;
12041 }
glennrp0fe50b42010-11-16 03:52:51 +000012042
cristy3ed852e2009-09-05 21:47:34 +000012043 else
12044 {
12045 if (image->units == PixelsPerCentimeterResolution)
12046 {
cristy35ef8242010-06-03 16:24:13 +000012047 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012048 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012049
cristy35ef8242010-06-03 16:24:13 +000012050 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012051 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012052
cristy3ed852e2009-09-05 21:47:34 +000012053 chunk[12]=1;
12054 }
glennrp0fe50b42010-11-16 03:52:51 +000012055
cristy3ed852e2009-09-05 21:47:34 +000012056 else
12057 {
cristy16ea1392012-03-21 20:38:41 +000012058 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12059 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012060 chunk[12]=0;
12061 }
12062 }
12063 (void) WriteBlob(image,13,chunk);
12064 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12065 }
12066
12067 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12068 {
12069 /*
12070 Write JNG oFFs chunk
12071 */
12072 (void) WriteBlobMSBULong(image,9L);
12073 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000012074 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000012075 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12076 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000012077 chunk[12]=0;
12078 (void) WriteBlob(image,13,chunk);
12079 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12080 }
12081 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12082 {
12083 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12084 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000012085 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012086 PNGLong(chunk+4,(png_uint_32) image->page.width);
12087 PNGLong(chunk+8,(png_uint_32) image->page.height);
12088 chunk[12]=0; /* unit = pixels */
12089 (void) WriteBlob(image,13,chunk);
12090 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12091 }
12092
12093
12094 if (transparent)
12095 {
12096 if (jng_alpha_compression_method==0)
12097 {
cristybb503372010-05-27 20:51:26 +000012098 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012099 i;
12100
cristybb503372010-05-27 20:51:26 +000012101 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012102 len;
12103
12104 /* Write IDAT chunk header */
12105 if (logging != MagickFalse)
12106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012107 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012108 length);
cristy3ed852e2009-09-05 21:47:34 +000012109
12110 /* Copy IDAT chunks */
12111 len=0;
12112 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012113 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012114 {
12115 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12116 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012117
cristy3ed852e2009-09-05 21:47:34 +000012118 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12119 {
12120 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012121 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012122 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012123 (void) WriteBlob(image,(size_t) len+4,p);
12124 (void) WriteBlobMSBULong(image,
12125 crc32(0,p,(uInt) len+4));
12126 }
glennrp0fe50b42010-11-16 03:52:51 +000012127
cristy3ed852e2009-09-05 21:47:34 +000012128 else
12129 {
12130 if (logging != MagickFalse)
12131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012132 " Skipping %c%c%c%c chunk, length=%.20g.",
12133 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012134 }
12135 p+=(8+len);
12136 }
12137 }
12138 else
12139 {
12140 /* Write JDAA chunk header */
12141 if (logging != MagickFalse)
12142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012143 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012144 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012145 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012146 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012147 /* Write JDAT chunk(s) data */
12148 (void) WriteBlob(image,4,chunk);
12149 (void) WriteBlob(image,length,blob);
12150 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12151 (uInt) length));
12152 }
12153 blob=(unsigned char *) RelinquishMagickMemory(blob);
12154 }
12155
12156 /* Encode image as a JPEG blob */
12157 if (logging != MagickFalse)
12158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12159 " Creating jpeg_image_info.");
12160 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12161 if (jpeg_image_info == (ImageInfo *) NULL)
12162 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12163
12164 if (logging != MagickFalse)
12165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12166 " Creating jpeg_image.");
12167
cristy16ea1392012-03-21 20:38:41 +000012168 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012169 if (jpeg_image == (Image *) NULL)
12170 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12171 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12172
12173 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012174 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012175 jpeg_image->filename);
12176
12177 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012178 exception);
cristy3ed852e2009-09-05 21:47:34 +000012179
12180 if (logging != MagickFalse)
12181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012182 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12183 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012184
12185 if (jng_color_type == 8 || jng_color_type == 12)
12186 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012187
glennrp59575fa2011-12-31 21:31:39 +000012188 jpeg_image_info->quality=jng_quality;
12189 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012190 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12191 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012192
cristy3ed852e2009-09-05 21:47:34 +000012193 if (logging != MagickFalse)
12194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12195 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012196
cristy16ea1392012-03-21 20:38:41 +000012197 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012198
cristy3ed852e2009-09-05 21:47:34 +000012199 if (logging != MagickFalse)
12200 {
12201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012202 " Successfully read jpeg_image into a blob, length=%.20g.",
12203 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012204
12205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012206 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012207 }
glennrp0fe50b42010-11-16 03:52:51 +000012208
cristy3ed852e2009-09-05 21:47:34 +000012209 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012210 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012211 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012212 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012213 (void) WriteBlob(image,4,chunk);
12214 (void) WriteBlob(image,length,blob);
12215 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12216
12217 jpeg_image=DestroyImage(jpeg_image);
12218 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12219 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12220 blob=(unsigned char *) RelinquishMagickMemory(blob);
12221
12222 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012223 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012224
12225 /* Write IEND chunk */
12226 (void) WriteBlobMSBULong(image,0L);
12227 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012228 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012229 (void) WriteBlob(image,4,chunk);
12230 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12231
12232 if (logging != MagickFalse)
12233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12234 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012235
cristy3ed852e2009-09-05 21:47:34 +000012236 return(status);
12237}
12238
12239
12240/*
12241%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12242% %
12243% %
12244% %
12245% W r i t e J N G I m a g e %
12246% %
12247% %
12248% %
12249%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12250%
12251% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12252%
12253% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12254%
12255% The format of the WriteJNGImage method is:
12256%
cristy16ea1392012-03-21 20:38:41 +000012257% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12258% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012259%
12260% A description of each parameter follows:
12261%
12262% o image_info: the image info.
12263%
12264% o image: The image.
12265%
cristy16ea1392012-03-21 20:38:41 +000012266% o exception: return any errors or warnings in this structure.
12267%
cristy3ed852e2009-09-05 21:47:34 +000012268%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12269*/
cristy16ea1392012-03-21 20:38:41 +000012270static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12271 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012272{
12273 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012274 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012275 logging,
cristy3ed852e2009-09-05 21:47:34 +000012276 status;
12277
12278 MngInfo
12279 *mng_info;
12280
cristy3ed852e2009-09-05 21:47:34 +000012281 /*
12282 Open image file.
12283 */
12284 assert(image_info != (const ImageInfo *) NULL);
12285 assert(image_info->signature == MagickSignature);
12286 assert(image != (Image *) NULL);
12287 assert(image->signature == MagickSignature);
12288 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012289 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012290 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012291 if (status == MagickFalse)
12292 return(status);
12293
12294 /*
12295 Allocate a MngInfo structure.
12296 */
12297 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012298 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012299 if (mng_info == (MngInfo *) NULL)
12300 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12301 /*
12302 Initialize members of the MngInfo structure.
12303 */
12304 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12305 mng_info->image=image;
12306 have_mng_structure=MagickTrue;
12307
12308 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12309
cristy16ea1392012-03-21 20:38:41 +000012310 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012311 (void) CloseBlob(image);
12312
12313 (void) CatchImageException(image);
12314 MngInfoFreeStruct(mng_info,&have_mng_structure);
12315 if (logging != MagickFalse)
12316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12317 return(status);
12318}
12319#endif
12320
cristy16ea1392012-03-21 20:38:41 +000012321static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12322 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012323{
12324 const char
12325 *option;
12326
12327 Image
12328 *next_image;
12329
12330 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012331 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012332 status;
12333
glennrp03812ae2010-12-24 01:31:34 +000012334 volatile MagickBooleanType
12335 logging;
12336
cristy3ed852e2009-09-05 21:47:34 +000012337 MngInfo
12338 *mng_info;
12339
12340 int
cristy3ed852e2009-09-05 21:47:34 +000012341 image_count,
12342 need_iterations,
12343 need_matte;
12344
12345 volatile int
12346#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12347 defined(PNG_MNG_FEATURES_SUPPORTED)
12348 need_local_plte,
12349#endif
12350 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012351 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012352 use_global_plte;
12353
cristybb503372010-05-27 20:51:26 +000012354 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012355 i;
12356
12357 unsigned char
12358 chunk[800];
12359
12360 volatile unsigned int
12361 write_jng,
12362 write_mng;
12363
cristybb503372010-05-27 20:51:26 +000012364 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012365 scene;
12366
cristybb503372010-05-27 20:51:26 +000012367 size_t
cristy3ed852e2009-09-05 21:47:34 +000012368 final_delay=0,
12369 initial_delay;
12370
glennrpd5045b42010-03-24 12:40:35 +000012371#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012372 if (image_info->verbose)
12373 printf("Your PNG library (libpng-%s) is rather old.\n",
12374 PNG_LIBPNG_VER_STRING);
12375#endif
12376
12377 /*
12378 Open image file.
12379 */
12380 assert(image_info != (const ImageInfo *) NULL);
12381 assert(image_info->signature == MagickSignature);
12382 assert(image != (Image *) NULL);
12383 assert(image->signature == MagickSignature);
12384 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012385 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012386 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012387 if (status == MagickFalse)
12388 return(status);
12389
12390 /*
12391 Allocate a MngInfo structure.
12392 */
12393 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012394 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012395 if (mng_info == (MngInfo *) NULL)
12396 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12397 /*
12398 Initialize members of the MngInfo structure.
12399 */
12400 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12401 mng_info->image=image;
12402 have_mng_structure=MagickTrue;
12403 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12404
12405 /*
12406 * See if user has requested a specific PNG subformat to be used
12407 * for all of the PNGs in the MNG being written, e.g.,
12408 *
12409 * convert *.png png8:animation.mng
12410 *
12411 * To do: check -define png:bit_depth and png:color_type as well,
12412 * or perhaps use mng:bit_depth and mng:color_type instead for
12413 * global settings.
12414 */
12415
12416 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12417 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12418 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12419
12420 write_jng=MagickFalse;
12421 if (image_info->compression == JPEGCompression)
12422 write_jng=MagickTrue;
12423
12424 mng_info->adjoin=image_info->adjoin &&
12425 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12426
cristy3ed852e2009-09-05 21:47:34 +000012427 if (logging != MagickFalse)
12428 {
12429 /* Log some info about the input */
12430 Image
12431 *p;
12432
12433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12434 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012435
cristy3ed852e2009-09-05 21:47:34 +000012436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012437 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012438
cristy3ed852e2009-09-05 21:47:34 +000012439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12440 " Type: %d",image_info->type);
12441
12442 scene=0;
12443 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12444 {
12445 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012446 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012447
cristy3ed852e2009-09-05 21:47:34 +000012448 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012449 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012450
cristy3ed852e2009-09-05 21:47:34 +000012451 if (p->matte)
12452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12453 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012454
cristy3ed852e2009-09-05 21:47:34 +000012455 else
12456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12457 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012458
cristy3ed852e2009-09-05 21:47:34 +000012459 if (p->storage_class == PseudoClass)
12460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12461 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012462
cristy3ed852e2009-09-05 21:47:34 +000012463 else
12464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12465 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012466
cristy3ed852e2009-09-05 21:47:34 +000012467 if (p->colors)
12468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012469 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012470
cristy3ed852e2009-09-05 21:47:34 +000012471 else
12472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12473 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012474
cristy3ed852e2009-09-05 21:47:34 +000012475 if (mng_info->adjoin == MagickFalse)
12476 break;
12477 }
12478 }
12479
cristy3ed852e2009-09-05 21:47:34 +000012480 use_global_plte=MagickFalse;
12481 all_images_are_gray=MagickFalse;
12482#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12483 need_local_plte=MagickTrue;
12484#endif
12485 need_defi=MagickFalse;
12486 need_matte=MagickFalse;
12487 mng_info->framing_mode=1;
12488 mng_info->old_framing_mode=1;
12489
12490 if (write_mng)
12491 if (image_info->page != (char *) NULL)
12492 {
12493 /*
12494 Determine image bounding box.
12495 */
12496 SetGeometry(image,&mng_info->page);
12497 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12498 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12499 }
12500 if (write_mng)
12501 {
12502 unsigned int
12503 need_geom;
12504
12505 unsigned short
12506 red,
12507 green,
12508 blue;
12509
12510 mng_info->page=image->page;
12511 need_geom=MagickTrue;
12512 if (mng_info->page.width || mng_info->page.height)
12513 need_geom=MagickFalse;
12514 /*
12515 Check all the scenes.
12516 */
12517 initial_delay=image->delay;
12518 need_iterations=MagickFalse;
12519 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12520 mng_info->equal_physs=MagickTrue,
12521 mng_info->equal_gammas=MagickTrue;
12522 mng_info->equal_srgbs=MagickTrue;
12523 mng_info->equal_backgrounds=MagickTrue;
12524 image_count=0;
12525#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12526 defined(PNG_MNG_FEATURES_SUPPORTED)
12527 all_images_are_gray=MagickTrue;
12528 mng_info->equal_palettes=MagickFalse;
12529 need_local_plte=MagickFalse;
12530#endif
12531 for (next_image=image; next_image != (Image *) NULL; )
12532 {
12533 if (need_geom)
12534 {
12535 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12536 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012537
cristy3ed852e2009-09-05 21:47:34 +000012538 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12539 mng_info->page.height=next_image->rows+next_image->page.y;
12540 }
glennrp0fe50b42010-11-16 03:52:51 +000012541
cristy3ed852e2009-09-05 21:47:34 +000012542 if (next_image->page.x || next_image->page.y)
12543 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012544
cristy3ed852e2009-09-05 21:47:34 +000012545 if (next_image->matte)
12546 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012547
cristy3ed852e2009-09-05 21:47:34 +000012548 if ((int) next_image->dispose >= BackgroundDispose)
12549 if (next_image->matte || next_image->page.x || next_image->page.y ||
12550 ((next_image->columns < mng_info->page.width) &&
12551 (next_image->rows < mng_info->page.height)))
12552 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012553
cristy3ed852e2009-09-05 21:47:34 +000012554 if (next_image->iterations)
12555 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012556
cristy3ed852e2009-09-05 21:47:34 +000012557 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012558
cristy3ed852e2009-09-05 21:47:34 +000012559 if (final_delay != initial_delay || final_delay > 1UL*
12560 next_image->ticks_per_second)
12561 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012562
cristy3ed852e2009-09-05 21:47:34 +000012563#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12564 defined(PNG_MNG_FEATURES_SUPPORTED)
12565 /*
12566 check for global palette possibility.
12567 */
12568 if (image->matte != MagickFalse)
12569 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012570
cristy3ed852e2009-09-05 21:47:34 +000012571 if (need_local_plte == 0)
12572 {
cristy16ea1392012-03-21 20:38:41 +000012573 if (ImageIsGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012574 all_images_are_gray=MagickFalse;
12575 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12576 if (use_global_plte == 0)
12577 use_global_plte=mng_info->equal_palettes;
12578 need_local_plte=!mng_info->equal_palettes;
12579 }
12580#endif
12581 if (GetNextImageInList(next_image) != (Image *) NULL)
12582 {
12583 if (next_image->background_color.red !=
12584 next_image->next->background_color.red ||
12585 next_image->background_color.green !=
12586 next_image->next->background_color.green ||
12587 next_image->background_color.blue !=
12588 next_image->next->background_color.blue)
12589 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012590
cristy3ed852e2009-09-05 21:47:34 +000012591 if (next_image->gamma != next_image->next->gamma)
12592 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012593
cristy3ed852e2009-09-05 21:47:34 +000012594 if (next_image->rendering_intent !=
12595 next_image->next->rendering_intent)
12596 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012597
cristy3ed852e2009-09-05 21:47:34 +000012598 if ((next_image->units != next_image->next->units) ||
cristy16ea1392012-03-21 20:38:41 +000012599 (next_image->resolution.x != next_image->next->resolution.x) ||
12600 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012601 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012602
cristy3ed852e2009-09-05 21:47:34 +000012603 if (mng_info->equal_chrms)
12604 {
12605 if (next_image->chromaticity.red_primary.x !=
12606 next_image->next->chromaticity.red_primary.x ||
12607 next_image->chromaticity.red_primary.y !=
12608 next_image->next->chromaticity.red_primary.y ||
12609 next_image->chromaticity.green_primary.x !=
12610 next_image->next->chromaticity.green_primary.x ||
12611 next_image->chromaticity.green_primary.y !=
12612 next_image->next->chromaticity.green_primary.y ||
12613 next_image->chromaticity.blue_primary.x !=
12614 next_image->next->chromaticity.blue_primary.x ||
12615 next_image->chromaticity.blue_primary.y !=
12616 next_image->next->chromaticity.blue_primary.y ||
12617 next_image->chromaticity.white_point.x !=
12618 next_image->next->chromaticity.white_point.x ||
12619 next_image->chromaticity.white_point.y !=
12620 next_image->next->chromaticity.white_point.y)
12621 mng_info->equal_chrms=MagickFalse;
12622 }
12623 }
12624 image_count++;
12625 next_image=GetNextImageInList(next_image);
12626 }
12627 if (image_count < 2)
12628 {
12629 mng_info->equal_backgrounds=MagickFalse;
12630 mng_info->equal_chrms=MagickFalse;
12631 mng_info->equal_gammas=MagickFalse;
12632 mng_info->equal_srgbs=MagickFalse;
12633 mng_info->equal_physs=MagickFalse;
12634 use_global_plte=MagickFalse;
12635#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12636 need_local_plte=MagickTrue;
12637#endif
12638 need_iterations=MagickFalse;
12639 }
glennrp0fe50b42010-11-16 03:52:51 +000012640
cristy3ed852e2009-09-05 21:47:34 +000012641 if (mng_info->need_fram == MagickFalse)
12642 {
12643 /*
12644 Only certain framing rates 100/n are exactly representable without
12645 the FRAM chunk but we'll allow some slop in VLC files
12646 */
12647 if (final_delay == 0)
12648 {
12649 if (need_iterations != MagickFalse)
12650 {
12651 /*
12652 It's probably a GIF with loop; don't run it *too* fast.
12653 */
glennrp02617122010-07-28 13:07:35 +000012654 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012655 {
12656 final_delay=10;
cristy16ea1392012-03-21 20:38:41 +000012657 (void) ThrowMagickException(exception,GetMagickModule(),
12658 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012659 "input has zero delay between all frames; assuming",
12660 " 10 cs `%s'","");
12661 }
cristy3ed852e2009-09-05 21:47:34 +000012662 }
12663 else
12664 mng_info->ticks_per_second=0;
12665 }
12666 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012667 mng_info->ticks_per_second=(png_uint_32)
12668 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012669 if (final_delay > 50)
12670 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012671
cristy3ed852e2009-09-05 21:47:34 +000012672 if (final_delay > 75)
12673 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012674
cristy3ed852e2009-09-05 21:47:34 +000012675 if (final_delay > 125)
12676 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012677
cristy3ed852e2009-09-05 21:47:34 +000012678 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12679 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12680 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12681 1UL*image->ticks_per_second))
12682 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12683 }
glennrp0fe50b42010-11-16 03:52:51 +000012684
cristy3ed852e2009-09-05 21:47:34 +000012685 if (mng_info->need_fram != MagickFalse)
12686 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12687 /*
12688 If pseudocolor, we should also check to see if all the
12689 palettes are identical and write a global PLTE if they are.
12690 ../glennrp Feb 99.
12691 */
12692 /*
12693 Write the MNG version 1.0 signature and MHDR chunk.
12694 */
12695 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12696 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12697 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012698 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012699 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12700 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012701 PNGLong(chunk+12,mng_info->ticks_per_second);
12702 PNGLong(chunk+16,0L); /* layer count=unknown */
12703 PNGLong(chunk+20,0L); /* frame count=unknown */
12704 PNGLong(chunk+24,0L); /* play time=unknown */
12705 if (write_jng)
12706 {
12707 if (need_matte)
12708 {
12709 if (need_defi || mng_info->need_fram || use_global_plte)
12710 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012711
cristy3ed852e2009-09-05 21:47:34 +000012712 else
12713 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12714 }
glennrp0fe50b42010-11-16 03:52:51 +000012715
cristy3ed852e2009-09-05 21:47:34 +000012716 else
12717 {
12718 if (need_defi || mng_info->need_fram || use_global_plte)
12719 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012720
cristy3ed852e2009-09-05 21:47:34 +000012721 else
12722 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12723 }
12724 }
glennrp0fe50b42010-11-16 03:52:51 +000012725
cristy3ed852e2009-09-05 21:47:34 +000012726 else
12727 {
12728 if (need_matte)
12729 {
12730 if (need_defi || mng_info->need_fram || use_global_plte)
12731 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012732
cristy3ed852e2009-09-05 21:47:34 +000012733 else
12734 PNGLong(chunk+28,9L); /* simplicity=VLC */
12735 }
glennrp0fe50b42010-11-16 03:52:51 +000012736
cristy3ed852e2009-09-05 21:47:34 +000012737 else
12738 {
12739 if (need_defi || mng_info->need_fram || use_global_plte)
12740 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012741
cristy3ed852e2009-09-05 21:47:34 +000012742 else
12743 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12744 }
12745 }
12746 (void) WriteBlob(image,32,chunk);
12747 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12748 option=GetImageOption(image_info,"mng:need-cacheoff");
12749 if (option != (const char *) NULL)
12750 {
12751 size_t
12752 length;
12753
12754 /*
12755 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12756 */
12757 PNGType(chunk,mng_nEED);
12758 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012759 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012760 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012761 length+=4;
12762 (void) WriteBlob(image,length,chunk);
12763 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12764 }
12765 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12766 (GetNextImageInList(image) != (Image *) NULL) &&
12767 (image->iterations != 1))
12768 {
12769 /*
12770 Write MNG TERM chunk
12771 */
12772 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12773 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012774 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012775 chunk[4]=3; /* repeat animation */
12776 chunk[5]=0; /* show last frame when done */
12777 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12778 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012779
cristy3ed852e2009-09-05 21:47:34 +000012780 if (image->iterations == 0)
12781 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012782
cristy3ed852e2009-09-05 21:47:34 +000012783 else
12784 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012785
cristy3ed852e2009-09-05 21:47:34 +000012786 if (logging != MagickFalse)
12787 {
12788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012789 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12790 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012791
cristy3ed852e2009-09-05 21:47:34 +000012792 if (image->iterations == 0)
12793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012794 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012795
cristy3ed852e2009-09-05 21:47:34 +000012796 else
12797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012798 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012799 }
12800 (void) WriteBlob(image,14,chunk);
12801 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12802 }
12803 /*
12804 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12805 */
12806 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12807 mng_info->equal_srgbs)
12808 {
12809 /*
12810 Write MNG sRGB chunk
12811 */
12812 (void) WriteBlobMSBULong(image,1L);
12813 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012814 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012815
cristy3ed852e2009-09-05 21:47:34 +000012816 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012817 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012818 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012819 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012820
cristy3ed852e2009-09-05 21:47:34 +000012821 else
glennrpe610a072010-08-05 17:08:46 +000012822 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012823 Magick_RenderingIntent_to_PNG_RenderingIntent(
12824 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012825
cristy3ed852e2009-09-05 21:47:34 +000012826 (void) WriteBlob(image,5,chunk);
12827 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12828 mng_info->have_write_global_srgb=MagickTrue;
12829 }
glennrp0fe50b42010-11-16 03:52:51 +000012830
cristy3ed852e2009-09-05 21:47:34 +000012831 else
12832 {
12833 if (image->gamma && mng_info->equal_gammas)
12834 {
12835 /*
12836 Write MNG gAMA chunk
12837 */
12838 (void) WriteBlobMSBULong(image,4L);
12839 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012840 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012841 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012842 (void) WriteBlob(image,8,chunk);
12843 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12844 mng_info->have_write_global_gama=MagickTrue;
12845 }
12846 if (mng_info->equal_chrms)
12847 {
12848 PrimaryInfo
12849 primary;
12850
12851 /*
12852 Write MNG cHRM chunk
12853 */
12854 (void) WriteBlobMSBULong(image,32L);
12855 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012856 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012857 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012858 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12859 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012860 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012861 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12862 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012863 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012864 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12865 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012866 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012867 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12868 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012869 (void) WriteBlob(image,36,chunk);
12870 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12871 mng_info->have_write_global_chrm=MagickTrue;
12872 }
12873 }
cristy16ea1392012-03-21 20:38:41 +000012874 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012875 {
12876 /*
12877 Write MNG pHYs chunk
12878 */
12879 (void) WriteBlobMSBULong(image,9L);
12880 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012881 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012882
cristy3ed852e2009-09-05 21:47:34 +000012883 if (image->units == PixelsPerInchResolution)
12884 {
cristy35ef8242010-06-03 16:24:13 +000012885 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012886 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012887
cristy35ef8242010-06-03 16:24:13 +000012888 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012889 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012890
cristy3ed852e2009-09-05 21:47:34 +000012891 chunk[12]=1;
12892 }
glennrp0fe50b42010-11-16 03:52:51 +000012893
cristy3ed852e2009-09-05 21:47:34 +000012894 else
12895 {
12896 if (image->units == PixelsPerCentimeterResolution)
12897 {
cristy35ef8242010-06-03 16:24:13 +000012898 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012899 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012900
cristy35ef8242010-06-03 16:24:13 +000012901 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012902 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012903
cristy3ed852e2009-09-05 21:47:34 +000012904 chunk[12]=1;
12905 }
glennrp0fe50b42010-11-16 03:52:51 +000012906
cristy3ed852e2009-09-05 21:47:34 +000012907 else
12908 {
cristy16ea1392012-03-21 20:38:41 +000012909 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12910 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012911 chunk[12]=0;
12912 }
12913 }
12914 (void) WriteBlob(image,13,chunk);
12915 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12916 }
12917 /*
12918 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12919 or does not cover the entire frame.
12920 */
12921 if (write_mng && (image->matte || image->page.x > 0 ||
12922 image->page.y > 0 || (image->page.width &&
12923 (image->page.width+image->page.x < mng_info->page.width))
12924 || (image->page.height && (image->page.height+image->page.y
12925 < mng_info->page.height))))
12926 {
12927 (void) WriteBlobMSBULong(image,6L);
12928 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012929 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012930 red=ScaleQuantumToShort(image->background_color.red);
12931 green=ScaleQuantumToShort(image->background_color.green);
12932 blue=ScaleQuantumToShort(image->background_color.blue);
12933 PNGShort(chunk+4,red);
12934 PNGShort(chunk+6,green);
12935 PNGShort(chunk+8,blue);
12936 (void) WriteBlob(image,10,chunk);
12937 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12938 if (mng_info->equal_backgrounds)
12939 {
12940 (void) WriteBlobMSBULong(image,6L);
12941 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012942 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012943 (void) WriteBlob(image,10,chunk);
12944 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12945 }
12946 }
12947
12948#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12949 if ((need_local_plte == MagickFalse) &&
12950 (image->storage_class == PseudoClass) &&
12951 (all_images_are_gray == MagickFalse))
12952 {
cristybb503372010-05-27 20:51:26 +000012953 size_t
cristy3ed852e2009-09-05 21:47:34 +000012954 data_length;
12955
12956 /*
12957 Write MNG PLTE chunk
12958 */
12959 data_length=3*image->colors;
12960 (void) WriteBlobMSBULong(image,data_length);
12961 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012962 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012963
cristybb503372010-05-27 20:51:26 +000012964 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012965 {
cristy16ea1392012-03-21 20:38:41 +000012966 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12967 image->colormap[i].red) & 0xff);
12968 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12969 image->colormap[i].green) & 0xff);
12970 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12971 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012972 }
glennrp0fe50b42010-11-16 03:52:51 +000012973
cristy3ed852e2009-09-05 21:47:34 +000012974 (void) WriteBlob(image,data_length+4,chunk);
12975 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12976 mng_info->have_write_global_plte=MagickTrue;
12977 }
12978#endif
12979 }
12980 scene=0;
12981 mng_info->delay=0;
12982#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12983 defined(PNG_MNG_FEATURES_SUPPORTED)
12984 mng_info->equal_palettes=MagickFalse;
12985#endif
12986 do
12987 {
12988 if (mng_info->adjoin)
12989 {
12990#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12991 defined(PNG_MNG_FEATURES_SUPPORTED)
12992 /*
12993 If we aren't using a global palette for the entire MNG, check to
12994 see if we can use one for two or more consecutive images.
12995 */
12996 if (need_local_plte && use_global_plte && !all_images_are_gray)
12997 {
12998 if (mng_info->IsPalette)
12999 {
13000 /*
13001 When equal_palettes is true, this image has the same palette
13002 as the previous PseudoClass image
13003 */
13004 mng_info->have_write_global_plte=mng_info->equal_palettes;
13005 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13006 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13007 {
13008 /*
13009 Write MNG PLTE chunk
13010 */
cristybb503372010-05-27 20:51:26 +000013011 size_t
cristy3ed852e2009-09-05 21:47:34 +000013012 data_length;
13013
13014 data_length=3*image->colors;
13015 (void) WriteBlobMSBULong(image,data_length);
13016 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013017 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013018
cristybb503372010-05-27 20:51:26 +000013019 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013020 {
13021 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13022 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13023 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13024 }
glennrp0fe50b42010-11-16 03:52:51 +000013025
cristy3ed852e2009-09-05 21:47:34 +000013026 (void) WriteBlob(image,data_length+4,chunk);
13027 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13028 (uInt) (data_length+4)));
13029 mng_info->have_write_global_plte=MagickTrue;
13030 }
13031 }
13032 else
13033 mng_info->have_write_global_plte=MagickFalse;
13034 }
13035#endif
13036 if (need_defi)
13037 {
cristybb503372010-05-27 20:51:26 +000013038 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000013039 previous_x,
13040 previous_y;
13041
13042 if (scene)
13043 {
13044 previous_x=mng_info->page.x;
13045 previous_y=mng_info->page.y;
13046 }
13047 else
13048 {
13049 previous_x=0;
13050 previous_y=0;
13051 }
13052 mng_info->page=image->page;
13053 if ((mng_info->page.x != previous_x) ||
13054 (mng_info->page.y != previous_y))
13055 {
13056 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13057 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000013058 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000013059 chunk[4]=0; /* object 0 MSB */
13060 chunk[5]=0; /* object 0 LSB */
13061 chunk[6]=0; /* visible */
13062 chunk[7]=0; /* abstract */
13063 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13064 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13065 (void) WriteBlob(image,16,chunk);
13066 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13067 }
13068 }
13069 }
13070
13071 mng_info->write_mng=write_mng;
13072
13073 if ((int) image->dispose >= 3)
13074 mng_info->framing_mode=3;
13075
13076 if (mng_info->need_fram && mng_info->adjoin &&
13077 ((image->delay != mng_info->delay) ||
13078 (mng_info->framing_mode != mng_info->old_framing_mode)))
13079 {
13080 if (image->delay == mng_info->delay)
13081 {
13082 /*
13083 Write a MNG FRAM chunk with the new framing mode.
13084 */
13085 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13086 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013087 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013088 chunk[4]=(unsigned char) mng_info->framing_mode;
13089 (void) WriteBlob(image,5,chunk);
13090 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13091 }
13092 else
13093 {
13094 /*
13095 Write a MNG FRAM chunk with the delay.
13096 */
13097 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13098 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013099 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013100 chunk[4]=(unsigned char) mng_info->framing_mode;
13101 chunk[5]=0; /* frame name separator (no name) */
13102 chunk[6]=2; /* flag for changing default delay */
13103 chunk[7]=0; /* flag for changing frame timeout */
13104 chunk[8]=0; /* flag for changing frame clipping */
13105 chunk[9]=0; /* flag for changing frame sync_id */
13106 PNGLong(chunk+10,(png_uint_32)
13107 ((mng_info->ticks_per_second*
13108 image->delay)/MagickMax(image->ticks_per_second,1)));
13109 (void) WriteBlob(image,14,chunk);
13110 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013111 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013112 }
13113 mng_info->old_framing_mode=mng_info->framing_mode;
13114 }
13115
13116#if defined(JNG_SUPPORTED)
13117 if (image_info->compression == JPEGCompression)
13118 {
13119 ImageInfo
13120 *write_info;
13121
13122 if (logging != MagickFalse)
13123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13124 " Writing JNG object.");
13125 /* To do: specify the desired alpha compression method. */
13126 write_info=CloneImageInfo(image_info);
13127 write_info->compression=UndefinedCompression;
cristy16ea1392012-03-21 20:38:41 +000013128 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013129 write_info=DestroyImageInfo(write_info);
13130 }
13131 else
13132#endif
13133 {
13134 if (logging != MagickFalse)
13135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13136 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013137
glennrpb9cfe272010-12-21 15:08:06 +000013138 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013139 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013140
13141 /* We don't want any ancillary chunks written */
13142 mng_info->ping_exclude_bKGD=MagickTrue;
13143 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013144 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013145 mng_info->ping_exclude_EXIF=MagickTrue;
13146 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013147 mng_info->ping_exclude_iCCP=MagickTrue;
13148 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13149 mng_info->ping_exclude_oFFs=MagickTrue;
13150 mng_info->ping_exclude_pHYs=MagickTrue;
13151 mng_info->ping_exclude_sRGB=MagickTrue;
13152 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013153 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013154 mng_info->ping_exclude_vpAg=MagickTrue;
13155 mng_info->ping_exclude_zCCP=MagickTrue;
13156 mng_info->ping_exclude_zTXt=MagickTrue;
13157
cristy16ea1392012-03-21 20:38:41 +000013158 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013159 }
13160
13161 if (status == MagickFalse)
13162 {
13163 MngInfoFreeStruct(mng_info,&have_mng_structure);
13164 (void) CloseBlob(image);
13165 return(MagickFalse);
13166 }
13167 (void) CatchImageException(image);
13168 if (GetNextImageInList(image) == (Image *) NULL)
13169 break;
13170 image=SyncNextImageInList(image);
13171 status=SetImageProgress(image,SaveImagesTag,scene++,
13172 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013173
cristy3ed852e2009-09-05 21:47:34 +000013174 if (status == MagickFalse)
13175 break;
glennrp0fe50b42010-11-16 03:52:51 +000013176
cristy3ed852e2009-09-05 21:47:34 +000013177 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013178
cristy3ed852e2009-09-05 21:47:34 +000013179 if (write_mng)
13180 {
13181 while (GetPreviousImageInList(image) != (Image *) NULL)
13182 image=GetPreviousImageInList(image);
13183 /*
13184 Write the MEND chunk.
13185 */
13186 (void) WriteBlobMSBULong(image,0x00000000L);
13187 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013188 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013189 (void) WriteBlob(image,4,chunk);
13190 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13191 }
13192 /*
13193 Relinquish resources.
13194 */
13195 (void) CloseBlob(image);
13196 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013197
cristy3ed852e2009-09-05 21:47:34 +000013198 if (logging != MagickFalse)
13199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013200
cristy3ed852e2009-09-05 21:47:34 +000013201 return(MagickTrue);
13202}
glennrpd5045b42010-03-24 12:40:35 +000013203#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013204
cristy3ed852e2009-09-05 21:47:34 +000013205static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13206{
glennrp3bd393f2011-12-21 18:54:53 +000013207 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013208 printf("Your PNG library is too old: You have libpng-%s\n",
13209 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013210
cristy3ed852e2009-09-05 21:47:34 +000013211 ThrowBinaryException(CoderError,"PNG library is too old",
13212 image_info->filename);
13213}
glennrp39992b42010-11-14 00:03:43 +000013214
cristy3ed852e2009-09-05 21:47:34 +000013215static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13216{
13217 return(WritePNGImage(image_info,image));
13218}
glennrpd5045b42010-03-24 12:40:35 +000013219#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013220#endif