blob: c561e59f2e7d226560ada06e99786063614a6bdd [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
cristy9d8c1222012-08-10 12:34:19 +00001126static const char *
glennrp98b83d42012-07-23 02:50:31 +00001127Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1128{
1129 switch (ping_intent)
1130 {
1131 case 0:
1132 return "Perceptual Intent";
1133
1134 case 1:
1135 return "Relative Intent";
1136
1137 case 2:
1138 return "Saturation Intent";
1139
1140 case 3:
1141 return "Absolute Intent";
1142
1143 default:
1144 return "Undefined Intent";
1145 }
1146}
1147
cristybb503372010-05-27 20:51:26 +00001148static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001149{
1150 if (x > y)
1151 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001152
cristy3ed852e2009-09-05 21:47:34 +00001153 return(y);
1154}
glennrp0c3e06b2010-11-19 13:45:02 +00001155
cristyd9ecd042012-06-17 18:26:12 +00001156static const char *
glennrp5dff4352012-06-06 22:12:04 +00001157Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1158{
1159 switch (ping_colortype)
1160 {
1161 case 0:
1162 return "Grayscale";
1163
1164 case 2:
1165 return "Truecolor";
1166
1167 case 3:
1168 return "Indexed";
1169
1170 case 4:
1171 return "GrayAlpha";
1172
1173 case 6:
1174 return "RGBA";
1175
1176 default:
1177 return "UndefinedColorType";
1178 }
1179}
1180
1181
cristybb503372010-05-27 20:51:26 +00001182static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001183{
1184 if (x < y)
1185 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001186
cristy3ed852e2009-09-05 21:47:34 +00001187 return(y);
1188}
glennrpd5045b42010-03-24 12:40:35 +00001189#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001190#endif /* MAGICKCORE_PNG_DELEGATE */
1191
1192/*
1193%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1194% %
1195% %
1196% %
1197% I s M N G %
1198% %
1199% %
1200% %
1201%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1202%
1203% IsMNG() returns MagickTrue if the image format type, identified by the
1204% magick string, is MNG.
1205%
1206% The format of the IsMNG method is:
1207%
1208% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1209%
1210% A description of each parameter follows:
1211%
1212% o magick: compare image format pattern against these bytes.
1213%
1214% o length: Specifies the length of the magick string.
1215%
1216%
1217*/
1218static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1219{
1220 if (length < 8)
1221 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001222
cristy3ed852e2009-09-05 21:47:34 +00001223 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1224 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001225
cristy3ed852e2009-09-05 21:47:34 +00001226 return(MagickFalse);
1227}
1228
1229/*
1230%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1231% %
1232% %
1233% %
1234% I s J N G %
1235% %
1236% %
1237% %
1238%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1239%
1240% IsJNG() returns MagickTrue if the image format type, identified by the
1241% magick string, is JNG.
1242%
1243% The format of the IsJNG method is:
1244%
1245% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1246%
1247% A description of each parameter follows:
1248%
1249% o magick: compare image format pattern against these bytes.
1250%
1251% o length: Specifies the length of the magick string.
1252%
1253%
1254*/
1255static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1256{
1257 if (length < 8)
1258 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001259
cristy3ed852e2009-09-05 21:47:34 +00001260 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1261 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001262
cristy3ed852e2009-09-05 21:47:34 +00001263 return(MagickFalse);
1264}
1265
1266/*
1267%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1268% %
1269% %
1270% %
1271% I s P N G %
1272% %
1273% %
1274% %
1275%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1276%
1277% IsPNG() returns MagickTrue if the image format type, identified by the
1278% magick string, is PNG.
1279%
1280% The format of the IsPNG method is:
1281%
1282% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1283%
1284% A description of each parameter follows:
1285%
1286% o magick: compare image format pattern against these bytes.
1287%
1288% o length: Specifies the length of the magick string.
1289%
1290*/
1291static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1292{
1293 if (length < 8)
1294 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001295
cristy3ed852e2009-09-05 21:47:34 +00001296 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1297 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001298
cristy3ed852e2009-09-05 21:47:34 +00001299 return(MagickFalse);
1300}
1301
1302#if defined(MAGICKCORE_PNG_DELEGATE)
1303#if defined(__cplusplus) || defined(c_plusplus)
1304extern "C" {
1305#endif
1306
glennrpd5045b42010-03-24 12:40:35 +00001307#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001308static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001309{
1310 unsigned char
1311 buffer[4];
1312
1313 assert(image != (Image *) NULL);
1314 assert(image->signature == MagickSignature);
1315 buffer[0]=(unsigned char) (value >> 24);
1316 buffer[1]=(unsigned char) (value >> 16);
1317 buffer[2]=(unsigned char) (value >> 8);
1318 buffer[3]=(unsigned char) value;
1319 return((size_t) WriteBlob(image,4,buffer));
1320}
1321
1322static void PNGLong(png_bytep p,png_uint_32 value)
1323{
1324 *p++=(png_byte) ((value >> 24) & 0xff);
1325 *p++=(png_byte) ((value >> 16) & 0xff);
1326 *p++=(png_byte) ((value >> 8) & 0xff);
1327 *p++=(png_byte) (value & 0xff);
1328}
1329
glennrpa521b2f2010-10-29 04:11:03 +00001330#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001331static void PNGsLong(png_bytep p,png_int_32 value)
1332{
1333 *p++=(png_byte) ((value >> 24) & 0xff);
1334 *p++=(png_byte) ((value >> 16) & 0xff);
1335 *p++=(png_byte) ((value >> 8) & 0xff);
1336 *p++=(png_byte) (value & 0xff);
1337}
glennrpa521b2f2010-10-29 04:11:03 +00001338#endif
cristy3ed852e2009-09-05 21:47:34 +00001339
1340static void PNGShort(png_bytep p,png_uint_16 value)
1341{
1342 *p++=(png_byte) ((value >> 8) & 0xff);
1343 *p++=(png_byte) (value & 0xff);
1344}
1345
1346static void PNGType(png_bytep p,png_bytep type)
1347{
1348 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1349}
1350
glennrp03812ae2010-12-24 01:31:34 +00001351static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1352 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001353{
1354 if (logging != MagickFalse)
1355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001356 " Writing %c%c%c%c chunk, length: %.20g",
1357 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001358}
glennrpd5045b42010-03-24 12:40:35 +00001359#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001360
1361#if defined(__cplusplus) || defined(c_plusplus)
1362}
1363#endif
1364
glennrpd5045b42010-03-24 12:40:35 +00001365#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001366/*
1367%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1368% %
1369% %
1370% %
1371% R e a d P N G I m a g e %
1372% %
1373% %
1374% %
1375%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1376%
1377% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1378% Multiple-image Network Graphics (MNG) image file and returns it. It
1379% allocates the memory necessary for the new Image structure and returns a
1380% pointer to the new image or set of images.
1381%
1382% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1383%
1384% The format of the ReadPNGImage method is:
1385%
1386% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1387%
1388% A description of each parameter follows:
1389%
1390% o image_info: the image info.
1391%
1392% o exception: return any errors or warnings in this structure.
1393%
1394% To do, more or less in chronological order (as of version 5.5.2,
1395% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1396%
1397% Get 16-bit cheap transparency working.
1398%
1399% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1400%
1401% Preserve all unknown and not-yet-handled known chunks found in input
1402% PNG file and copy them into output PNG files according to the PNG
1403% copying rules.
1404%
1405% (At this point, PNG encoding should be in full MNG compliance)
1406%
1407% Provide options for choice of background to use when the MNG BACK
1408% chunk is not present or is not mandatory (i.e., leave transparent,
1409% user specified, MNG BACK, PNG bKGD)
1410%
1411% Implement LOOP/ENDL [done, but could do discretionary loops more
1412% efficiently by linking in the duplicate frames.].
1413%
1414% Decode and act on the MHDR simplicity profile (offer option to reject
1415% files or attempt to process them anyway when the profile isn't LC or VLC).
1416%
1417% Upgrade to full MNG without Delta-PNG.
1418%
1419% o BACK [done a while ago except for background image ID]
1420% o MOVE [done 15 May 1999]
1421% o CLIP [done 15 May 1999]
1422% o DISC [done 19 May 1999]
1423% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1424% o SEEK [partially done 19 May 1999 (discard function only)]
1425% o SHOW
1426% o PAST
1427% o BASI
1428% o MNG-level tEXt/iTXt/zTXt
1429% o pHYg
1430% o pHYs
1431% o sBIT
1432% o bKGD
1433% o iTXt (wait for libpng implementation).
1434%
1435% Use the scene signature to discover when an identical scene is
1436% being reused, and just point to the original image->exception instead
1437% of storing another set of pixels. This not specific to MNG
1438% but could be applied generally.
1439%
1440% Upgrade to full MNG with Delta-PNG.
1441%
1442% JNG tEXt/iTXt/zTXt
1443%
1444% We will not attempt to read files containing the CgBI chunk.
1445% They are really Xcode files meant for display on the iPhone.
1446% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001447% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001448% since irretrievable loss of color data has occurred due to the
1449% use of premultiplied alpha.
1450*/
1451
1452#if defined(__cplusplus) || defined(c_plusplus)
1453extern "C" {
1454#endif
1455
1456/*
1457 This the function that does the actual reading of data. It is
1458 the same as the one supplied in libpng, except that it receives the
1459 datastream from the ReadBlob() function instead of standard input.
1460*/
1461static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1462{
1463 Image
1464 *image;
1465
1466 image=(Image *) png_get_io_ptr(png_ptr);
1467 if (length)
1468 {
1469 png_size_t
1470 check;
1471
1472 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1473 if (check != length)
1474 {
1475 char
1476 msg[MaxTextExtent];
1477
cristy3b6fd2e2011-05-20 12:53:50 +00001478 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001479 "Expected %.20g bytes; found %.20g bytes",(double) length,
1480 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001481 png_warning(png_ptr,msg);
1482 png_error(png_ptr,"Read Exception");
1483 }
1484 }
1485}
1486
1487#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1488 !defined(PNG_MNG_FEATURES_SUPPORTED)
1489/* We use mng_get_data() instead of png_get_data() if we have a libpng
1490 * older than libpng-1.0.3a, which was the first to allow the empty
1491 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1492 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1493 * encountered after an empty PLTE, so we have to look ahead for bKGD
1494 * chunks and remove them from the datastream that is passed to libpng,
1495 * and store their contents for later use.
1496 */
1497static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1498{
1499 MngInfo
1500 *mng_info;
1501
1502 Image
1503 *image;
1504
1505 png_size_t
1506 check;
1507
cristybb503372010-05-27 20:51:26 +00001508 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001509 i;
1510
1511 i=0;
1512 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1513 image=(Image *) mng_info->image;
1514 while (mng_info->bytes_in_read_buffer && length)
1515 {
1516 data[i]=mng_info->read_buffer[i];
1517 mng_info->bytes_in_read_buffer--;
1518 length--;
1519 i++;
1520 }
1521 if (length)
1522 {
1523 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001524
cristy3ed852e2009-09-05 21:47:34 +00001525 if (check != length)
1526 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001527
cristy3ed852e2009-09-05 21:47:34 +00001528 if (length == 4)
1529 {
1530 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1531 (data[3] == 0))
1532 {
1533 check=(png_size_t) ReadBlob(image,(size_t) length,
1534 (char *) mng_info->read_buffer);
1535 mng_info->read_buffer[4]=0;
1536 mng_info->bytes_in_read_buffer=4;
1537 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1538 mng_info->found_empty_plte=MagickTrue;
1539 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1540 {
1541 mng_info->found_empty_plte=MagickFalse;
1542 mng_info->have_saved_bkgd_index=MagickFalse;
1543 }
1544 }
glennrp0fe50b42010-11-16 03:52:51 +00001545
cristy3ed852e2009-09-05 21:47:34 +00001546 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1547 (data[3] == 1))
1548 {
1549 check=(png_size_t) ReadBlob(image,(size_t) length,
1550 (char *) mng_info->read_buffer);
1551 mng_info->read_buffer[4]=0;
1552 mng_info->bytes_in_read_buffer=4;
1553 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1554 if (mng_info->found_empty_plte)
1555 {
1556 /*
1557 Skip the bKGD data byte and CRC.
1558 */
1559 check=(png_size_t)
1560 ReadBlob(image,5,(char *) mng_info->read_buffer);
1561 check=(png_size_t) ReadBlob(image,(size_t) length,
1562 (char *) mng_info->read_buffer);
1563 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1564 mng_info->have_saved_bkgd_index=MagickTrue;
1565 mng_info->bytes_in_read_buffer=0;
1566 }
1567 }
1568 }
1569 }
1570}
1571#endif
1572
1573static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1574{
1575 Image
1576 *image;
1577
1578 image=(Image *) png_get_io_ptr(png_ptr);
1579 if (length)
1580 {
1581 png_size_t
1582 check;
1583
cristybb503372010-05-27 20:51:26 +00001584 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001585
cristy3ed852e2009-09-05 21:47:34 +00001586 if (check != length)
1587 png_error(png_ptr,"WriteBlob Failed");
1588 }
1589}
1590
1591static void png_flush_data(png_structp png_ptr)
1592{
1593 (void) png_ptr;
1594}
1595
1596#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1597static int PalettesAreEqual(Image *a,Image *b)
1598{
cristybb503372010-05-27 20:51:26 +00001599 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001600 i;
1601
1602 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1603 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001604
cristy3ed852e2009-09-05 21:47:34 +00001605 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1606 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001607
cristy3ed852e2009-09-05 21:47:34 +00001608 if (a->colors != b->colors)
1609 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001610
cristybb503372010-05-27 20:51:26 +00001611 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001612 {
1613 if ((a->colormap[i].red != b->colormap[i].red) ||
1614 (a->colormap[i].green != b->colormap[i].green) ||
1615 (a->colormap[i].blue != b->colormap[i].blue))
1616 return((int) MagickFalse);
1617 }
glennrp0fe50b42010-11-16 03:52:51 +00001618
cristy3ed852e2009-09-05 21:47:34 +00001619 return((int) MagickTrue);
1620}
1621#endif
1622
1623static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1624{
1625 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1626 mng_info->exists[i] && !mng_info->frozen[i])
1627 {
1628#ifdef MNG_OBJECT_BUFFERS
1629 if (mng_info->ob[i] != (MngBuffer *) NULL)
1630 {
1631 if (mng_info->ob[i]->reference_count > 0)
1632 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001633
cristy3ed852e2009-09-05 21:47:34 +00001634 if (mng_info->ob[i]->reference_count == 0)
1635 {
1636 if (mng_info->ob[i]->image != (Image *) NULL)
1637 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001638
cristy3ed852e2009-09-05 21:47:34 +00001639 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1640 }
1641 }
1642 mng_info->ob[i]=(MngBuffer *) NULL;
1643#endif
1644 mng_info->exists[i]=MagickFalse;
1645 mng_info->invisible[i]=MagickFalse;
1646 mng_info->viewable[i]=MagickFalse;
1647 mng_info->frozen[i]=MagickFalse;
1648 mng_info->x_off[i]=0;
1649 mng_info->y_off[i]=0;
1650 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001651 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001652 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001653 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001654 }
1655}
1656
glennrp21f0e622011-01-07 16:20:57 +00001657static void MngInfoFreeStruct(MngInfo *mng_info,
1658 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001659{
glennrp21f0e622011-01-07 16:20:57 +00001660 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001661 {
cristybb503372010-05-27 20:51:26 +00001662 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001663 i;
1664
1665 for (i=1; i < MNG_MAX_OBJECTS; i++)
1666 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001667
cristy3ed852e2009-09-05 21:47:34 +00001668 if (mng_info->global_plte != (png_colorp) NULL)
1669 mng_info->global_plte=(png_colorp)
1670 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001671
cristy3ed852e2009-09-05 21:47:34 +00001672 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1673 *have_mng_structure=MagickFalse;
1674 }
1675}
1676
1677static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1678{
1679 MngBox
1680 box;
1681
1682 box=box1;
1683 if (box.left < box2.left)
1684 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001685
cristy3ed852e2009-09-05 21:47:34 +00001686 if (box.top < box2.top)
1687 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001688
cristy3ed852e2009-09-05 21:47:34 +00001689 if (box.right > box2.right)
1690 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001691
cristy3ed852e2009-09-05 21:47:34 +00001692 if (box.bottom > box2.bottom)
1693 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001694
cristy3ed852e2009-09-05 21:47:34 +00001695 return box;
1696}
1697
1698static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1699{
1700 MngBox
1701 box;
1702
1703 /*
1704 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1705 */
cristybb503372010-05-27 20:51:26 +00001706 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1707 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1708 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1709 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001710 if (delta_type != 0)
1711 {
1712 box.left+=previous_box.left;
1713 box.right+=previous_box.right;
1714 box.top+=previous_box.top;
1715 box.bottom+=previous_box.bottom;
1716 }
glennrp0fe50b42010-11-16 03:52:51 +00001717
cristy3ed852e2009-09-05 21:47:34 +00001718 return(box);
1719}
1720
1721static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1722 unsigned char *p)
1723{
1724 MngPair
1725 pair;
1726 /*
cristybb503372010-05-27 20:51:26 +00001727 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001728 */
cristy8182b072010-05-30 20:10:53 +00001729 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1730 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001731
cristy3ed852e2009-09-05 21:47:34 +00001732 if (delta_type != 0)
1733 {
1734 pair.a+=previous_pair.a;
1735 pair.b+=previous_pair.b;
1736 }
glennrp0fe50b42010-11-16 03:52:51 +00001737
cristy3ed852e2009-09-05 21:47:34 +00001738 return(pair);
1739}
1740
cristy8182b072010-05-30 20:10:53 +00001741static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001742{
cristy8182b072010-05-30 20:10:53 +00001743 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001744}
1745
cristy16ea1392012-03-21 20:38:41 +00001746typedef struct _PNGErrorInfo
cristyc82a27b2011-10-21 01:07:16 +00001747{
cristyc82a27b2011-10-21 01:07:16 +00001748 Image
1749 *image;
1750
cristy16ea1392012-03-21 20:38:41 +00001751 ExceptionInfo
1752 *exception;
1753} PNGErrorInfo;
cristyc82a27b2011-10-21 01:07:16 +00001754
cristy16ea1392012-03-21 20:38:41 +00001755static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1756{
1757 ExceptionInfo
1758 *exception;
cristyc82a27b2011-10-21 01:07:16 +00001759
cristy16ea1392012-03-21 20:38:41 +00001760 Image
1761 *image;
1762
1763 PNGErrorInfo
1764 *error_info;
1765
1766 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1767 image=error_info->image;
1768 exception=error_info->exception;
1769
1770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1771 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1772
1773 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1774 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001775
glennrpe4017e32011-01-08 17:16:09 +00001776#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001777 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1778 * are building with libpng-1.4.x and can be ignored.
1779 */
cristy3ed852e2009-09-05 21:47:34 +00001780 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001781#else
1782 png_longjmp(ping,1);
1783#endif
cristy3ed852e2009-09-05 21:47:34 +00001784}
1785
glennrpcf002022011-01-30 02:38:15 +00001786static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001787{
cristy16ea1392012-03-21 20:38:41 +00001788 ExceptionInfo
1789 *exception;
1790
cristy3ed852e2009-09-05 21:47:34 +00001791 Image
1792 *image;
1793
cristy16ea1392012-03-21 20:38:41 +00001794 PNGErrorInfo
1795 *error_info;
1796
cristy3ed852e2009-09-05 21:47:34 +00001797 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1798 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001799
cristy16ea1392012-03-21 20:38:41 +00001800 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1801 image=error_info->image;
1802 exception=error_info->exception;
1803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1804 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001805
cristy16ea1392012-03-21 20:38:41 +00001806 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001807 message,"`%s'",image->filename);
1808}
1809
1810#ifdef PNG_USER_MEM_SUPPORTED
cristya865ccd2012-07-28 00:33:10 +00001811#if PNG_LIBPNG_VER >= 14000
1812static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1813#else
1814static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1815#endif
cristy3ed852e2009-09-05 21:47:34 +00001816{
cristydf0d90e2011-12-12 01:03:55 +00001817 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001818 return((png_voidp) AcquireMagickMemory((size_t) size));
cristy3ed852e2009-09-05 21:47:34 +00001819}
1820
1821/*
1822 Free a pointer. It is removed from the list at the same time.
1823*/
glennrpcf002022011-01-30 02:38:15 +00001824static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001825{
glennrp3bd393f2011-12-21 18:54:53 +00001826 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001827 ptr=RelinquishMagickMemory(ptr);
1828 return((png_free_ptr) NULL);
1829}
1830#endif
1831
1832#if defined(__cplusplus) || defined(c_plusplus)
1833}
1834#endif
1835
1836static int
glennrpedaa0382012-04-12 14:16:21 +00001837Magick_png_read_raw_profile(png_struct *ping,Image *image,
1838 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001839{
cristybb503372010-05-27 20:51:26 +00001840 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001841 i;
1842
1843 register unsigned char
1844 *dp;
1845
1846 register png_charp
1847 sp;
1848
1849 png_uint_32
1850 length,
1851 nibbles;
1852
1853 StringInfo
1854 *profile;
1855
glennrp0c3e06b2010-11-19 13:45:02 +00001856 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001857 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1858 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1859 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1860 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1861 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1862 13,14,15};
1863
1864 sp=text[ii].text+1;
1865 /* look for newline */
1866 while (*sp != '\n')
1867 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001868
cristy3ed852e2009-09-05 21:47:34 +00001869 /* look for length */
1870 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1871 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001872
cristyf2f27272009-12-17 14:48:46 +00001873 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001874
glennrp97f90e22011-02-22 05:47:58 +00001875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1876 " length: %lu",(unsigned long) length);
1877
cristy3ed852e2009-09-05 21:47:34 +00001878 while (*sp != ' ' && *sp != '\n')
1879 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001880
cristy3ed852e2009-09-05 21:47:34 +00001881 /* allocate space */
1882 if (length == 0)
1883 {
glennrpedaa0382012-04-12 14:16:21 +00001884 png_warning(ping,"invalid profile length");
cristy3ed852e2009-09-05 21:47:34 +00001885 return(MagickFalse);
1886 }
glennrp0fe50b42010-11-16 03:52:51 +00001887
cristy8723e4b2011-09-01 13:11:19 +00001888 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001889
cristy3ed852e2009-09-05 21:47:34 +00001890 if (profile == (StringInfo *) NULL)
1891 {
glennrpedaa0382012-04-12 14:16:21 +00001892 png_warning(ping, "unable to copy profile");
cristy3ed852e2009-09-05 21:47:34 +00001893 return(MagickFalse);
1894 }
glennrp0fe50b42010-11-16 03:52:51 +00001895
cristy3ed852e2009-09-05 21:47:34 +00001896 /* copy profile, skipping white space and column 1 "=" signs */
1897 dp=GetStringInfoDatum(profile);
1898 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001899
cristybb503372010-05-27 20:51:26 +00001900 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001901 {
1902 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1903 {
1904 if (*sp == '\0')
1905 {
glennrpedaa0382012-04-12 14:16:21 +00001906 png_warning(ping, "ran out of profile data");
cristy3ed852e2009-09-05 21:47:34 +00001907 profile=DestroyStringInfo(profile);
1908 return(MagickFalse);
1909 }
1910 sp++;
1911 }
glennrp0fe50b42010-11-16 03:52:51 +00001912
cristy3ed852e2009-09-05 21:47:34 +00001913 if (i%2 == 0)
1914 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001915
cristy3ed852e2009-09-05 21:47:34 +00001916 else
1917 (*dp++)+=unhex[(int) *sp++];
1918 }
1919 /*
1920 We have already read "Raw profile type.
1921 */
cristy16ea1392012-03-21 20:38:41 +00001922 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001923 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001924
cristy3ed852e2009-09-05 21:47:34 +00001925 if (image_info->verbose)
1926 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001927
cristy3ed852e2009-09-05 21:47:34 +00001928 return MagickTrue;
1929}
1930
1931#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1932static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1933{
1934 Image
1935 *image;
1936
1937
1938 /* The unknown chunk structure contains the chunk data:
1939 png_byte name[5];
1940 png_byte *data;
1941 png_size_t size;
1942
1943 Note that libpng has already taken care of the CRC handling.
1944 */
1945
1946
1947 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1948 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1949 return(0); /* Did not recognize */
1950
1951 /* recognized vpAg */
1952
1953 if (chunk->size != 9)
1954 return(-1); /* Error return */
1955
1956 if (chunk->data[8] != 0)
1957 return(0); /* ImageMagick requires pixel units */
1958
1959 image=(Image *) png_get_user_chunk_ptr(ping);
1960
cristybb503372010-05-27 20:51:26 +00001961 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001962 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001963
cristybb503372010-05-27 20:51:26 +00001964 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001965 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1966
1967 /* Return one of the following: */
1968 /* return(-n); chunk had an error */
1969 /* return(0); did not recognize */
1970 /* return(n); success */
1971
1972 return(1);
1973
1974}
1975#endif
1976
1977/*
1978%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1979% %
1980% %
1981% %
1982% R e a d O n e P N G I m a g e %
1983% %
1984% %
1985% %
1986%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1987%
1988% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1989% (minus the 8-byte signature) and returns it. It allocates the memory
1990% necessary for the new Image structure and returns a pointer to the new
1991% image.
1992%
1993% The format of the ReadOnePNGImage method is:
1994%
1995% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1996% ExceptionInfo *exception)
1997%
1998% A description of each parameter follows:
1999%
2000% o mng_info: Specifies a pointer to a MngInfo structure.
2001%
2002% o image_info: the image info.
2003%
2004% o exception: return any errors or warnings in this structure.
2005%
2006*/
2007static Image *ReadOnePNGImage(MngInfo *mng_info,
2008 const ImageInfo *image_info, ExceptionInfo *exception)
2009{
2010 /* Read one PNG image */
2011
glennrpcc95c3f2011-04-18 16:46:48 +00002012 /* To do: Read the tIME chunk into the date:modify property */
2013 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2014
cristy3ed852e2009-09-05 21:47:34 +00002015 Image
2016 *image;
2017
2018 int
glennrp98b83d42012-07-23 02:50:31 +00002019 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
glennrpcb395ac2011-03-30 19:50:23 +00002020 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00002021 num_text,
glennrp4eb39312011-03-30 21:34:55 +00002022 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00002023 num_passes,
glennrp913f9612012-06-27 17:50:00 +00002024 number_colors,
glennrpfaa852b2010-03-30 12:17:00 +00002025 pass,
2026 ping_bit_depth,
2027 ping_color_type,
2028 ping_interlace_method,
2029 ping_compression_method,
2030 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002031 ping_num_trans,
2032 unit_type;
2033
2034 double
2035 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002036
2037 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002038 logging,
glennrp98b83d42012-07-23 02:50:31 +00002039 ping_found_cHRM,
2040 ping_found_gAMA,
2041 ping_found_iCCP,
2042 ping_found_sRGB,
cristy3ed852e2009-09-05 21:47:34 +00002043 status;
2044
cristy16ea1392012-03-21 20:38:41 +00002045 PixelInfo
2046 transparent_color;
2047
2048 PNGErrorInfo
2049 error_info;
2050
glennrpfaa852b2010-03-30 12:17:00 +00002051 png_bytep
2052 ping_trans_alpha;
2053
2054 png_color_16p
2055 ping_background,
2056 ping_trans_color;
2057
cristy3ed852e2009-09-05 21:47:34 +00002058 png_info
2059 *end_info,
2060 *ping_info;
2061
2062 png_struct
2063 *ping;
2064
2065 png_textp
2066 text;
2067
glennrpfaa852b2010-03-30 12:17:00 +00002068 png_uint_32
2069 ping_height,
2070 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002071 x_resolution,
2072 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002073
cristy16ea1392012-03-21 20:38:41 +00002074 QuantumInfo
2075 *quantum_info;
2076
cristy3ed852e2009-09-05 21:47:34 +00002077 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002078 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002079
cristybb503372010-05-27 20:51:26 +00002080 ssize_t
cristy756ae432011-11-19 02:18:25 +00002081 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002082 y;
2083
2084 register unsigned char
2085 *p;
2086
cristybb503372010-05-27 20:51:26 +00002087 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002088 i,
2089 x;
2090
cristy16ea1392012-03-21 20:38:41 +00002091 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002092 *q;
2093
2094 size_t
glennrp39992b42010-11-14 00:03:43 +00002095 length,
cristy3ed852e2009-09-05 21:47:34 +00002096 row_offset;
2097
cristyeb3b22a2011-03-31 20:16:11 +00002098 ssize_t
2099 j;
2100
glennrp629960f2012-05-29 19:13:52 +00002101#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002102 png_byte unused_chunks[]=
2103 {
2104 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2105 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2106 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2107 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2108 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2109 116, 73, 77, 69, (png_byte) '\0', /* tIME */
glennrp629960f2012-05-29 19:13:52 +00002110#ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2111 /* ignore the APNG chunks */
2112 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2113 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2114 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2115#endif
cristy3ed852e2009-09-05 21:47:34 +00002116 };
2117#endif
2118
2119 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002120 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002121
glennrp25c1e2b2010-03-25 01:39:56 +00002122#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002123 if (image_info->verbose)
2124 printf("Your PNG library (libpng-%s) is rather old.\n",
2125 PNG_LIBPNG_VER_STRING);
2126#endif
2127
glennrp61b4c952009-11-10 20:40:41 +00002128#if (PNG_LIBPNG_VER >= 10400)
2129# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2130 if (image_info->verbose)
2131 {
2132 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2133 PNG_LIBPNG_VER_STRING);
2134 printf("Please update it.\n");
2135 }
2136# endif
2137#endif
2138
cristy16ea1392012-03-21 20:38:41 +00002139
2140 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002141 image=mng_info->image;
2142
glennrpa6a06632011-01-19 15:15:34 +00002143 if (logging != MagickFalse)
glennrp98b83d42012-07-23 02:50:31 +00002144 {
glennrpa6a06632011-01-19 15:15:34 +00002145 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2146 " image->matte=%d",(int) image->matte);
2147
glennrp98b83d42012-07-23 02:50:31 +00002148 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2149 " image->rendering_intent=%d",(int) image->rendering_intent);
glennrpe88af772012-08-22 13:59:50 +00002150
2151 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2152 " image->colorspace=%d",(int) image->colorspace);
glennrp98b83d42012-07-23 02:50:31 +00002153 }
2154 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2155
glennrp0e319732011-01-25 21:53:13 +00002156 /* Set to an out-of-range color unless tRNS chunk is present */
2157 transparent_color.red=65537;
2158 transparent_color.green=65537;
2159 transparent_color.blue=65537;
cristy16ea1392012-03-21 20:38:41 +00002160 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002161
glennrp913f9612012-06-27 17:50:00 +00002162 number_colors=0;
glennrpcb395ac2011-03-30 19:50:23 +00002163 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002164 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002165 num_raw_profiles = 0;
2166
glennrp98b83d42012-07-23 02:50:31 +00002167 ping_found_cHRM = MagickFalse;
2168 ping_found_gAMA = MagickFalse;
2169 ping_found_iCCP = MagickFalse;
2170 ping_found_sRGB = MagickFalse;
2171
cristy3ed852e2009-09-05 21:47:34 +00002172 /*
2173 Allocate the PNG structures
2174 */
2175#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00002176 error_info.image=image;
2177 error_info.exception=exception;
2178 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002179 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2180 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002181#else
cristy16ea1392012-03-21 20:38:41 +00002182 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002183 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002184#endif
2185 if (ping == (png_struct *) NULL)
2186 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002187
cristy3ed852e2009-09-05 21:47:34 +00002188 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002189
cristy3ed852e2009-09-05 21:47:34 +00002190 if (ping_info == (png_info *) NULL)
2191 {
2192 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2193 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2194 }
glennrp0fe50b42010-11-16 03:52:51 +00002195
cristy3ed852e2009-09-05 21:47:34 +00002196 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002197
cristy3ed852e2009-09-05 21:47:34 +00002198 if (end_info == (png_info *) NULL)
2199 {
2200 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2201 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2202 }
glennrp0fe50b42010-11-16 03:52:51 +00002203
glennrpcf002022011-01-30 02:38:15 +00002204 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002205
glennrpfaa852b2010-03-30 12:17:00 +00002206 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002207 {
2208 /*
2209 PNG image is corrupt.
2210 */
2211 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002212
2213#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002214 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002215#endif
glennrpedaa0382012-04-12 14:16:21 +00002216
2217 if (ping_pixels != (unsigned char *) NULL)
2218 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2219
cristy3ed852e2009-09-05 21:47:34 +00002220 if (logging != MagickFalse)
2221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2222 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002223
cristy3ed852e2009-09-05 21:47:34 +00002224 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002225 {
cristy16ea1392012-03-21 20:38:41 +00002226 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002227 image->columns=0;
2228 }
glennrp0fe50b42010-11-16 03:52:51 +00002229
cristy3ed852e2009-09-05 21:47:34 +00002230 return(GetFirstImageInList(image));
2231 }
glennrpedaa0382012-04-12 14:16:21 +00002232
2233 /* { For navigation to end of SETJMP-protected block. Within this
2234 * block, use png_error() instead of Throwing an Exception, to ensure
2235 * that libpng is able to clean up, and that the semaphore is unlocked.
2236 */
2237
2238#ifdef PNG_SETJMP_NOT_THREAD_SAFE
2239 LockSemaphoreInfo(ping_semaphore);
2240#endif
2241
cristy3ed852e2009-09-05 21:47:34 +00002242 /*
2243 Prepare PNG for reading.
2244 */
glennrpfaa852b2010-03-30 12:17:00 +00002245
cristy3ed852e2009-09-05 21:47:34 +00002246 mng_info->image_found++;
2247 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002248
cristy3ed852e2009-09-05 21:47:34 +00002249 if (LocaleCompare(image_info->magick,"MNG") == 0)
2250 {
2251#if defined(PNG_MNG_FEATURES_SUPPORTED)
2252 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2253 png_set_read_fn(ping,image,png_get_data);
2254#else
2255#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2256 png_permit_empty_plte(ping,MagickTrue);
2257 png_set_read_fn(ping,image,png_get_data);
2258#else
2259 mng_info->image=image;
2260 mng_info->bytes_in_read_buffer=0;
2261 mng_info->found_empty_plte=MagickFalse;
2262 mng_info->have_saved_bkgd_index=MagickFalse;
2263 png_set_read_fn(ping,mng_info,mng_get_data);
2264#endif
2265#endif
2266 }
glennrp0fe50b42010-11-16 03:52:51 +00002267
cristy3ed852e2009-09-05 21:47:34 +00002268 else
2269 png_set_read_fn(ping,image,png_get_data);
2270
2271#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2272 /* Ignore unused chunks and all unknown chunks except for vpAg */
2273 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2274 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2275 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2276 (int)sizeof(unused_chunks)/5);
2277 /* Callback for other unknown chunks */
2278 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2279#endif
2280
glennrp9bf97b62012-06-06 21:03:14 +00002281#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2282 /* Disable new libpng-1.5.10 feature */
2283 png_set_check_for_invalid_index (ping, 0);
2284#endif
2285
glennrp991e92a2010-01-28 03:09:00 +00002286#if (PNG_LIBPNG_VER < 10400)
2287# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2288 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002289 /* Disable thread-unsafe features of pnggccrd */
2290 if (png_access_version_number() >= 10200)
2291 {
2292 png_uint_32 mmx_disable_mask=0;
2293 png_uint_32 asm_flags;
2294
2295 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2296 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2297 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2298 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2299 asm_flags=png_get_asm_flags(ping);
2300 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2301 }
glennrp991e92a2010-01-28 03:09:00 +00002302# endif
cristy3ed852e2009-09-05 21:47:34 +00002303#endif
2304
2305 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002306
2307 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2308 &ping_bit_depth,&ping_color_type,
2309 &ping_interlace_method,&ping_compression_method,
2310 &ping_filter_method);
2311
2312 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2313 &ping_trans_color);
2314
2315 (void) png_get_bKGD(ping, ping_info, &ping_background);
2316
2317 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002318 {
glennrpfaa852b2010-03-30 12:17:00 +00002319 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2320 {
2321 png_set_packing(ping);
2322 ping_bit_depth = 8;
2323 }
cristy3ed852e2009-09-05 21:47:34 +00002324 }
glennrpfaa852b2010-03-30 12:17:00 +00002325
2326 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002327 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002328 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
glennrp98b83d42012-07-23 02:50:31 +00002329
cristy176b29a2012-06-21 13:35:15 +00002330 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2331 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2332 {
2333 image->rendering_intent=UndefinedIntent;
glennrp98b83d42012-07-23 02:50:31 +00002334 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
cristy176b29a2012-06-21 13:35:15 +00002335 image->gamma=1.000;
2336 (void) ResetMagickMemory(&image->chromaticity,0,
2337 sizeof(image->chromaticity));
2338 }
glennrp98b83d42012-07-23 02:50:31 +00002339
cristy3ed852e2009-09-05 21:47:34 +00002340 if (logging != MagickFalse)
2341 {
2342 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002343 " PNG width: %.20g, height: %.20g",
2344 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002345
cristy3ed852e2009-09-05 21:47:34 +00002346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2347 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002348 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002349
cristy3ed852e2009-09-05 21:47:34 +00002350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2351 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002352 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002353
cristy3ed852e2009-09-05 21:47:34 +00002354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2355 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002356 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002357 }
2358
glennrp98b83d42012-07-23 02:50:31 +00002359 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2360 {
2361 ping_found_gAMA=MagickTrue;
2362 if (logging != MagickFalse)
2363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2364 " Found PNG gAMA chunk.");
2365 }
2366
2367 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2368 {
2369 ping_found_cHRM=MagickTrue;
2370 if (logging != MagickFalse)
2371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2372 " Found PNG cHRM chunk.");
2373 }
2374
2375 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2376 {
2377 ping_found_iCCP=MagickTrue;
2378 if (logging != MagickFalse)
2379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2380 " Found PNG iCCP chunk.");
2381 }
2382
2383 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2384 {
2385 ping_found_sRGB=MagickTrue;
2386 if (logging != MagickFalse)
2387 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2388 " Found PNG sRGB chunk.");
2389 }
2390
glennrpfaa852b2010-03-30 12:17:00 +00002391#ifdef PNG_READ_iCCP_SUPPORTED
2392 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002393 {
2394 int
2395 compression;
2396
glennrpe4017e32011-01-08 17:16:09 +00002397#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002398 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002399 info;
2400#else
2401 png_bytep
2402 info;
2403#endif
2404
2405 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002406 name;
2407
2408 png_uint_32
2409 profile_length;
2410
2411 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2412 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002413
cristy3ed852e2009-09-05 21:47:34 +00002414 if (profile_length != 0)
2415 {
2416 StringInfo
2417 *profile;
2418
2419 if (logging != MagickFalse)
2420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2421 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002422 profile=BlobToStringInfo(info,profile_length);
2423 if (profile == (StringInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00002424 {
2425 png_warning(ping, "ICC profile is NULL");
2426 profile=DestroyStringInfo(profile);
2427 }
2428 else
2429 {
2430 (void) SetImageProfile(image,"icc",profile,exception);
2431 profile=DestroyStringInfo(profile);
2432 }
cristy3ed852e2009-09-05 21:47:34 +00002433 }
2434 }
2435#endif
2436#if defined(PNG_READ_sRGB_SUPPORTED)
2437 {
cristy3ed852e2009-09-05 21:47:34 +00002438 if (mng_info->have_global_srgb)
cristy2ea7a8e2012-02-08 01:04:50 +00002439 {
cristy2ea7a8e2012-02-08 01:04:50 +00002440 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2441 (mng_info->global_srgb_intent);
2442 }
glennrp0fe50b42010-11-16 03:52:51 +00002443
cristy3ed852e2009-09-05 21:47:34 +00002444 if (png_get_sRGB(ping,ping_info,&intent))
2445 {
glennrpcf002022011-01-30 02:38:15 +00002446 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2447 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002448
cristy3ed852e2009-09-05 21:47:34 +00002449 if (logging != MagickFalse)
2450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002451 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002452 }
2453 }
2454#endif
2455 {
glennrpfaa852b2010-03-30 12:17:00 +00002456 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2457 if (mng_info->have_global_gama)
2458 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002459
cristy3ed852e2009-09-05 21:47:34 +00002460 if (png_get_gAMA(ping,ping_info,&file_gamma))
2461 {
2462 image->gamma=(float) file_gamma;
2463 if (logging != MagickFalse)
2464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2465 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2466 }
2467 }
glennrp98b83d42012-07-23 02:50:31 +00002468
glennrpfaa852b2010-03-30 12:17:00 +00002469 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2470 {
2471 if (mng_info->have_global_chrm != MagickFalse)
2472 {
2473 (void) png_set_cHRM(ping,ping_info,
2474 mng_info->global_chrm.white_point.x,
2475 mng_info->global_chrm.white_point.y,
2476 mng_info->global_chrm.red_primary.x,
2477 mng_info->global_chrm.red_primary.y,
2478 mng_info->global_chrm.green_primary.x,
2479 mng_info->global_chrm.green_primary.y,
2480 mng_info->global_chrm.blue_primary.x,
2481 mng_info->global_chrm.blue_primary.y);
2482 }
2483 }
glennrp0fe50b42010-11-16 03:52:51 +00002484
glennrpfaa852b2010-03-30 12:17:00 +00002485 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002486 {
2487 (void) png_get_cHRM(ping,ping_info,
2488 &image->chromaticity.white_point.x,
2489 &image->chromaticity.white_point.y,
2490 &image->chromaticity.red_primary.x,
2491 &image->chromaticity.red_primary.y,
2492 &image->chromaticity.green_primary.x,
2493 &image->chromaticity.green_primary.y,
2494 &image->chromaticity.blue_primary.x,
2495 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002496
cristy3ed852e2009-09-05 21:47:34 +00002497 }
glennrp0fe50b42010-11-16 03:52:51 +00002498
glennrpe610a072010-08-05 17:08:46 +00002499 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002500 {
glennrpe610a072010-08-05 17:08:46 +00002501 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002502 Magick_RenderingIntent_to_PNG_RenderingIntent
2503 (image->rendering_intent));
cristyda7803d2012-05-03 01:22:45 +00002504 png_set_gAMA(ping,ping_info,1.000f/2.200f);
glennrpfaa852b2010-03-30 12:17:00 +00002505 png_set_cHRM(ping,ping_info,
2506 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2507 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002508 }
cristy3ed852e2009-09-05 21:47:34 +00002509#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002510 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002511 {
cristy905ef802011-02-23 00:29:18 +00002512 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2513 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002514
cristy3ed852e2009-09-05 21:47:34 +00002515 if (logging != MagickFalse)
2516 if (image->page.x || image->page.y)
2517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002518 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2519 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002520 }
2521#endif
2522#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002523 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2524 {
2525 if (mng_info->have_global_phys)
2526 {
2527 png_set_pHYs(ping,ping_info,
2528 mng_info->global_x_pixels_per_unit,
2529 mng_info->global_y_pixels_per_unit,
2530 mng_info->global_phys_unit_type);
2531 }
2532 }
2533
2534 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002535 {
cristy3ed852e2009-09-05 21:47:34 +00002536 /*
2537 Set image resolution.
2538 */
2539 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002540 &unit_type);
cristy16ea1392012-03-21 20:38:41 +00002541 image->resolution.x=(double) x_resolution;
2542 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002543
cristy3ed852e2009-09-05 21:47:34 +00002544 if (unit_type == PNG_RESOLUTION_METER)
2545 {
2546 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00002547 image->resolution.x=(double) x_resolution/100.0;
2548 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002549 }
glennrp0fe50b42010-11-16 03:52:51 +00002550
cristy3ed852e2009-09-05 21:47:34 +00002551 if (logging != MagickFalse)
2552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002553 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2554 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002555 }
cristy3ed852e2009-09-05 21:47:34 +00002556#endif
glennrp823b55c2011-03-14 18:46:46 +00002557
glennrpfaa852b2010-03-30 12:17:00 +00002558 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002559 {
cristy3ed852e2009-09-05 21:47:34 +00002560 png_colorp
2561 palette;
2562
2563 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002564
cristy3ed852e2009-09-05 21:47:34 +00002565 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002566 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002567 {
2568 if (mng_info->global_plte_length)
2569 {
2570 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2571 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002572
glennrpfaa852b2010-03-30 12:17:00 +00002573 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrpedaa0382012-04-12 14:16:21 +00002574 {
cristy3ed852e2009-09-05 21:47:34 +00002575 if (mng_info->global_trns_length)
2576 {
glennrpedaa0382012-04-12 14:16:21 +00002577 png_warning(ping,
2578 "global tRNS has more entries than global PLTE");
cristy3ed852e2009-09-05 21:47:34 +00002579 }
glennrpedaa0382012-04-12 14:16:21 +00002580 else
2581 {
2582 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2583 (int) mng_info->global_trns_length,NULL);
2584 }
2585 }
glennrpbfd9e612011-04-22 14:02:20 +00002586#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002587 if (
2588#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2589 mng_info->have_saved_bkgd_index ||
2590#endif
glennrpfaa852b2010-03-30 12:17:00 +00002591 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002592 {
2593 png_color_16
2594 background;
2595
2596#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2597 if (mng_info->have_saved_bkgd_index)
2598 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002599#endif
glennrpfaa852b2010-03-30 12:17:00 +00002600 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2601 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002602
cristy3ed852e2009-09-05 21:47:34 +00002603 background.red=(png_uint_16)
2604 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002605
cristy3ed852e2009-09-05 21:47:34 +00002606 background.green=(png_uint_16)
2607 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002608
cristy3ed852e2009-09-05 21:47:34 +00002609 background.blue=(png_uint_16)
2610 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002611
glennrpc6c391a2011-04-27 02:23:56 +00002612 background.gray=(png_uint_16)
2613 mng_info->global_plte[background.index].green;
2614
cristy3ed852e2009-09-05 21:47:34 +00002615 png_set_bKGD(ping,ping_info,&background);
2616 }
2617#endif
2618 }
2619 else
glennrpedaa0382012-04-12 14:16:21 +00002620 png_error(ping,"No global PLTE in file");
cristy3ed852e2009-09-05 21:47:34 +00002621 }
2622 }
2623
glennrpbfd9e612011-04-22 14:02:20 +00002624#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002625 if (mng_info->have_global_bkgd &&
2626 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002627 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002628
glennrpfaa852b2010-03-30 12:17:00 +00002629 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002630 {
glennrpbfd9e612011-04-22 14:02:20 +00002631 unsigned int
2632 bkgd_scale;
2633
cristy3ed852e2009-09-05 21:47:34 +00002634 /*
2635 Set image background color.
2636 */
2637 if (logging != MagickFalse)
2638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2639 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002640
glennrpbfd9e612011-04-22 14:02:20 +00002641 /* Scale background components to 16-bit, then scale
2642 * to quantum depth
2643 */
2644 if (logging != MagickFalse)
2645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2646 " raw ping_background=(%d,%d,%d).",ping_background->red,
2647 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002648
glennrpbfd9e612011-04-22 14:02:20 +00002649 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002650
glennrpbfd9e612011-04-22 14:02:20 +00002651 if (ping_bit_depth == 1)
2652 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002653
glennrpbfd9e612011-04-22 14:02:20 +00002654 else if (ping_bit_depth == 2)
2655 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002656
glennrpbfd9e612011-04-22 14:02:20 +00002657 else if (ping_bit_depth == 4)
2658 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002659
glennrpbfd9e612011-04-22 14:02:20 +00002660 if (ping_bit_depth <= 8)
2661 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002662
glennrpbfd9e612011-04-22 14:02:20 +00002663 ping_background->red *= bkgd_scale;
2664 ping_background->green *= bkgd_scale;
2665 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002666
glennrpbfd9e612011-04-22 14:02:20 +00002667 if (logging != MagickFalse)
2668 {
glennrp2cbb4482010-06-02 04:37:24 +00002669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2670 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002671
glennrp2cbb4482010-06-02 04:37:24 +00002672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2673 " ping_background=(%d,%d,%d).",ping_background->red,
2674 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002675 }
glennrp2cbb4482010-06-02 04:37:24 +00002676
glennrpbfd9e612011-04-22 14:02:20 +00002677 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002678 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002679
glennrpbfd9e612011-04-22 14:02:20 +00002680 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002681 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002682
glennrpbfd9e612011-04-22 14:02:20 +00002683 image->background_color.blue=
2684 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002685
cristy16ea1392012-03-21 20:38:41 +00002686 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002687
glennrpbfd9e612011-04-22 14:02:20 +00002688 if (logging != MagickFalse)
2689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2690 " image->background_color=(%.20g,%.20g,%.20g).",
2691 (double) image->background_color.red,
2692 (double) image->background_color.green,
2693 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002694 }
glennrpbfd9e612011-04-22 14:02:20 +00002695#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002696
glennrpfaa852b2010-03-30 12:17:00 +00002697 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002698 {
2699 /*
glennrpa6a06632011-01-19 15:15:34 +00002700 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002701 */
2702 int
2703 max_sample;
2704
cristy35ef8242010-06-03 16:24:13 +00002705 size_t
2706 one=1;
2707
cristy3ed852e2009-09-05 21:47:34 +00002708 if (logging != MagickFalse)
2709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2710 " Reading PNG tRNS chunk.");
2711
cristyf9cca6a2010-06-04 23:49:28 +00002712 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002713
glennrpfaa852b2010-03-30 12:17:00 +00002714 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2715 (int)ping_trans_color->gray > max_sample) ||
2716 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2717 ((int)ping_trans_color->red > max_sample ||
2718 (int)ping_trans_color->green > max_sample ||
2719 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002720 {
2721 if (logging != MagickFalse)
2722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2723 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002724 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002725 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002726 image->matte=MagickFalse;
2727 }
2728 else
2729 {
glennrpa6a06632011-01-19 15:15:34 +00002730 int
2731 scale_to_short;
2732
2733 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2734
2735 /* Scale transparent_color to short */
2736 transparent_color.red= scale_to_short*ping_trans_color->red;
2737 transparent_color.green= scale_to_short*ping_trans_color->green;
2738 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy16ea1392012-03-21 20:38:41 +00002739 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002740
glennrpfaa852b2010-03-30 12:17:00 +00002741 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002742 {
glennrp0f111982010-07-07 20:18:33 +00002743 if (logging != MagickFalse)
2744 {
2745 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2746 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002747
glennrp0f111982010-07-07 20:18:33 +00002748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00002749 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002750 }
cristy16ea1392012-03-21 20:38:41 +00002751 transparent_color.red=transparent_color.alpha;
2752 transparent_color.green=transparent_color.alpha;
2753 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002754 }
2755 }
2756 }
2757#if defined(PNG_READ_sBIT_SUPPORTED)
2758 if (mng_info->have_global_sbit)
2759 {
glennrpfaa852b2010-03-30 12:17:00 +00002760 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002761 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2762 }
2763#endif
2764 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002765
cristy3ed852e2009-09-05 21:47:34 +00002766 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002767
2768 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2769
cristy3ed852e2009-09-05 21:47:34 +00002770 /*
2771 Initialize image structure.
2772 */
2773 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002774 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002775 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002776 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002777 if (mng_info->mng_type == 0)
2778 {
glennrpfaa852b2010-03-30 12:17:00 +00002779 mng_info->mng_width=ping_width;
2780 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002781 mng_info->frame=mng_info->image_box;
2782 mng_info->clip=mng_info->image_box;
2783 }
glennrp0fe50b42010-11-16 03:52:51 +00002784
cristy3ed852e2009-09-05 21:47:34 +00002785 else
2786 {
2787 image->page.y=mng_info->y_off[mng_info->object_id];
2788 }
glennrp0fe50b42010-11-16 03:52:51 +00002789
cristy3ed852e2009-09-05 21:47:34 +00002790 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002791 image->columns=ping_width;
2792 image->rows=ping_height;
glennrpa6c5d342012-05-14 13:21:17 +00002793
cristy16ea1392012-03-21 20:38:41 +00002794 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2795 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
glennrp8d0bca52012-05-17 02:33:23 +00002796 {
glennrpe88af772012-08-22 13:59:50 +00002797 if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
2798 image->gamma == 1.0) &&
glennrp8d0bca52012-05-17 02:33:23 +00002799 !png_get_valid(ping,ping_info,PNG_INFO_cHRM) &&
2800 !png_get_valid(ping,ping_info,PNG_INFO_sRGB))
2801 {
2802 /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
glennrpe88af772012-08-22 13:59:50 +00002803 * image->colorspace to GRAY, and reset image->chromaticity.
glennrp8d0bca52012-05-17 02:33:23 +00002804 */
2805 SetImageColorspace(image,GRAYColorspace,exception);
2806 }
glennrp8d0bca52012-05-17 02:33:23 +00002807 }
glennrpe88af772012-08-22 13:59:50 +00002808
2809 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2810 " image->colorspace=%d",(int) image->colorspace);
glennrpa6c5d342012-05-14 13:21:17 +00002811
glennrpfaa852b2010-03-30 12:17:00 +00002812 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002813 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002814 {
cristybefe4d22010-06-07 01:18:58 +00002815 size_t
2816 one;
2817
cristy3ed852e2009-09-05 21:47:34 +00002818 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002819 one=1;
2820 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002821#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2822 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002823 image->colors=256;
2824#else
2825 if (image->colors > 65536L)
2826 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002827#endif
glennrpfaa852b2010-03-30 12:17:00 +00002828 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002829 {
cristy3ed852e2009-09-05 21:47:34 +00002830 png_colorp
2831 palette;
2832
2833 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002834 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002835
cristy3ed852e2009-09-05 21:47:34 +00002836 if (logging != MagickFalse)
2837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2838 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2839 }
2840 }
2841
2842 if (image->storage_class == PseudoClass)
2843 {
2844 /*
2845 Initialize image colormap.
2846 */
cristy16ea1392012-03-21 20:38:41 +00002847 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
glennrpedaa0382012-04-12 14:16:21 +00002848 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00002849
glennrpfaa852b2010-03-30 12:17:00 +00002850 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002851 {
cristy3ed852e2009-09-05 21:47:34 +00002852 png_colorp
2853 palette;
2854
2855 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002856
glennrp6af6cf12011-04-22 13:05:16 +00002857 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002858 {
2859 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2860 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2861 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2862 }
glennrp6af6cf12011-04-22 13:05:16 +00002863
glennrp67b9c1a2011-04-22 18:47:36 +00002864 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002865 {
2866 image->colormap[i].red=0;
2867 image->colormap[i].green=0;
2868 image->colormap[i].blue=0;
2869 }
cristy3ed852e2009-09-05 21:47:34 +00002870 }
glennrp0fe50b42010-11-16 03:52:51 +00002871
cristy3ed852e2009-09-05 21:47:34 +00002872 else
2873 {
cristybb503372010-05-27 20:51:26 +00002874 size_t
cristy3ed852e2009-09-05 21:47:34 +00002875 scale;
2876
glennrpfaa852b2010-03-30 12:17:00 +00002877 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002878
cristy3ed852e2009-09-05 21:47:34 +00002879 if (scale < 1)
2880 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002881
cristybb503372010-05-27 20:51:26 +00002882 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002883 {
2884 image->colormap[i].red=(Quantum) (i*scale);
2885 image->colormap[i].green=(Quantum) (i*scale);
2886 image->colormap[i].blue=(Quantum) (i*scale);
2887 }
2888 }
2889 }
glennrp147bc912011-03-30 18:47:21 +00002890
glennrpcb395ac2011-03-30 19:50:23 +00002891 /* Set some properties for reporting by "identify" */
2892 {
glennrp147bc912011-03-30 18:47:21 +00002893 char
2894 msg[MaxTextExtent];
2895
2896 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2897 ping_interlace_method in value */
2898
cristy3b6fd2e2011-05-20 12:53:50 +00002899 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002900 "%d, %d",(int) ping_width, (int) ping_height);
cristy16ea1392012-03-21 20:38:41 +00002901 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002902
cristy3b6fd2e2011-05-20 12:53:50 +00002903 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristy16ea1392012-03-21 20:38:41 +00002904 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002905
glennrp5dff4352012-06-06 22:12:04 +00002906 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
2907 (int) ping_color_type,
2908 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
cristy16ea1392012-03-21 20:38:41 +00002909 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002910
glennrp913f9612012-06-27 17:50:00 +00002911 if (ping_interlace_method == 0)
2912 {
2913 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
2914 (int) ping_interlace_method);
2915 }
2916 else if (ping_interlace_method == 1)
2917 {
2918 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
2919 (int) ping_interlace_method);
2920 }
2921 else
2922 {
2923 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
2924 (int) ping_interlace_method);
2925 }
2926 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
2927
2928 if (number_colors != 0)
2929 {
2930 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
2931 (int) number_colors);
2932 (void) SetImageProperty(image,"png:PLTE.number_colors ",msg,
2933 exception);
2934 }
glennrpcb395ac2011-03-30 19:50:23 +00002935 }
glennrp147bc912011-03-30 18:47:21 +00002936
cristy3ed852e2009-09-05 21:47:34 +00002937 /*
2938 Read image scanlines.
2939 */
2940 if (image->delay != 0)
2941 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002942
glennrp0ca69b12010-07-26 01:57:52 +00002943 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002944 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2945 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002946 {
glennrp1b888c42012-03-02 15:18:14 +00002947 /* This happens later in non-ping decodes */
2948 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2949 image->storage_class=DirectClass;
2950
cristy3ed852e2009-09-05 21:47:34 +00002951 if (logging != MagickFalse)
2952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002953 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002954 mng_info->scenes_found-1);
2955 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002956
2957#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002958 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002959#endif
glennrpedaa0382012-04-12 14:16:21 +00002960
cristy3ed852e2009-09-05 21:47:34 +00002961 if (logging != MagickFalse)
2962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2963 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002964
cristy3ed852e2009-09-05 21:47:34 +00002965 return(image);
2966 }
glennrp0fe50b42010-11-16 03:52:51 +00002967
cristy3ed852e2009-09-05 21:47:34 +00002968 if (logging != MagickFalse)
2969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2970 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002971
cristy3ed852e2009-09-05 21:47:34 +00002972 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002973 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2974 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002975
cristy3ed852e2009-09-05 21:47:34 +00002976 else
glennrpcf002022011-01-30 02:38:15 +00002977 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2978 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002979
glennrpcf002022011-01-30 02:38:15 +00002980 if (ping_pixels == (unsigned char *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00002981 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00002982
cristy3ed852e2009-09-05 21:47:34 +00002983 if (logging != MagickFalse)
2984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2985 " Converting PNG pixels to pixel packets");
2986 /*
2987 Convert PNG pixels to pixel packets.
2988 */
cristy16ea1392012-03-21 20:38:41 +00002989 quantum_info=AcquireQuantumInfo(image_info,image);
2990
2991 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00002992 png_error(ping,"Failed to allocate quantum_info");
glennrp0fe50b42010-11-16 03:52:51 +00002993
glennrpc8cbc5d2011-01-01 00:12:34 +00002994 {
2995
2996 MagickBooleanType
2997 found_transparent_pixel;
2998
2999 found_transparent_pixel=MagickFalse;
3000
cristy3ed852e2009-09-05 21:47:34 +00003001 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00003002 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003003 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00003004 {
glennrpc8cbc5d2011-01-01 00:12:34 +00003005 /*
3006 Convert image to DirectClass pixel packets.
3007 */
glennrp67b9c1a2011-04-22 18:47:36 +00003008#if (MAGICKCORE_QUANTUM_DEPTH == 8)
3009 int
3010 depth;
3011
3012 depth=(ssize_t) ping_bit_depth;
3013#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00003014 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3015 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3016 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3017 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003018
glennrpc8cbc5d2011-01-01 00:12:34 +00003019 for (y=0; y < (ssize_t) image->rows; y++)
3020 {
3021 if (num_passes > 1)
3022 row_offset=ping_rowbytes*y;
3023
3024 else
3025 row_offset=0;
3026
glennrpcf002022011-01-30 02:38:15 +00003027 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy862a33c2012-05-17 22:49:37 +00003028 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003029
cristy16ea1392012-03-21 20:38:41 +00003030 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00003031 break;
3032
cristy16ea1392012-03-21 20:38:41 +00003033 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3034 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3035 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003036
cristy16ea1392012-03-21 20:38:41 +00003037 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3038 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3039 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003040
cristy16ea1392012-03-21 20:38:41 +00003041 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3042 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3043 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003044
cristy16ea1392012-03-21 20:38:41 +00003045 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3046 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3047 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00003048
cristy16ea1392012-03-21 20:38:41 +00003049 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3050 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3051 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00003052
glennrpc8cbc5d2011-01-01 00:12:34 +00003053 if (found_transparent_pixel == MagickFalse)
3054 {
3055 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00003056 if (y== 0 && logging != MagickFalse)
3057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3058 " Looking for cheap transparent pixel");
3059
glennrpc8cbc5d2011-01-01 00:12:34 +00003060 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3061 {
glennrp5aa37f62011-01-02 03:07:57 +00003062 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3063 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy16ea1392012-03-21 20:38:41 +00003064 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00003065 {
glennrpa6a06632011-01-19 15:15:34 +00003066 if (logging != MagickFalse)
3067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3068 " ...got one.");
3069
glennrpc8cbc5d2011-01-01 00:12:34 +00003070 found_transparent_pixel = MagickTrue;
3071 break;
3072 }
glennrp4f25bd02011-01-01 18:51:28 +00003073 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3074 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
cristy16ea1392012-03-21 20:38:41 +00003075 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3076 transparent_color.red &&
3077 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3078 transparent_color.green &&
3079 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3080 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00003081 {
glennrpa6a06632011-01-19 15:15:34 +00003082 if (logging != MagickFalse)
3083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3084 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00003085 found_transparent_pixel = MagickTrue;
3086 break;
3087 }
cristy16ea1392012-03-21 20:38:41 +00003088 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00003089 }
3090 }
3091
3092 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3093 {
3094 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3095 image->rows);
3096
3097 if (status == MagickFalse)
3098 break;
3099 }
3100 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3101 break;
3102 }
3103
3104 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3105 {
3106 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00003107 if (status == MagickFalse)
3108 break;
3109 }
cristy3ed852e2009-09-05 21:47:34 +00003110 }
cristy3ed852e2009-09-05 21:47:34 +00003111 }
glennrp0fe50b42010-11-16 03:52:51 +00003112
cristy3ed852e2009-09-05 21:47:34 +00003113 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00003114
cristy3ed852e2009-09-05 21:47:34 +00003115 for (pass=0; pass < num_passes; pass++)
3116 {
3117 Quantum
3118 *quantum_scanline;
3119
3120 register Quantum
3121 *r;
3122
3123 /*
3124 Convert grayscale image to PseudoClass pixel packets.
3125 */
glennrpc17d96f2011-06-27 01:20:11 +00003126 if (logging != MagickFalse)
3127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3128 " Converting grayscale pixels to pixel packets");
cristy16ea1392012-03-21 20:38:41 +00003129
glennrpfaa852b2010-03-30 12:17:00 +00003130 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00003131 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003132
cristy3ed852e2009-09-05 21:47:34 +00003133 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3134 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003135
cristy3ed852e2009-09-05 21:47:34 +00003136 if (quantum_scanline == (Quantum *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003137 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003138
cristybb503372010-05-27 20:51:26 +00003139 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003140 {
3141 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003142 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003143
cristy3ed852e2009-09-05 21:47:34 +00003144 else
3145 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003146
glennrpcf002022011-01-30 02:38:15 +00003147 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003148 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003149
cristy16ea1392012-03-21 20:38:41 +00003150 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003151 break;
glennrp0fe50b42010-11-16 03:52:51 +00003152
glennrpcf002022011-01-30 02:38:15 +00003153 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003154 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003155
glennrpfaa852b2010-03-30 12:17:00 +00003156 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003157 {
3158 case 1:
3159 {
cristybb503372010-05-27 20:51:26 +00003160 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003161 bit;
3162
cristybb503372010-05-27 20:51:26 +00003163 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003164 {
3165 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003166 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003167 p++;
3168 }
glennrp0fe50b42010-11-16 03:52:51 +00003169
cristy3ed852e2009-09-05 21:47:34 +00003170 if ((image->columns % 8) != 0)
3171 {
cristybb503372010-05-27 20:51:26 +00003172 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003173 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003174 }
glennrp0fe50b42010-11-16 03:52:51 +00003175
cristy3ed852e2009-09-05 21:47:34 +00003176 break;
3177 }
glennrp47b9dd52010-11-24 18:12:06 +00003178
cristy3ed852e2009-09-05 21:47:34 +00003179 case 2:
3180 {
cristybb503372010-05-27 20:51:26 +00003181 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003182 {
glennrpa18d5bc2011-04-23 14:51:34 +00003183 *r++=(*p >> 6) & 0x03;
3184 *r++=(*p >> 4) & 0x03;
3185 *r++=(*p >> 2) & 0x03;
3186 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003187 }
glennrp0fe50b42010-11-16 03:52:51 +00003188
cristy3ed852e2009-09-05 21:47:34 +00003189 if ((image->columns % 4) != 0)
3190 {
cristybb503372010-05-27 20:51:26 +00003191 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003192 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003193 }
glennrp0fe50b42010-11-16 03:52:51 +00003194
cristy3ed852e2009-09-05 21:47:34 +00003195 break;
3196 }
glennrp47b9dd52010-11-24 18:12:06 +00003197
cristy3ed852e2009-09-05 21:47:34 +00003198 case 4:
3199 {
cristybb503372010-05-27 20:51:26 +00003200 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003201 {
glennrpa18d5bc2011-04-23 14:51:34 +00003202 *r++=(*p >> 4) & 0x0f;
3203 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003204 }
glennrp0fe50b42010-11-16 03:52:51 +00003205
cristy3ed852e2009-09-05 21:47:34 +00003206 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003207 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003208
cristy3ed852e2009-09-05 21:47:34 +00003209 break;
3210 }
glennrp47b9dd52010-11-24 18:12:06 +00003211
cristy3ed852e2009-09-05 21:47:34 +00003212 case 8:
3213 {
glennrpfaa852b2010-03-30 12:17:00 +00003214 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003215 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003216 {
glennrpa18d5bc2011-04-23 14:51:34 +00003217 *r++=*p++;
cristy16ea1392012-03-21 20:38:41 +00003218 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3219 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003220 found_transparent_pixel = MagickTrue;
cristy16ea1392012-03-21 20:38:41 +00003221 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003222 }
glennrp0fe50b42010-11-16 03:52:51 +00003223
cristy3ed852e2009-09-05 21:47:34 +00003224 else
cristybb503372010-05-27 20:51:26 +00003225 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003226 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003227
cristy3ed852e2009-09-05 21:47:34 +00003228 break;
3229 }
glennrp47b9dd52010-11-24 18:12:06 +00003230
cristy3ed852e2009-09-05 21:47:34 +00003231 case 16:
3232 {
cristybb503372010-05-27 20:51:26 +00003233 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003234 {
glennrpc17d96f2011-06-27 01:20:11 +00003235#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003236 size_t
3237 quantum;
3238
3239 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003240 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003241
3242 else
glennrpc17d96f2011-06-27 01:20:11 +00003243 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003244
glennrp58f77c72011-04-23 14:09:09 +00003245 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003246 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003247 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003248
3249 if (ping_color_type == 4)
3250 {
glennrpc17d96f2011-06-27 01:20:11 +00003251 if (image->colors > 256)
3252 quantum=((*p++) << 8);
3253 else
3254 quantum=0;
3255
glennrp58f77c72011-04-23 14:09:09 +00003256 quantum|=(*p++);
cristy16ea1392012-03-21 20:38:41 +00003257 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3258 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003259 found_transparent_pixel = MagickTrue;
cristy16ea1392012-03-21 20:38:41 +00003260 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003261 }
glennrp58f77c72011-04-23 14:09:09 +00003262
3263#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3264 *r++=(*p++);
3265 p++; /* strip low byte */
3266
3267 if (ping_color_type == 4)
3268 {
cristy16ea1392012-03-21 20:38:41 +00003269 SetPixelAlpha(image,*p++,q);
3270 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003271 found_transparent_pixel = MagickTrue;
3272 p++;
cristy16ea1392012-03-21 20:38:41 +00003273 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003274 }
cristy3ed852e2009-09-05 21:47:34 +00003275#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003276 }
glennrp47b9dd52010-11-24 18:12:06 +00003277
cristy3ed852e2009-09-05 21:47:34 +00003278 break;
3279 }
glennrp47b9dd52010-11-24 18:12:06 +00003280
cristy3ed852e2009-09-05 21:47:34 +00003281 default:
3282 break;
3283 }
glennrp3faa9a32011-04-23 14:00:25 +00003284
cristy3ed852e2009-09-05 21:47:34 +00003285 /*
3286 Transfer image scanline.
3287 */
3288 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003289
cristy16ea1392012-03-21 20:38:41 +00003290 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3291
3292 if (q == (Quantum *) NULL)
3293 break;
cristybb503372010-05-27 20:51:26 +00003294 for (x=0; x < (ssize_t) image->columns; x++)
cristy16ea1392012-03-21 20:38:41 +00003295 {
3296 SetPixelIndex(image,*r++,q);
3297 q+=GetPixelChannels(image);
3298 }
glennrp0fe50b42010-11-16 03:52:51 +00003299
cristy3ed852e2009-09-05 21:47:34 +00003300 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3301 break;
glennrp0fe50b42010-11-16 03:52:51 +00003302
cristy7a287bf2010-02-14 02:18:09 +00003303 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3304 {
cristycee97112010-05-28 00:44:52 +00003305 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003306 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003307
cristy7a287bf2010-02-14 02:18:09 +00003308 if (status == MagickFalse)
3309 break;
3310 }
cristy3ed852e2009-09-05 21:47:34 +00003311 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003312
cristy7a287bf2010-02-14 02:18:09 +00003313 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003314 {
3315 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003316
cristy3ed852e2009-09-05 21:47:34 +00003317 if (status == MagickFalse)
3318 break;
3319 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003320
cristy3ed852e2009-09-05 21:47:34 +00003321 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3322 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003323
3324 image->matte=found_transparent_pixel;
3325
3326 if (logging != MagickFalse)
3327 {
3328 if (found_transparent_pixel != MagickFalse)
3329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3330 " Found transparent pixel");
3331 else
glennrp5aa37f62011-01-02 03:07:57 +00003332 {
3333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3334 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003335
glennrp5aa37f62011-01-02 03:07:57 +00003336 ping_color_type&=0x03;
3337 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003338 }
3339 }
3340
cristy16ea1392012-03-21 20:38:41 +00003341 if (quantum_info != (QuantumInfo *) NULL)
3342 quantum_info=DestroyQuantumInfo(quantum_info);
3343
cristy5c6f7892010-05-05 22:53:29 +00003344 if (image->storage_class == PseudoClass)
3345 {
cristyaeb2cbc2010-05-07 13:28:58 +00003346 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003347 matte;
3348
3349 matte=image->matte;
3350 image->matte=MagickFalse;
cristy16ea1392012-03-21 20:38:41 +00003351 (void) SyncImage(image,exception);
cristyaeb2cbc2010-05-07 13:28:58 +00003352 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003353 }
glennrp47b9dd52010-11-24 18:12:06 +00003354
glennrp4eb39312011-03-30 21:34:55 +00003355 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003356
3357 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003358 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003359 {
3360 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003361 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003362 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00003363 (void) SetImageBackgroundColor(image,exception);
glennrpedaa0382012-04-12 14:16:21 +00003364#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00003365 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003366#endif
3367 if (logging != MagickFalse)
3368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3369 " exit ReadOnePNGImage() early.");
3370 return(image);
3371 }
glennrp47b9dd52010-11-24 18:12:06 +00003372
glennrpfaa852b2010-03-30 12:17:00 +00003373 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003374 {
3375 ClassType
3376 storage_class;
3377
3378 /*
3379 Image has a transparent background.
3380 */
3381 storage_class=image->storage_class;
3382 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003383
glennrp3c218112010-11-27 15:31:26 +00003384/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003385
glennrp0fe50b42010-11-16 03:52:51 +00003386 if (storage_class == PseudoClass)
3387 {
3388 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003389 {
glennrp0fe50b42010-11-16 03:52:51 +00003390 for (x=0; x < ping_num_trans; x++)
3391 {
cristy16ea1392012-03-21 20:38:41 +00003392 image->colormap[x].matte=MagickTrue;
3393 image->colormap[x].alpha =
3394 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003395 }
glennrpc11cf6a2010-03-20 16:46:19 +00003396 }
glennrp47b9dd52010-11-24 18:12:06 +00003397
glennrp0fe50b42010-11-16 03:52:51 +00003398 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3399 {
3400 for (x=0; x < (int) image->colors; x++)
3401 {
3402 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy16ea1392012-03-21 20:38:41 +00003403 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003404 {
cristy16ea1392012-03-21 20:38:41 +00003405 image->colormap[x].matte=MagickTrue;
3406 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003407 }
3408 }
3409 }
cristy16ea1392012-03-21 20:38:41 +00003410 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003411 }
glennrp47b9dd52010-11-24 18:12:06 +00003412
glennrpa6a06632011-01-19 15:15:34 +00003413#if 1 /* Should have already been done above, but glennrp problem P10
3414 * needs this.
3415 */
glennrp0fe50b42010-11-16 03:52:51 +00003416 else
3417 {
3418 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003419 {
glennrp0fe50b42010-11-16 03:52:51 +00003420 image->storage_class=storage_class;
3421 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3422
cristy16ea1392012-03-21 20:38:41 +00003423 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003424 break;
3425
glennrp0fe50b42010-11-16 03:52:51 +00003426
glennrpa6a06632011-01-19 15:15:34 +00003427 /* Caution: on a Q8 build, this does not distinguish between
3428 * 16-bit colors that differ only in the low byte
3429 */
glennrp0fe50b42010-11-16 03:52:51 +00003430 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3431 {
cristy16ea1392012-03-21 20:38:41 +00003432 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3433 transparent_color.red &&
3434 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3435 transparent_color.green &&
3436 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3437 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003438 {
cristy16ea1392012-03-21 20:38:41 +00003439 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003440 }
glennrp0fe50b42010-11-16 03:52:51 +00003441
glennrp67b9c1a2011-04-22 18:47:36 +00003442#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003443 else
glennrp4f25bd02011-01-01 18:51:28 +00003444 {
cristy16ea1392012-03-21 20:38:41 +00003445 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003446 }
glennrpa6a06632011-01-19 15:15:34 +00003447#endif
glennrp0fe50b42010-11-16 03:52:51 +00003448
cristy16ea1392012-03-21 20:38:41 +00003449 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003450 }
3451
3452 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3453 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003454 }
glennrp0fe50b42010-11-16 03:52:51 +00003455 }
glennrpa6a06632011-01-19 15:15:34 +00003456#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003457
cristy3ed852e2009-09-05 21:47:34 +00003458 image->storage_class=DirectClass;
3459 }
glennrp3c218112010-11-27 15:31:26 +00003460
cristyeb3b22a2011-03-31 20:16:11 +00003461 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003462 {
3463 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003464 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3465 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003466 else
glennrpa0ed0092011-04-18 16:36:29 +00003467 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3468 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003469
glennrp4eb39312011-03-30 21:34:55 +00003470 if (status != MagickFalse)
3471 for (i=0; i < (ssize_t) num_text; i++)
3472 {
3473 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003474
glennrp4eb39312011-03-30 21:34:55 +00003475 if (logging != MagickFalse)
3476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3477 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003478
glennrp4eb39312011-03-30 21:34:55 +00003479 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003480 {
glennrpedaa0382012-04-12 14:16:21 +00003481 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3482 (int) i,exception);
glennrp4eb39312011-03-30 21:34:55 +00003483 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003484 }
glennrp0fe50b42010-11-16 03:52:51 +00003485
glennrp4eb39312011-03-30 21:34:55 +00003486 else
3487 {
3488 char
3489 *value;
3490
3491 length=text[i].text_length;
3492 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3493 sizeof(*value));
3494 if (value == (char *) NULL)
3495 {
glennrpedaa0382012-04-12 14:16:21 +00003496 png_error(ping,"Memory allocation failed");
glennrp4eb39312011-03-30 21:34:55 +00003497 break;
3498 }
3499 *value='\0';
3500 (void) ConcatenateMagickString(value,text[i].text,length+2);
3501
3502 /* Don't save "density" or "units" property if we have a pHYs
3503 * chunk
3504 */
3505 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3506 (LocaleCompare(text[i].key,"density") != 0 &&
3507 LocaleCompare(text[i].key,"units") != 0))
cristy16ea1392012-03-21 20:38:41 +00003508 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003509
3510 if (logging != MagickFalse)
3511 {
3512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3513 " length: %lu",(unsigned long) length);
3514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3515 " Keyword: %s",text[i].key);
3516 }
3517
3518 value=DestroyString(value);
3519 }
3520 }
3521 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003522 }
glennrp3c218112010-11-27 15:31:26 +00003523
cristy3ed852e2009-09-05 21:47:34 +00003524#ifdef MNG_OBJECT_BUFFERS
3525 /*
3526 Store the object if necessary.
3527 */
3528 if (object_id && !mng_info->frozen[object_id])
3529 {
3530 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3531 {
3532 /*
3533 create a new object buffer.
3534 */
3535 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003536 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003537
cristy3ed852e2009-09-05 21:47:34 +00003538 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3539 {
3540 mng_info->ob[object_id]->image=(Image *) NULL;
3541 mng_info->ob[object_id]->reference_count=1;
3542 }
3543 }
glennrp47b9dd52010-11-24 18:12:06 +00003544
cristy3ed852e2009-09-05 21:47:34 +00003545 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3546 mng_info->ob[object_id]->frozen)
3547 {
3548 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003549 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003550
cristy3ed852e2009-09-05 21:47:34 +00003551 if (mng_info->ob[object_id]->frozen)
glennrpedaa0382012-04-12 14:16:21 +00003552 png_error(ping,"Cannot overwrite frozen MNG object buffer");
cristy3ed852e2009-09-05 21:47:34 +00003553 }
glennrp0fe50b42010-11-16 03:52:51 +00003554
cristy3ed852e2009-09-05 21:47:34 +00003555 else
3556 {
cristy3ed852e2009-09-05 21:47:34 +00003557
3558 if (mng_info->ob[object_id]->image != (Image *) NULL)
3559 mng_info->ob[object_id]->image=DestroyImage
3560 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003561
cristy3ed852e2009-09-05 21:47:34 +00003562 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristy16ea1392012-03-21 20:38:41 +00003563 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003564
cristy3ed852e2009-09-05 21:47:34 +00003565 if (mng_info->ob[object_id]->image != (Image *) NULL)
3566 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003567
cristy3ed852e2009-09-05 21:47:34 +00003568 else
glennrpedaa0382012-04-12 14:16:21 +00003569 png_error(ping, "Cloning image for object buffer failed");
glennrp0fe50b42010-11-16 03:52:51 +00003570
glennrpfaa852b2010-03-30 12:17:00 +00003571 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003572 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003573
glennrpfaa852b2010-03-30 12:17:00 +00003574 mng_info->ob[object_id]->width=ping_width;
3575 mng_info->ob[object_id]->height=ping_height;
3576 mng_info->ob[object_id]->color_type=ping_color_type;
3577 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3578 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3579 mng_info->ob[object_id]->compression_method=
3580 ping_compression_method;
3581 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003582
glennrpfaa852b2010-03-30 12:17:00 +00003583 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003584 {
cristy3ed852e2009-09-05 21:47:34 +00003585 png_colorp
3586 plte;
3587
3588 /*
3589 Copy the PLTE to the object buffer.
3590 */
3591 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3592 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003593
cristy3ed852e2009-09-05 21:47:34 +00003594 for (i=0; i < number_colors; i++)
3595 {
3596 mng_info->ob[object_id]->plte[i]=plte[i];
3597 }
3598 }
glennrp47b9dd52010-11-24 18:12:06 +00003599
cristy3ed852e2009-09-05 21:47:34 +00003600 else
3601 mng_info->ob[object_id]->plte_length=0;
3602 }
3603 }
3604#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003605
3606 /* Set image->matte to MagickTrue if the input colortype supports
3607 * alpha or if a valid tRNS chunk is present, no matter whether there
3608 * is actual transparency present.
3609 */
3610 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3611 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3612 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3613 MagickTrue : MagickFalse;
3614
glennrpcb395ac2011-03-30 19:50:23 +00003615 /* Set more properties for identify to retrieve */
3616 {
3617 char
3618 msg[MaxTextExtent];
3619
glennrp4eb39312011-03-30 21:34:55 +00003620 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003621 {
3622 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003623 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003624 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy16ea1392012-03-21 20:38:41 +00003625 (void) SetImageProperty(image,"png:text ",msg,
3626 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003627 }
3628
3629 if (num_raw_profiles != 0)
3630 {
cristy3b6fd2e2011-05-20 12:53:50 +00003631 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003632 "%d were found", num_raw_profiles);
cristy16ea1392012-03-21 20:38:41 +00003633 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3634 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003635 }
3636
glennrp98b83d42012-07-23 02:50:31 +00003637 if (ping_found_cHRM != MagickFalse)
glennrp59612252011-03-30 21:45:21 +00003638 {
cristy3b6fd2e2011-05-20 12:53:50 +00003639 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003640 "chunk was found (see Chromaticity, above)");
cristy16ea1392012-03-21 20:38:41 +00003641 (void) SetImageProperty(image,"png:cHRM ",msg,
3642 exception);
glennrp59612252011-03-30 21:45:21 +00003643 }
glennrpcb395ac2011-03-30 19:50:23 +00003644
3645 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003646 {
cristy3b6fd2e2011-05-20 12:53:50 +00003647 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003648 "chunk was found (see Background color, above)");
cristy16ea1392012-03-21 20:38:41 +00003649 (void) SetImageProperty(image,"png:bKGD ",msg,
3650 exception);
glennrp59612252011-03-30 21:45:21 +00003651 }
3652
cristy3b6fd2e2011-05-20 12:53:50 +00003653 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003654 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003655
glennrp98b83d42012-07-23 02:50:31 +00003656#if defined(PNG_iCCP_SUPPORTED)
3657 if (ping_found_iCCP != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00003658 (void) SetImageProperty(image,"png:iCCP ",msg,
3659 exception);
glennrp98b83d42012-07-23 02:50:31 +00003660#endif
glennrpcb395ac2011-03-30 19:50:23 +00003661
glennrpcb395ac2011-03-30 19:50:23 +00003662 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy16ea1392012-03-21 20:38:41 +00003663 (void) SetImageProperty(image,"png:tRNS ",msg,
3664 exception);
glennrp4eb39312011-03-30 21:34:55 +00003665
3666#if defined(PNG_sRGB_SUPPORTED)
glennrp98b83d42012-07-23 02:50:31 +00003667 if (ping_found_sRGB != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003668 {
cristy3b6fd2e2011-05-20 12:53:50 +00003669 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp98b83d42012-07-23 02:50:31 +00003670 "intent=%d (%s)",
3671 (int) intent,
3672 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
cristy16ea1392012-03-21 20:38:41 +00003673 (void) SetImageProperty(image,"png:sRGB ",msg,
glennrp98b83d42012-07-23 02:50:31 +00003674 exception);
glennrp4eb39312011-03-30 21:34:55 +00003675 }
3676#endif
3677
glennrp98b83d42012-07-23 02:50:31 +00003678 if (ping_found_gAMA != MagickFalse)
glennrp4eb39312011-03-30 21:34:55 +00003679 {
cristy3b6fd2e2011-05-20 12:53:50 +00003680 (void) FormatLocaleString(msg,MaxTextExtent,
cristy16ea1392012-03-21 20:38:41 +00003681 "gamma=%.8g (See Gamma, above)",
3682 file_gamma);
3683 (void) SetImageProperty(image,"png:gAMA ",msg,
3684 exception);
glennrp4eb39312011-03-30 21:34:55 +00003685 }
3686
3687#if defined(PNG_pHYs_SUPPORTED)
3688 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3689 {
cristy3b6fd2e2011-05-20 12:53:50 +00003690 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003691 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003692 (double) x_resolution,(double) y_resolution, unit_type);
cristy16ea1392012-03-21 20:38:41 +00003693 (void) SetImageProperty(image,"png:pHYs ",msg,
3694 exception);
glennrp4eb39312011-03-30 21:34:55 +00003695 }
3696#endif
3697
3698#if defined(PNG_oFFs_SUPPORTED)
3699 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3700 {
cristy3b6fd2e2011-05-20 12:53:50 +00003701 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003702 (double) image->page.x,(double) image->page.y);
cristy16ea1392012-03-21 20:38:41 +00003703 (void) SetImageProperty(image,"png:oFFs ",msg,
3704 exception);
glennrp4eb39312011-03-30 21:34:55 +00003705 }
3706#endif
3707
glennrp07523c72011-03-31 18:12:10 +00003708 if ((image->page.width != 0 && image->page.width != image->columns) ||
3709 (image->page.height != 0 && image->page.height != image->rows))
3710 {
cristy3b6fd2e2011-05-20 12:53:50 +00003711 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003712 "width=%.20g, height=%.20g",
3713 (double) image->page.width,(double) image->page.height);
cristy16ea1392012-03-21 20:38:41 +00003714 (void) SetImageProperty(image,"png:vpAg ",msg,
3715 exception);
glennrp07523c72011-03-31 18:12:10 +00003716 }
glennrpcb395ac2011-03-30 19:50:23 +00003717 }
3718
cristy3ed852e2009-09-05 21:47:34 +00003719 /*
3720 Relinquish resources.
3721 */
3722 png_destroy_read_struct(&ping,&ping_info,&end_info);
3723
glennrpcf002022011-01-30 02:38:15 +00003724 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003725
3726 if (logging != MagickFalse)
3727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3728 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003729
glennrpedaa0382012-04-12 14:16:21 +00003730#ifdef PNG_SETJMP_NOT_THREAD_SAFE
3731 UnlockSemaphoreInfo(ping_semaphore);
3732#endif
3733
3734 /* } for navigation to beginning of SETJMP-protected block, revert to
3735 * Throwing an Exception when an error occurs.
3736 */
3737
cristy3ed852e2009-09-05 21:47:34 +00003738 return(image);
3739
3740/* end of reading one PNG image */
3741}
3742
3743static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3744{
3745 Image
3746 *image,
3747 *previous;
3748
3749 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003750 have_mng_structure,
3751 logging,
cristy3ed852e2009-09-05 21:47:34 +00003752 status;
3753
3754 MngInfo
3755 *mng_info;
3756
3757 char
3758 magic_number[MaxTextExtent];
3759
cristy3ed852e2009-09-05 21:47:34 +00003760 ssize_t
3761 count;
3762
3763 /*
3764 Open image file.
3765 */
3766 assert(image_info != (const ImageInfo *) NULL);
3767 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003768
cristy3ed852e2009-09-05 21:47:34 +00003769 if (image_info->debug != MagickFalse)
3770 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3771 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003772
cristy3ed852e2009-09-05 21:47:34 +00003773 assert(exception != (ExceptionInfo *) NULL);
3774 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003775 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy16ea1392012-03-21 20:38:41 +00003776 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003777 mng_info=(MngInfo *) NULL;
3778 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003779
cristy3ed852e2009-09-05 21:47:34 +00003780 if (status == MagickFalse)
3781 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003782
cristy3ed852e2009-09-05 21:47:34 +00003783 /*
3784 Verify PNG signature.
3785 */
3786 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003787
glennrpdde35db2011-02-21 12:06:32 +00003788 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003789 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003790
cristy3ed852e2009-09-05 21:47:34 +00003791 /*
3792 Allocate a MngInfo structure.
3793 */
3794 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003795 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003796
cristy3ed852e2009-09-05 21:47:34 +00003797 if (mng_info == (MngInfo *) NULL)
3798 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003799
cristy3ed852e2009-09-05 21:47:34 +00003800 /*
3801 Initialize members of the MngInfo structure.
3802 */
3803 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3804 mng_info->image=image;
3805 have_mng_structure=MagickTrue;
3806
3807 previous=image;
3808 image=ReadOnePNGImage(mng_info,image_info,exception);
3809 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003810
cristy3ed852e2009-09-05 21:47:34 +00003811 if (image == (Image *) NULL)
3812 {
3813 if (previous != (Image *) NULL)
3814 {
3815 if (previous->signature != MagickSignature)
3816 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003817
cristy3ed852e2009-09-05 21:47:34 +00003818 (void) CloseBlob(previous);
3819 (void) DestroyImageList(previous);
3820 }
glennrp0fe50b42010-11-16 03:52:51 +00003821
cristy3ed852e2009-09-05 21:47:34 +00003822 if (logging != MagickFalse)
3823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3824 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003825
cristy3ed852e2009-09-05 21:47:34 +00003826 return((Image *) NULL);
3827 }
glennrp47b9dd52010-11-24 18:12:06 +00003828
cristy3ed852e2009-09-05 21:47:34 +00003829 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003830
cristy3ed852e2009-09-05 21:47:34 +00003831 if ((image->columns == 0) || (image->rows == 0))
3832 {
3833 if (logging != MagickFalse)
3834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3835 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003836
cristy3ed852e2009-09-05 21:47:34 +00003837 ThrowReaderException(CorruptImageError,"CorruptImage");
3838 }
glennrp47b9dd52010-11-24 18:12:06 +00003839
cristy72715f52012-06-26 17:55:16 +00003840 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
3841 (image->gamma == 1.0))
3842 SetImageColorspace(image,RGBColorspace,exception);
3843
cristy3ed852e2009-09-05 21:47:34 +00003844 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3845 {
cristy16ea1392012-03-21 20:38:41 +00003846 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003847 image->matte=MagickFalse;
3848 }
glennrp0fe50b42010-11-16 03:52:51 +00003849
cristy3ed852e2009-09-05 21:47:34 +00003850 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy16ea1392012-03-21 20:38:41 +00003851 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003852
cristy3ed852e2009-09-05 21:47:34 +00003853 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3855 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3856 (double) image->page.width,(double) image->page.height,
3857 (double) image->page.x,(double) image->page.y);
3858
3859 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003861
cristy3ed852e2009-09-05 21:47:34 +00003862 return(image);
3863}
3864
3865
3866
3867#if defined(JNG_SUPPORTED)
3868/*
3869%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3870% %
3871% %
3872% %
3873% R e a d O n e J N G I m a g e %
3874% %
3875% %
3876% %
3877%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3878%
3879% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3880% (minus the 8-byte signature) and returns it. It allocates the memory
3881% necessary for the new Image structure and returns a pointer to the new
3882% image.
3883%
3884% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3885%
3886% The format of the ReadOneJNGImage method is:
3887%
3888% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3889% ExceptionInfo *exception)
3890%
3891% A description of each parameter follows:
3892%
3893% o mng_info: Specifies a pointer to a MngInfo structure.
3894%
3895% o image_info: the image info.
3896%
3897% o exception: return any errors or warnings in this structure.
3898%
3899*/
3900static Image *ReadOneJNGImage(MngInfo *mng_info,
3901 const ImageInfo *image_info, ExceptionInfo *exception)
3902{
3903 Image
3904 *alpha_image,
3905 *color_image,
3906 *image,
3907 *jng_image;
3908
3909 ImageInfo
3910 *alpha_image_info,
3911 *color_image_info;
3912
cristy4383ec82011-01-05 15:42:32 +00003913 MagickBooleanType
3914 logging;
3915
cristybb503372010-05-27 20:51:26 +00003916 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003917 y;
3918
3919 MagickBooleanType
3920 status;
3921
3922 png_uint_32
3923 jng_height,
3924 jng_width;
3925
3926 png_byte
3927 jng_color_type,
3928 jng_image_sample_depth,
3929 jng_image_compression_method,
3930 jng_image_interlace_method,
3931 jng_alpha_sample_depth,
3932 jng_alpha_compression_method,
3933 jng_alpha_filter_method,
3934 jng_alpha_interlace_method;
3935
cristy16ea1392012-03-21 20:38:41 +00003936 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003937 *s;
3938
cristybb503372010-05-27 20:51:26 +00003939 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003940 i,
3941 x;
3942
cristy16ea1392012-03-21 20:38:41 +00003943 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003944 *q;
3945
3946 register unsigned char
3947 *p;
3948
3949 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003950 read_JSEP,
3951 reading_idat,
3952 skip_to_iend;
3953
cristybb503372010-05-27 20:51:26 +00003954 size_t
cristy3ed852e2009-09-05 21:47:34 +00003955 length;
3956
3957 jng_alpha_compression_method=0;
3958 jng_alpha_sample_depth=8;
3959 jng_color_type=0;
3960 jng_height=0;
3961 jng_width=0;
3962 alpha_image=(Image *) NULL;
3963 color_image=(Image *) NULL;
3964 alpha_image_info=(ImageInfo *) NULL;
3965 color_image_info=(ImageInfo *) NULL;
3966
3967 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003968 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003969
3970 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003971
cristy16ea1392012-03-21 20:38:41 +00003972 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003973 {
3974 /*
3975 Allocate next image structure.
3976 */
3977 if (logging != MagickFalse)
3978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3979 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003980
cristy16ea1392012-03-21 20:38:41 +00003981 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003982
cristy3ed852e2009-09-05 21:47:34 +00003983 if (GetNextImageInList(image) == (Image *) NULL)
3984 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003985
cristy3ed852e2009-09-05 21:47:34 +00003986 image=SyncNextImageInList(image);
3987 }
3988 mng_info->image=image;
3989
3990 /*
3991 Signature bytes have already been read.
3992 */
3993
3994 read_JSEP=MagickFalse;
3995 reading_idat=MagickFalse;
3996 skip_to_iend=MagickFalse;
3997 for (;;)
3998 {
3999 char
4000 type[MaxTextExtent];
4001
4002 unsigned char
4003 *chunk;
4004
4005 unsigned int
4006 count;
4007
4008 /*
4009 Read a new JNG chunk.
4010 */
4011 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4012 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004013
cristy3ed852e2009-09-05 21:47:34 +00004014 if (status == MagickFalse)
4015 break;
glennrp0fe50b42010-11-16 03:52:51 +00004016
cristy3ed852e2009-09-05 21:47:34 +00004017 type[0]='\0';
4018 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4019 length=ReadBlobMSBLong(image);
4020 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4021
4022 if (logging != MagickFalse)
4023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004024 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4025 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004026
4027 if (length > PNG_UINT_31_MAX || count == 0)
4028 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004029
cristy3ed852e2009-09-05 21:47:34 +00004030 p=NULL;
4031 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00004032
cristy3ed852e2009-09-05 21:47:34 +00004033 if (length)
4034 {
4035 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00004036
cristy3ed852e2009-09-05 21:47:34 +00004037 if (chunk == (unsigned char *) NULL)
4038 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004039
cristybb503372010-05-27 20:51:26 +00004040 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004041 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00004042
cristy3ed852e2009-09-05 21:47:34 +00004043 p=chunk;
4044 }
glennrp47b9dd52010-11-24 18:12:06 +00004045
cristy3ed852e2009-09-05 21:47:34 +00004046 (void) ReadBlobMSBLong(image); /* read crc word */
4047
4048 if (skip_to_iend)
4049 {
4050 if (length)
4051 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004052
cristy3ed852e2009-09-05 21:47:34 +00004053 continue;
4054 }
4055
4056 if (memcmp(type,mng_JHDR,4) == 0)
4057 {
4058 if (length == 16)
4059 {
cristybb503372010-05-27 20:51:26 +00004060 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004061 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00004062 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004063 (p[6] << 8) | p[7]);
4064 jng_color_type=p[8];
4065 jng_image_sample_depth=p[9];
4066 jng_image_compression_method=p[10];
4067 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00004068
cristy3ed852e2009-09-05 21:47:34 +00004069 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4070 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00004071
cristy3ed852e2009-09-05 21:47:34 +00004072 jng_alpha_sample_depth=p[12];
4073 jng_alpha_compression_method=p[13];
4074 jng_alpha_filter_method=p[14];
4075 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00004076
cristy3ed852e2009-09-05 21:47:34 +00004077 if (logging != MagickFalse)
4078 {
4079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004080 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00004081
cristy3ed852e2009-09-05 21:47:34 +00004082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00004083 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00004084
cristy3ed852e2009-09-05 21:47:34 +00004085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4086 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00004087
cristy3ed852e2009-09-05 21:47:34 +00004088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4089 " jng_image_sample_depth: %3d",
4090 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004091
cristy3ed852e2009-09-05 21:47:34 +00004092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4093 " jng_image_compression_method:%3d",
4094 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004095
cristy3ed852e2009-09-05 21:47:34 +00004096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4097 " jng_image_interlace_method: %3d",
4098 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00004099
cristy3ed852e2009-09-05 21:47:34 +00004100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4101 " jng_alpha_sample_depth: %3d",
4102 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00004103
cristy3ed852e2009-09-05 21:47:34 +00004104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4105 " jng_alpha_compression_method:%3d",
4106 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00004107
cristy3ed852e2009-09-05 21:47:34 +00004108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4109 " jng_alpha_filter_method: %3d",
4110 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00004111
cristy3ed852e2009-09-05 21:47:34 +00004112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4113 " jng_alpha_interlace_method: %3d",
4114 jng_alpha_interlace_method);
4115 }
4116 }
glennrp47b9dd52010-11-24 18:12:06 +00004117
cristy3ed852e2009-09-05 21:47:34 +00004118 if (length)
4119 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004120
cristy3ed852e2009-09-05 21:47:34 +00004121 continue;
4122 }
4123
4124
4125 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4126 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4127 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4128 {
4129 /*
4130 o create color_image
4131 o open color_blob, attached to color_image
4132 o if (color type has alpha)
4133 open alpha_blob, attached to alpha_image
4134 */
4135
cristy73bd4a52010-10-05 11:24:23 +00004136 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004137
cristy3ed852e2009-09-05 21:47:34 +00004138 if (color_image_info == (ImageInfo *) NULL)
4139 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004140
cristy3ed852e2009-09-05 21:47:34 +00004141 GetImageInfo(color_image_info);
cristy16ea1392012-03-21 20:38:41 +00004142 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004143
cristy3ed852e2009-09-05 21:47:34 +00004144 if (color_image == (Image *) NULL)
4145 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4146
4147 if (logging != MagickFalse)
4148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4149 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004150
cristy3ed852e2009-09-05 21:47:34 +00004151 (void) AcquireUniqueFilename(color_image->filename);
4152 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4153 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004154
cristy3ed852e2009-09-05 21:47:34 +00004155 if (status == MagickFalse)
4156 return((Image *) NULL);
4157
4158 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4159 {
4160 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004161 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004162
cristy3ed852e2009-09-05 21:47:34 +00004163 if (alpha_image_info == (ImageInfo *) NULL)
4164 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004165
cristy3ed852e2009-09-05 21:47:34 +00004166 GetImageInfo(alpha_image_info);
cristy16ea1392012-03-21 20:38:41 +00004167 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004168
cristy3ed852e2009-09-05 21:47:34 +00004169 if (alpha_image == (Image *) NULL)
4170 {
4171 alpha_image=DestroyImage(alpha_image);
4172 ThrowReaderException(ResourceLimitError,
4173 "MemoryAllocationFailed");
4174 }
glennrp0fe50b42010-11-16 03:52:51 +00004175
cristy3ed852e2009-09-05 21:47:34 +00004176 if (logging != MagickFalse)
4177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4178 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004179
cristy3ed852e2009-09-05 21:47:34 +00004180 (void) AcquireUniqueFilename(alpha_image->filename);
4181 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4182 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004183
cristy3ed852e2009-09-05 21:47:34 +00004184 if (status == MagickFalse)
4185 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004186
cristy3ed852e2009-09-05 21:47:34 +00004187 if (jng_alpha_compression_method == 0)
4188 {
4189 unsigned char
4190 data[18];
4191
4192 if (logging != MagickFalse)
4193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4194 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004195
cristy3ed852e2009-09-05 21:47:34 +00004196 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4197 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004198
cristy3ed852e2009-09-05 21:47:34 +00004199 (void) WriteBlobMSBULong(alpha_image,13L);
4200 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004201 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004202 PNGLong(data+4,jng_width);
4203 PNGLong(data+8,jng_height);
4204 data[12]=jng_alpha_sample_depth;
4205 data[13]=0; /* color_type gray */
4206 data[14]=0; /* compression method 0 */
4207 data[15]=0; /* filter_method 0 */
4208 data[16]=0; /* interlace_method 0 */
4209 (void) WriteBlob(alpha_image,17,data);
4210 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4211 }
4212 }
4213 reading_idat=MagickTrue;
4214 }
4215
4216 if (memcmp(type,mng_JDAT,4) == 0)
4217 {
glennrp47b9dd52010-11-24 18:12:06 +00004218 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004219
4220 if (logging != MagickFalse)
4221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4222 " Copying JDAT chunk data to color_blob.");
4223
4224 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004225
cristy3ed852e2009-09-05 21:47:34 +00004226 if (length)
4227 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004228
cristy3ed852e2009-09-05 21:47:34 +00004229 continue;
4230 }
4231
4232 if (memcmp(type,mng_IDAT,4) == 0)
4233 {
4234 png_byte
4235 data[5];
4236
glennrp47b9dd52010-11-24 18:12:06 +00004237 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004238
4239 if (image_info->ping == MagickFalse)
4240 {
4241 if (logging != MagickFalse)
4242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4243 " Copying IDAT chunk data to alpha_blob.");
4244
cristybb503372010-05-27 20:51:26 +00004245 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004246 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004247 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004248 (void) WriteBlob(alpha_image,4,data);
4249 (void) WriteBlob(alpha_image,length,chunk);
4250 (void) WriteBlobMSBULong(alpha_image,
4251 crc32(crc32(0,data,4),chunk,(uInt) length));
4252 }
glennrp0fe50b42010-11-16 03:52:51 +00004253
cristy3ed852e2009-09-05 21:47:34 +00004254 if (length)
4255 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004256
cristy3ed852e2009-09-05 21:47:34 +00004257 continue;
4258 }
4259
4260 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4261 {
glennrp47b9dd52010-11-24 18:12:06 +00004262 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004263
4264 if (image_info->ping == MagickFalse)
4265 {
4266 if (logging != MagickFalse)
4267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4268 " Copying JDAA chunk data to alpha_blob.");
4269
4270 (void) WriteBlob(alpha_image,length,chunk);
4271 }
glennrp0fe50b42010-11-16 03:52:51 +00004272
cristy3ed852e2009-09-05 21:47:34 +00004273 if (length)
4274 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004275
cristy3ed852e2009-09-05 21:47:34 +00004276 continue;
4277 }
4278
4279 if (memcmp(type,mng_JSEP,4) == 0)
4280 {
4281 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004282
cristy3ed852e2009-09-05 21:47:34 +00004283 if (length)
4284 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004285
cristy3ed852e2009-09-05 21:47:34 +00004286 continue;
4287 }
4288
4289 if (memcmp(type,mng_bKGD,4) == 0)
4290 {
4291 if (length == 2)
4292 {
4293 image->background_color.red=ScaleCharToQuantum(p[1]);
4294 image->background_color.green=image->background_color.red;
4295 image->background_color.blue=image->background_color.red;
4296 }
glennrp0fe50b42010-11-16 03:52:51 +00004297
cristy3ed852e2009-09-05 21:47:34 +00004298 if (length == 6)
4299 {
4300 image->background_color.red=ScaleCharToQuantum(p[1]);
4301 image->background_color.green=ScaleCharToQuantum(p[3]);
4302 image->background_color.blue=ScaleCharToQuantum(p[5]);
4303 }
glennrp0fe50b42010-11-16 03:52:51 +00004304
cristy3ed852e2009-09-05 21:47:34 +00004305 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4306 continue;
4307 }
4308
4309 if (memcmp(type,mng_gAMA,4) == 0)
4310 {
4311 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004312 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004313
cristy3ed852e2009-09-05 21:47:34 +00004314 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4315 continue;
4316 }
4317
4318 if (memcmp(type,mng_cHRM,4) == 0)
4319 {
4320 if (length == 32)
4321 {
cristy8182b072010-05-30 20:10:53 +00004322 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4323 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4324 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4325 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4326 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4327 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4328 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4329 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004330 }
glennrp47b9dd52010-11-24 18:12:06 +00004331
cristy3ed852e2009-09-05 21:47:34 +00004332 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4333 continue;
4334 }
4335
4336 if (memcmp(type,mng_sRGB,4) == 0)
4337 {
4338 if (length == 1)
4339 {
glennrpe610a072010-08-05 17:08:46 +00004340 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004341 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristyda7803d2012-05-03 01:22:45 +00004342 image->gamma=1.000f/2.200f;
cristy3ed852e2009-09-05 21:47:34 +00004343 image->chromaticity.red_primary.x=0.6400f;
4344 image->chromaticity.red_primary.y=0.3300f;
4345 image->chromaticity.green_primary.x=0.3000f;
4346 image->chromaticity.green_primary.y=0.6000f;
4347 image->chromaticity.blue_primary.x=0.1500f;
4348 image->chromaticity.blue_primary.y=0.0600f;
4349 image->chromaticity.white_point.x=0.3127f;
4350 image->chromaticity.white_point.y=0.3290f;
4351 }
glennrp47b9dd52010-11-24 18:12:06 +00004352
cristy3ed852e2009-09-05 21:47:34 +00004353 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4354 continue;
4355 }
4356
4357 if (memcmp(type,mng_oFFs,4) == 0)
4358 {
4359 if (length > 8)
4360 {
glennrp5eae7602011-02-22 15:21:32 +00004361 image->page.x=(ssize_t) mng_get_long(p);
4362 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004363
cristy3ed852e2009-09-05 21:47:34 +00004364 if ((int) p[8] != 0)
4365 {
4366 image->page.x/=10000;
4367 image->page.y/=10000;
4368 }
4369 }
glennrp47b9dd52010-11-24 18:12:06 +00004370
cristy3ed852e2009-09-05 21:47:34 +00004371 if (length)
4372 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004373
cristy3ed852e2009-09-05 21:47:34 +00004374 continue;
4375 }
4376
4377 if (memcmp(type,mng_pHYs,4) == 0)
4378 {
4379 if (length > 8)
4380 {
cristy16ea1392012-03-21 20:38:41 +00004381 image->resolution.x=(double) mng_get_long(p);
4382 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004383 if ((int) p[8] == PNG_RESOLUTION_METER)
4384 {
4385 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00004386 image->resolution.x=image->resolution.x/100.0f;
4387 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004388 }
4389 }
glennrp0fe50b42010-11-16 03:52:51 +00004390
cristy3ed852e2009-09-05 21:47:34 +00004391 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4392 continue;
4393 }
4394
4395#if 0
4396 if (memcmp(type,mng_iCCP,4) == 0)
4397 {
glennrpfd05d622011-02-25 04:10:33 +00004398 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004399 if (length)
4400 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004401
cristy3ed852e2009-09-05 21:47:34 +00004402 continue;
4403 }
4404#endif
4405
4406 if (length)
4407 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4408
4409 if (memcmp(type,mng_IEND,4))
4410 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004411
cristy3ed852e2009-09-05 21:47:34 +00004412 break;
4413 }
4414
4415
4416 /* IEND found */
4417
4418 /*
4419 Finish up reading image data:
4420
4421 o read main image from color_blob.
4422
4423 o close color_blob.
4424
4425 o if (color_type has alpha)
4426 if alpha_encoding is PNG
4427 read secondary image from alpha_blob via ReadPNG
4428 if alpha_encoding is JPEG
4429 read secondary image from alpha_blob via ReadJPEG
4430
4431 o close alpha_blob.
4432
4433 o copy intensity of secondary image into
cristy16ea1392012-03-21 20:38:41 +00004434 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004435
4436 o destroy the secondary image.
4437 */
4438
4439 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004440
cristy3ed852e2009-09-05 21:47:34 +00004441 if (logging != MagickFalse)
4442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4443 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004444
cristy3b6fd2e2011-05-20 12:53:50 +00004445 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004446 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004447
cristy3ed852e2009-09-05 21:47:34 +00004448 color_image_info->ping=MagickFalse; /* To do: avoid this */
4449 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004450
cristy3ed852e2009-09-05 21:47:34 +00004451 if (jng_image == (Image *) NULL)
4452 return((Image *) NULL);
4453
4454 (void) RelinquishUniqueFileResource(color_image->filename);
4455 color_image=DestroyImage(color_image);
4456 color_image_info=DestroyImageInfo(color_image_info);
4457
4458 if (jng_image == (Image *) NULL)
4459 return((Image *) NULL);
4460
4461 if (logging != MagickFalse)
4462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4463 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004464
cristy3ed852e2009-09-05 21:47:34 +00004465 image->rows=jng_height;
4466 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004467
cristybb503372010-05-27 20:51:26 +00004468 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004469 {
cristy16ea1392012-03-21 20:38:41 +00004470 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004471 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00004472 for (x=(ssize_t) image->columns; x != 0; x--)
4473 {
4474 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4475 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4476 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4477 q+=GetPixelChannels(image);
4478 s+=GetPixelChannels(jng_image);
4479 }
glennrp47b9dd52010-11-24 18:12:06 +00004480
cristy3ed852e2009-09-05 21:47:34 +00004481 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4482 break;
4483 }
glennrp0fe50b42010-11-16 03:52:51 +00004484
cristy3ed852e2009-09-05 21:47:34 +00004485 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004486
cristy3ed852e2009-09-05 21:47:34 +00004487 if (image_info->ping == MagickFalse)
4488 {
4489 if (jng_color_type >= 12)
4490 {
4491 if (jng_alpha_compression_method == 0)
4492 {
4493 png_byte
4494 data[5];
4495 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4496 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004497 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004498 (void) WriteBlob(alpha_image,4,data);
4499 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4500 }
glennrp0fe50b42010-11-16 03:52:51 +00004501
cristy3ed852e2009-09-05 21:47:34 +00004502 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004503
cristy3ed852e2009-09-05 21:47:34 +00004504 if (logging != MagickFalse)
4505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00004506 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004507
cristy3b6fd2e2011-05-20 12:53:50 +00004508 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004509 "%s",alpha_image->filename);
4510
4511 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004512
cristy3ed852e2009-09-05 21:47:34 +00004513 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004514 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004515 {
4516 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy16ea1392012-03-21 20:38:41 +00004517 exception);
cristy3ed852e2009-09-05 21:47:34 +00004518 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004519
cristy3ed852e2009-09-05 21:47:34 +00004520 if (image->matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00004521 for (x=(ssize_t) image->columns; x != 0; x--)
4522 {
4523 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4524 q+=GetPixelChannels(image);
4525 s+=GetPixelChannels(jng_image);
4526 }
glennrp0fe50b42010-11-16 03:52:51 +00004527
cristy3ed852e2009-09-05 21:47:34 +00004528 else
cristy16ea1392012-03-21 20:38:41 +00004529 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004530 {
cristy16ea1392012-03-21 20:38:41 +00004531 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4532 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004533 image->matte=MagickTrue;
cristy16ea1392012-03-21 20:38:41 +00004534 q+=GetPixelChannels(image);
4535 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004536 }
glennrp0fe50b42010-11-16 03:52:51 +00004537
cristy3ed852e2009-09-05 21:47:34 +00004538 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4539 break;
4540 }
4541 (void) RelinquishUniqueFileResource(alpha_image->filename);
4542 alpha_image=DestroyImage(alpha_image);
4543 alpha_image_info=DestroyImageInfo(alpha_image_info);
4544 if (jng_image != (Image *) NULL)
4545 jng_image=DestroyImage(jng_image);
4546 }
4547 }
4548
glennrp47b9dd52010-11-24 18:12:06 +00004549 /* Read the JNG image. */
4550
cristy3ed852e2009-09-05 21:47:34 +00004551 if (mng_info->mng_type == 0)
4552 {
4553 mng_info->mng_width=jng_width;
4554 mng_info->mng_height=jng_height;
4555 }
glennrp0fe50b42010-11-16 03:52:51 +00004556
cristy3ed852e2009-09-05 21:47:34 +00004557 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004558 {
4559 image->page.width=jng_width;
4560 image->page.height=jng_height;
4561 }
4562
cristy3ed852e2009-09-05 21:47:34 +00004563 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004564 {
4565 image->page.x=mng_info->x_off[mng_info->object_id];
4566 image->page.y=mng_info->y_off[mng_info->object_id];
4567 }
4568
cristy3ed852e2009-09-05 21:47:34 +00004569 else
glennrp0fe50b42010-11-16 03:52:51 +00004570 {
4571 image->page.y=mng_info->y_off[mng_info->object_id];
4572 }
4573
cristy3ed852e2009-09-05 21:47:34 +00004574 mng_info->image_found++;
4575 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4576 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004577
cristy3ed852e2009-09-05 21:47:34 +00004578 if (logging != MagickFalse)
4579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4580 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004581
cristy3ed852e2009-09-05 21:47:34 +00004582 return(image);
4583}
4584
4585/*
4586%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4587% %
4588% %
4589% %
4590% R e a d J N G I m a g e %
4591% %
4592% %
4593% %
4594%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4595%
4596% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4597% (including the 8-byte signature) and returns it. It allocates the memory
4598% necessary for the new Image structure and returns a pointer to the new
4599% image.
4600%
4601% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4602%
4603% The format of the ReadJNGImage method is:
4604%
4605% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4606% *exception)
4607%
4608% A description of each parameter follows:
4609%
4610% o image_info: the image info.
4611%
4612% o exception: return any errors or warnings in this structure.
4613%
4614*/
4615
4616static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4617{
4618 Image
4619 *image,
4620 *previous;
4621
4622 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004623 have_mng_structure,
4624 logging,
cristy3ed852e2009-09-05 21:47:34 +00004625 status;
4626
4627 MngInfo
4628 *mng_info;
4629
4630 char
4631 magic_number[MaxTextExtent];
4632
cristy3ed852e2009-09-05 21:47:34 +00004633 size_t
4634 count;
4635
4636 /*
4637 Open image file.
4638 */
4639 assert(image_info != (const ImageInfo *) NULL);
4640 assert(image_info->signature == MagickSignature);
4641 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4642 assert(exception != (ExceptionInfo *) NULL);
4643 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004644 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004645 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004646 mng_info=(MngInfo *) NULL;
4647 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004648
cristy3ed852e2009-09-05 21:47:34 +00004649 if (status == MagickFalse)
4650 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004651
cristy3ed852e2009-09-05 21:47:34 +00004652 if (LocaleCompare(image_info->magick,"JNG") != 0)
4653 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004654
glennrp47b9dd52010-11-24 18:12:06 +00004655 /* Verify JNG signature. */
4656
cristy3ed852e2009-09-05 21:47:34 +00004657 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004658
glennrp3b8763e2011-02-21 12:08:18 +00004659 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004660 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004661
glennrp47b9dd52010-11-24 18:12:06 +00004662 /* Allocate a MngInfo structure. */
4663
cristy3ed852e2009-09-05 21:47:34 +00004664 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004665 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004666
cristy3ed852e2009-09-05 21:47:34 +00004667 if (mng_info == (MngInfo *) NULL)
4668 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004669
glennrp47b9dd52010-11-24 18:12:06 +00004670 /* Initialize members of the MngInfo structure. */
4671
cristy3ed852e2009-09-05 21:47:34 +00004672 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4673 have_mng_structure=MagickTrue;
4674
4675 mng_info->image=image;
4676 previous=image;
4677 image=ReadOneJNGImage(mng_info,image_info,exception);
4678 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004679
cristy3ed852e2009-09-05 21:47:34 +00004680 if (image == (Image *) NULL)
4681 {
4682 if (IsImageObject(previous) != MagickFalse)
4683 {
4684 (void) CloseBlob(previous);
4685 (void) DestroyImageList(previous);
4686 }
glennrp0fe50b42010-11-16 03:52:51 +00004687
cristy3ed852e2009-09-05 21:47:34 +00004688 if (logging != MagickFalse)
4689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4690 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004691
cristy3ed852e2009-09-05 21:47:34 +00004692 return((Image *) NULL);
4693 }
4694 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004695
cristy3ed852e2009-09-05 21:47:34 +00004696 if (image->columns == 0 || image->rows == 0)
4697 {
4698 if (logging != MagickFalse)
4699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4700 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004701
cristy3ed852e2009-09-05 21:47:34 +00004702 ThrowReaderException(CorruptImageError,"CorruptImage");
4703 }
glennrp0fe50b42010-11-16 03:52:51 +00004704
cristy3ed852e2009-09-05 21:47:34 +00004705 if (logging != MagickFalse)
4706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004707
cristy3ed852e2009-09-05 21:47:34 +00004708 return(image);
4709}
4710#endif
4711
4712static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4713{
4714 char
4715 page_geometry[MaxTextExtent];
4716
4717 Image
4718 *image,
4719 *previous;
4720
cristy4383ec82011-01-05 15:42:32 +00004721 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004722 logging,
4723 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004724
cristy3ed852e2009-09-05 21:47:34 +00004725 volatile int
4726 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004727 object_id,
4728 term_chunk_found,
4729 skip_to_iend;
4730
cristybb503372010-05-27 20:51:26 +00004731 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004732 image_count=0;
4733
4734 MagickBooleanType
4735 status;
4736
4737 MagickOffsetType
4738 offset;
4739
4740 MngInfo
4741 *mng_info;
4742
4743 MngBox
4744 default_fb,
4745 fb,
4746 previous_fb;
4747
4748#if defined(MNG_INSERT_LAYERS)
cristy16ea1392012-03-21 20:38:41 +00004749 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004750 mng_background_color;
4751#endif
4752
4753 register unsigned char
4754 *p;
4755
cristybb503372010-05-27 20:51:26 +00004756 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004757 i;
4758
4759 size_t
4760 count;
4761
cristybb503372010-05-27 20:51:26 +00004762 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004763 loop_level;
4764
4765 volatile short
4766 skipping_loop;
4767
4768#if defined(MNG_INSERT_LAYERS)
4769 unsigned int
4770 mandatory_back=0;
4771#endif
4772
4773 volatile unsigned int
4774#ifdef MNG_OBJECT_BUFFERS
4775 mng_background_object=0,
4776#endif
4777 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4778
cristybb503372010-05-27 20:51:26 +00004779 size_t
cristy3ed852e2009-09-05 21:47:34 +00004780 default_frame_timeout,
4781 frame_timeout,
4782#if defined(MNG_INSERT_LAYERS)
4783 image_height,
4784 image_width,
4785#endif
4786 length;
4787
glennrp38ea0832010-06-02 18:50:28 +00004788 /* These delays are all measured in image ticks_per_second,
4789 * not in MNG ticks_per_second
4790 */
cristybb503372010-05-27 20:51:26 +00004791 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004792 default_frame_delay,
4793 final_delay,
4794 final_image_delay,
4795 frame_delay,
4796#if defined(MNG_INSERT_LAYERS)
4797 insert_layers,
4798#endif
4799 mng_iterations=1,
4800 simplicity=0,
4801 subframe_height=0,
4802 subframe_width=0;
4803
4804 previous_fb.top=0;
4805 previous_fb.bottom=0;
4806 previous_fb.left=0;
4807 previous_fb.right=0;
4808 default_fb.top=0;
4809 default_fb.bottom=0;
4810 default_fb.left=0;
4811 default_fb.right=0;
4812
glennrp47b9dd52010-11-24 18:12:06 +00004813 /* Open image file. */
4814
cristy3ed852e2009-09-05 21:47:34 +00004815 assert(image_info != (const ImageInfo *) NULL);
4816 assert(image_info->signature == MagickSignature);
4817 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4818 assert(exception != (ExceptionInfo *) NULL);
4819 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004820 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004821 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004822 mng_info=(MngInfo *) NULL;
4823 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004824
cristy3ed852e2009-09-05 21:47:34 +00004825 if (status == MagickFalse)
4826 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004827
cristy3ed852e2009-09-05 21:47:34 +00004828 first_mng_object=MagickFalse;
4829 skipping_loop=(-1);
4830 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004831
4832 /* Allocate a MngInfo structure. */
4833
cristy73bd4a52010-10-05 11:24:23 +00004834 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004835
cristy3ed852e2009-09-05 21:47:34 +00004836 if (mng_info == (MngInfo *) NULL)
4837 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004838
glennrp47b9dd52010-11-24 18:12:06 +00004839 /* Initialize members of the MngInfo structure. */
4840
cristy3ed852e2009-09-05 21:47:34 +00004841 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4842 mng_info->image=image;
4843 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004844
4845 if (LocaleCompare(image_info->magick,"MNG") == 0)
4846 {
4847 char
4848 magic_number[MaxTextExtent];
4849
glennrp47b9dd52010-11-24 18:12:06 +00004850 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004851 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4852 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4853 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004854
4855 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004856 for (i=0; i < MNG_MAX_OBJECTS; i++)
4857 {
cristybb503372010-05-27 20:51:26 +00004858 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4859 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004860 }
4861 mng_info->exists[0]=MagickTrue;
4862 }
glennrp47b9dd52010-11-24 18:12:06 +00004863
cristy3ed852e2009-09-05 21:47:34 +00004864 first_mng_object=MagickTrue;
4865 mng_type=0;
4866#if defined(MNG_INSERT_LAYERS)
4867 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4868#endif
4869 default_frame_delay=0;
4870 default_frame_timeout=0;
4871 frame_delay=0;
4872 final_delay=1;
4873 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4874 object_id=0;
4875 skip_to_iend=MagickFalse;
4876 term_chunk_found=MagickFalse;
4877 mng_info->framing_mode=1;
4878#if defined(MNG_INSERT_LAYERS)
4879 mandatory_back=MagickFalse;
4880#endif
4881#if defined(MNG_INSERT_LAYERS)
4882 mng_background_color=image->background_color;
4883#endif
4884 default_fb=mng_info->frame;
4885 previous_fb=mng_info->frame;
4886 do
4887 {
4888 char
4889 type[MaxTextExtent];
4890
4891 if (LocaleCompare(image_info->magick,"MNG") == 0)
4892 {
4893 unsigned char
4894 *chunk;
4895
4896 /*
4897 Read a new chunk.
4898 */
4899 type[0]='\0';
4900 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4901 length=ReadBlobMSBLong(image);
4902 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4903
4904 if (logging != MagickFalse)
4905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004906 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4907 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004908
4909 if (length > PNG_UINT_31_MAX)
4910 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004911
cristy3ed852e2009-09-05 21:47:34 +00004912 if (count == 0)
4913 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004914
cristy3ed852e2009-09-05 21:47:34 +00004915 p=NULL;
4916 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004917
cristy3ed852e2009-09-05 21:47:34 +00004918 if (length)
4919 {
4920 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004921
cristy3ed852e2009-09-05 21:47:34 +00004922 if (chunk == (unsigned char *) NULL)
4923 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004924
cristybb503372010-05-27 20:51:26 +00004925 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004926 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004927
cristy3ed852e2009-09-05 21:47:34 +00004928 p=chunk;
4929 }
glennrp0fe50b42010-11-16 03:52:51 +00004930
cristy3ed852e2009-09-05 21:47:34 +00004931 (void) ReadBlobMSBLong(image); /* read crc word */
4932
4933#if !defined(JNG_SUPPORTED)
4934 if (memcmp(type,mng_JHDR,4) == 0)
4935 {
4936 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004937
cristy3ed852e2009-09-05 21:47:34 +00004938 if (mng_info->jhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00004939 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004940 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004941
cristy3ed852e2009-09-05 21:47:34 +00004942 mng_info->jhdr_warning++;
4943 }
4944#endif
4945 if (memcmp(type,mng_DHDR,4) == 0)
4946 {
4947 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004948
cristy3ed852e2009-09-05 21:47:34 +00004949 if (mng_info->dhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00004950 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004951 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004952
cristy3ed852e2009-09-05 21:47:34 +00004953 mng_info->dhdr_warning++;
4954 }
4955 if (memcmp(type,mng_MEND,4) == 0)
4956 break;
glennrp47b9dd52010-11-24 18:12:06 +00004957
cristy3ed852e2009-09-05 21:47:34 +00004958 if (skip_to_iend)
4959 {
4960 if (memcmp(type,mng_IEND,4) == 0)
4961 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004962
cristy3ed852e2009-09-05 21:47:34 +00004963 if (length)
4964 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004965
cristy3ed852e2009-09-05 21:47:34 +00004966 if (logging != MagickFalse)
4967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4968 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004969
cristy3ed852e2009-09-05 21:47:34 +00004970 continue;
4971 }
glennrp0fe50b42010-11-16 03:52:51 +00004972
cristy3ed852e2009-09-05 21:47:34 +00004973 if (memcmp(type,mng_MHDR,4) == 0)
4974 {
cristybb503372010-05-27 20:51:26 +00004975 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004976 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004977
cristybb503372010-05-27 20:51:26 +00004978 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004979 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004980
cristy3ed852e2009-09-05 21:47:34 +00004981 if (logging != MagickFalse)
4982 {
4983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004984 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004986 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004987 }
glennrp0fe50b42010-11-16 03:52:51 +00004988
cristy3ed852e2009-09-05 21:47:34 +00004989 p+=8;
cristy8182b072010-05-30 20:10:53 +00004990 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004991
cristy3ed852e2009-09-05 21:47:34 +00004992 if (mng_info->ticks_per_second == 0)
4993 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004994
cristy3ed852e2009-09-05 21:47:34 +00004995 else
4996 default_frame_delay=1UL*image->ticks_per_second/
4997 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004998
cristy3ed852e2009-09-05 21:47:34 +00004999 frame_delay=default_frame_delay;
5000 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00005001
cristy3ed852e2009-09-05 21:47:34 +00005002 if (length > 16)
5003 {
5004 p+=16;
cristy8182b072010-05-30 20:10:53 +00005005 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005006 }
glennrp0fe50b42010-11-16 03:52:51 +00005007
cristy3ed852e2009-09-05 21:47:34 +00005008 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00005009
cristy3ed852e2009-09-05 21:47:34 +00005010 if ((simplicity != 0) && ((simplicity | 11) == 11))
5011 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00005012
cristy3ed852e2009-09-05 21:47:34 +00005013 if ((simplicity != 0) && ((simplicity | 9) == 9))
5014 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00005015
cristy3ed852e2009-09-05 21:47:34 +00005016#if defined(MNG_INSERT_LAYERS)
5017 if (mng_type != 3)
5018 insert_layers=MagickTrue;
5019#endif
cristy16ea1392012-03-21 20:38:41 +00005020 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005021 {
glennrp47b9dd52010-11-24 18:12:06 +00005022 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005023 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005024
cristy3ed852e2009-09-05 21:47:34 +00005025 if (GetNextImageInList(image) == (Image *) NULL)
5026 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00005027
cristy3ed852e2009-09-05 21:47:34 +00005028 image=SyncNextImageInList(image);
5029 mng_info->image=image;
5030 }
5031
5032 if ((mng_info->mng_width > 65535L) ||
5033 (mng_info->mng_height > 65535L))
5034 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00005035
cristy3b6fd2e2011-05-20 12:53:50 +00005036 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00005037 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00005038 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00005039
cristy3ed852e2009-09-05 21:47:34 +00005040 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00005041 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00005042 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00005043 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00005044 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005045
cristy3ed852e2009-09-05 21:47:34 +00005046 for (i=0; i < MNG_MAX_OBJECTS; i++)
5047 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00005048
cristy3ed852e2009-09-05 21:47:34 +00005049 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5050 continue;
5051 }
5052
5053 if (memcmp(type,mng_TERM,4) == 0)
5054 {
5055 int
5056 repeat=0;
5057
5058
5059 if (length)
5060 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005061
cristy3ed852e2009-09-05 21:47:34 +00005062 if (repeat == 3)
5063 {
cristy8182b072010-05-30 20:10:53 +00005064 final_delay=(png_uint_32) mng_get_long(&p[2]);
5065 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00005066
cristy3ed852e2009-09-05 21:47:34 +00005067 if (mng_iterations == PNG_UINT_31_MAX)
5068 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00005069
cristy3ed852e2009-09-05 21:47:34 +00005070 image->iterations=mng_iterations;
5071 term_chunk_found=MagickTrue;
5072 }
glennrp0fe50b42010-11-16 03:52:51 +00005073
cristy3ed852e2009-09-05 21:47:34 +00005074 if (logging != MagickFalse)
5075 {
5076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5077 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00005078
cristy3ed852e2009-09-05 21:47:34 +00005079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005080 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00005081
cristy3ed852e2009-09-05 21:47:34 +00005082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005083 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00005084 }
glennrp0fe50b42010-11-16 03:52:51 +00005085
cristy3ed852e2009-09-05 21:47:34 +00005086 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5087 continue;
5088 }
5089 if (memcmp(type,mng_DEFI,4) == 0)
5090 {
5091 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005092 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005093 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5094 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005095
cristy3ed852e2009-09-05 21:47:34 +00005096 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005097
cristy3ed852e2009-09-05 21:47:34 +00005098 if (mng_type == 2 && object_id != 0)
cristy16ea1392012-03-21 20:38:41 +00005099 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005100 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5101 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005102
cristy3ed852e2009-09-05 21:47:34 +00005103 if (object_id > MNG_MAX_OBJECTS)
5104 {
5105 /*
glennrpedaa0382012-04-12 14:16:21 +00005106 Instead of using a warning we should allocate a larger
cristy3ed852e2009-09-05 21:47:34 +00005107 MngInfo structure and continue.
5108 */
cristy16ea1392012-03-21 20:38:41 +00005109 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005110 CoderError,"object id too large","`%s'",image->filename);
5111 object_id=MNG_MAX_OBJECTS;
5112 }
glennrp0fe50b42010-11-16 03:52:51 +00005113
cristy3ed852e2009-09-05 21:47:34 +00005114 if (mng_info->exists[object_id])
5115 if (mng_info->frozen[object_id])
5116 {
5117 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristy16ea1392012-03-21 20:38:41 +00005118 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005119 GetMagickModule(),CoderError,
5120 "DEFI cannot redefine a frozen MNG object","`%s'",
5121 image->filename);
5122 continue;
5123 }
glennrp0fe50b42010-11-16 03:52:51 +00005124
cristy3ed852e2009-09-05 21:47:34 +00005125 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00005126
cristy3ed852e2009-09-05 21:47:34 +00005127 if (length > 2)
5128 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00005129
cristy3ed852e2009-09-05 21:47:34 +00005130 /*
5131 Extract object offset info.
5132 */
5133 if (length > 11)
5134 {
glennrp0fe50b42010-11-16 03:52:51 +00005135 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5136 (p[5] << 16) | (p[6] << 8) | p[7]);
5137
5138 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5139 (p[9] << 16) | (p[10] << 8) | p[11]);
5140
cristy3ed852e2009-09-05 21:47:34 +00005141 if (logging != MagickFalse)
5142 {
5143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005144 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005145 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005146
cristy3ed852e2009-09-05 21:47:34 +00005147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005148 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005149 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005150 }
5151 }
glennrp0fe50b42010-11-16 03:52:51 +00005152
cristy3ed852e2009-09-05 21:47:34 +00005153 /*
5154 Extract object clipping info.
5155 */
5156 if (length > 27)
5157 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5158 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005159
cristy3ed852e2009-09-05 21:47:34 +00005160 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5161 continue;
5162 }
5163 if (memcmp(type,mng_bKGD,4) == 0)
5164 {
5165 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005166
cristy3ed852e2009-09-05 21:47:34 +00005167 if (length > 5)
5168 {
5169 mng_info->mng_global_bkgd.red=
5170 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005171
cristy3ed852e2009-09-05 21:47:34 +00005172 mng_info->mng_global_bkgd.green=
5173 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005174
cristy3ed852e2009-09-05 21:47:34 +00005175 mng_info->mng_global_bkgd.blue=
5176 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005177
cristy3ed852e2009-09-05 21:47:34 +00005178 mng_info->have_global_bkgd=MagickTrue;
5179 }
glennrp0fe50b42010-11-16 03:52:51 +00005180
cristy3ed852e2009-09-05 21:47:34 +00005181 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5182 continue;
5183 }
5184 if (memcmp(type,mng_BACK,4) == 0)
5185 {
5186#if defined(MNG_INSERT_LAYERS)
5187 if (length > 6)
5188 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005189
cristy3ed852e2009-09-05 21:47:34 +00005190 else
5191 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005192
cristy3ed852e2009-09-05 21:47:34 +00005193 if (mandatory_back && length > 5)
5194 {
5195 mng_background_color.red=
5196 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005197
cristy3ed852e2009-09-05 21:47:34 +00005198 mng_background_color.green=
5199 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005200
cristy3ed852e2009-09-05 21:47:34 +00005201 mng_background_color.blue=
5202 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005203
cristy16ea1392012-03-21 20:38:41 +00005204 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005205 }
glennrp0fe50b42010-11-16 03:52:51 +00005206
cristy3ed852e2009-09-05 21:47:34 +00005207#ifdef MNG_OBJECT_BUFFERS
5208 if (length > 8)
5209 mng_background_object=(p[7] << 8) | p[8];
5210#endif
5211#endif
5212 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5213 continue;
5214 }
glennrp47b9dd52010-11-24 18:12:06 +00005215
cristy3ed852e2009-09-05 21:47:34 +00005216 if (memcmp(type,mng_PLTE,4) == 0)
5217 {
glennrp47b9dd52010-11-24 18:12:06 +00005218 /* Read global PLTE. */
5219
cristy3ed852e2009-09-05 21:47:34 +00005220 if (length && (length < 769))
5221 {
5222 if (mng_info->global_plte == (png_colorp) NULL)
5223 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5224 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005225
cristybb503372010-05-27 20:51:26 +00005226 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005227 {
5228 mng_info->global_plte[i].red=p[3*i];
5229 mng_info->global_plte[i].green=p[3*i+1];
5230 mng_info->global_plte[i].blue=p[3*i+2];
5231 }
glennrp0fe50b42010-11-16 03:52:51 +00005232
cristy35ef8242010-06-03 16:24:13 +00005233 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005234 }
5235#ifdef MNG_LOOSE
5236 for ( ; i < 256; i++)
5237 {
5238 mng_info->global_plte[i].red=i;
5239 mng_info->global_plte[i].green=i;
5240 mng_info->global_plte[i].blue=i;
5241 }
glennrp0fe50b42010-11-16 03:52:51 +00005242
cristy3ed852e2009-09-05 21:47:34 +00005243 if (length)
5244 mng_info->global_plte_length=256;
5245#endif
5246 else
5247 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005248
cristy3ed852e2009-09-05 21:47:34 +00005249 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5250 continue;
5251 }
glennrp47b9dd52010-11-24 18:12:06 +00005252
cristy3ed852e2009-09-05 21:47:34 +00005253 if (memcmp(type,mng_tRNS,4) == 0)
5254 {
5255 /* read global tRNS */
5256
5257 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005258 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005259 mng_info->global_trns[i]=p[i];
5260
5261#ifdef MNG_LOOSE
5262 for ( ; i < 256; i++)
5263 mng_info->global_trns[i]=255;
5264#endif
cristy12560f32010-06-03 16:51:08 +00005265 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005266 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5267 continue;
5268 }
5269 if (memcmp(type,mng_gAMA,4) == 0)
5270 {
5271 if (length == 4)
5272 {
cristybb503372010-05-27 20:51:26 +00005273 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005274 igamma;
5275
cristy8182b072010-05-30 20:10:53 +00005276 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005277 mng_info->global_gamma=((float) igamma)*0.00001;
5278 mng_info->have_global_gama=MagickTrue;
5279 }
glennrp0fe50b42010-11-16 03:52:51 +00005280
cristy3ed852e2009-09-05 21:47:34 +00005281 else
5282 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005283
cristy3ed852e2009-09-05 21:47:34 +00005284 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5285 continue;
5286 }
5287
5288 if (memcmp(type,mng_cHRM,4) == 0)
5289 {
glennrp47b9dd52010-11-24 18:12:06 +00005290 /* Read global cHRM */
5291
cristy3ed852e2009-09-05 21:47:34 +00005292 if (length == 32)
5293 {
cristy8182b072010-05-30 20:10:53 +00005294 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5295 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5296 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005297 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005298 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005299 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005300 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005301 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005302 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005303 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005304 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005305 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005306 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005307 mng_info->have_global_chrm=MagickTrue;
5308 }
5309 else
5310 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005311
cristy3ed852e2009-09-05 21:47:34 +00005312 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5313 continue;
5314 }
glennrp47b9dd52010-11-24 18:12:06 +00005315
cristy3ed852e2009-09-05 21:47:34 +00005316 if (memcmp(type,mng_sRGB,4) == 0)
5317 {
5318 /*
5319 Read global sRGB.
5320 */
5321 if (length)
5322 {
glennrpe610a072010-08-05 17:08:46 +00005323 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005324 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005325 mng_info->have_global_srgb=MagickTrue;
5326 }
5327 else
5328 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005329
cristy3ed852e2009-09-05 21:47:34 +00005330 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5331 continue;
5332 }
glennrp47b9dd52010-11-24 18:12:06 +00005333
cristy3ed852e2009-09-05 21:47:34 +00005334 if (memcmp(type,mng_iCCP,4) == 0)
5335 {
glennrpfd05d622011-02-25 04:10:33 +00005336 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005337
5338 /*
5339 Read global iCCP.
5340 */
5341 if (length)
5342 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005343
cristy3ed852e2009-09-05 21:47:34 +00005344 continue;
5345 }
glennrp47b9dd52010-11-24 18:12:06 +00005346
cristy3ed852e2009-09-05 21:47:34 +00005347 if (memcmp(type,mng_FRAM,4) == 0)
5348 {
5349 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005350 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005351 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5352 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005353
cristy3ed852e2009-09-05 21:47:34 +00005354 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5355 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005356
cristy3ed852e2009-09-05 21:47:34 +00005357 frame_delay=default_frame_delay;
5358 frame_timeout=default_frame_timeout;
5359 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005360
cristy3ed852e2009-09-05 21:47:34 +00005361 if (length)
5362 if (p[0])
5363 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005364
cristy3ed852e2009-09-05 21:47:34 +00005365 if (logging != MagickFalse)
5366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5367 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005368
cristy3ed852e2009-09-05 21:47:34 +00005369 if (length > 6)
5370 {
glennrp47b9dd52010-11-24 18:12:06 +00005371 /* Note the delay and frame clipping boundaries. */
5372
cristy3ed852e2009-09-05 21:47:34 +00005373 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005374
cristybb503372010-05-27 20:51:26 +00005375 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005376 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005377
cristy3ed852e2009-09-05 21:47:34 +00005378 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005379
cristybb503372010-05-27 20:51:26 +00005380 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005381 {
5382 int
5383 change_delay,
5384 change_timeout,
5385 change_clipping;
5386
5387 change_delay=(*p++);
5388 change_timeout=(*p++);
5389 change_clipping=(*p++);
5390 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005391
cristy3ed852e2009-09-05 21:47:34 +00005392 if (change_delay)
5393 {
cristy8182b072010-05-30 20:10:53 +00005394 frame_delay=1UL*image->ticks_per_second*
5395 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005396
cristy8182b072010-05-30 20:10:53 +00005397 if (mng_info->ticks_per_second != 0)
5398 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005399
glennrpbb010dd2010-06-01 13:07:15 +00005400 else
5401 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005402
cristy3ed852e2009-09-05 21:47:34 +00005403 if (change_delay == 2)
5404 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005405
cristy3ed852e2009-09-05 21:47:34 +00005406 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005407
cristy3ed852e2009-09-05 21:47:34 +00005408 if (logging != MagickFalse)
5409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005410 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005411 }
glennrp47b9dd52010-11-24 18:12:06 +00005412
cristy3ed852e2009-09-05 21:47:34 +00005413 if (change_timeout)
5414 {
glennrpbb010dd2010-06-01 13:07:15 +00005415 frame_timeout=1UL*image->ticks_per_second*
5416 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005417
glennrpbb010dd2010-06-01 13:07:15 +00005418 if (mng_info->ticks_per_second != 0)
5419 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005420
glennrpbb010dd2010-06-01 13:07:15 +00005421 else
5422 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005423
cristy3ed852e2009-09-05 21:47:34 +00005424 if (change_delay == 2)
5425 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005426
cristy3ed852e2009-09-05 21:47:34 +00005427 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005428
cristy3ed852e2009-09-05 21:47:34 +00005429 if (logging != MagickFalse)
5430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005431 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005432 }
glennrp47b9dd52010-11-24 18:12:06 +00005433
cristy3ed852e2009-09-05 21:47:34 +00005434 if (change_clipping)
5435 {
5436 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5437 p+=17;
5438 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005439
cristy3ed852e2009-09-05 21:47:34 +00005440 if (logging != MagickFalse)
5441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005442 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005443 (double) fb.left,(double) fb.right,(double) fb.top,
5444 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005445
cristy3ed852e2009-09-05 21:47:34 +00005446 if (change_clipping == 2)
5447 default_fb=fb;
5448 }
5449 }
5450 }
5451 mng_info->clip=fb;
5452 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005453
cristybb503372010-05-27 20:51:26 +00005454 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005455 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005456
cristybb503372010-05-27 20:51:26 +00005457 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005458 -mng_info->clip.top);
5459 /*
5460 Insert a background layer behind the frame if framing_mode is 4.
5461 */
5462#if defined(MNG_INSERT_LAYERS)
5463 if (logging != MagickFalse)
5464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005465 " subframe_width=%.20g, subframe_height=%.20g",(double)
5466 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005467
cristy3ed852e2009-09-05 21:47:34 +00005468 if (insert_layers && (mng_info->framing_mode == 4) &&
5469 (subframe_width) && (subframe_height))
5470 {
glennrp47b9dd52010-11-24 18:12:06 +00005471 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005472 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005473 {
cristy16ea1392012-03-21 20:38:41 +00005474 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005475
cristy3ed852e2009-09-05 21:47:34 +00005476 if (GetNextImageInList(image) == (Image *) NULL)
5477 {
5478 image=DestroyImageList(image);
5479 MngInfoFreeStruct(mng_info,&have_mng_structure);
5480 return((Image *) NULL);
5481 }
glennrp47b9dd52010-11-24 18:12:06 +00005482
cristy3ed852e2009-09-05 21:47:34 +00005483 image=SyncNextImageInList(image);
5484 }
glennrp0fe50b42010-11-16 03:52:51 +00005485
cristy3ed852e2009-09-05 21:47:34 +00005486 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005487
cristy3ed852e2009-09-05 21:47:34 +00005488 if (term_chunk_found)
5489 {
5490 image->start_loop=MagickTrue;
5491 image->iterations=mng_iterations;
5492 term_chunk_found=MagickFalse;
5493 }
glennrp0fe50b42010-11-16 03:52:51 +00005494
cristy3ed852e2009-09-05 21:47:34 +00005495 else
5496 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005497
cristy3ed852e2009-09-05 21:47:34 +00005498 image->columns=subframe_width;
5499 image->rows=subframe_height;
5500 image->page.width=subframe_width;
5501 image->page.height=subframe_height;
5502 image->page.x=mng_info->clip.left;
5503 image->page.y=mng_info->clip.top;
5504 image->background_color=mng_background_color;
5505 image->matte=MagickFalse;
5506 image->delay=0;
cristy16ea1392012-03-21 20:38:41 +00005507 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005508
cristy3ed852e2009-09-05 21:47:34 +00005509 if (logging != MagickFalse)
5510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005511 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005512 (double) mng_info->clip.left,(double) mng_info->clip.right,
5513 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005514 }
5515#endif
5516 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5517 continue;
5518 }
5519 if (memcmp(type,mng_CLIP,4) == 0)
5520 {
5521 unsigned int
5522 first_object,
5523 last_object;
5524
5525 /*
5526 Read CLIP.
5527 */
5528 first_object=(p[0] << 8) | p[1];
5529 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005530
cristy3ed852e2009-09-05 21:47:34 +00005531 for (i=(int) first_object; i <= (int) last_object; i++)
5532 {
5533 if (mng_info->exists[i] && !mng_info->frozen[i])
5534 {
5535 MngBox
5536 box;
5537
5538 box=mng_info->object_clip[i];
5539 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5540 }
5541 }
glennrp47b9dd52010-11-24 18:12:06 +00005542
cristy3ed852e2009-09-05 21:47:34 +00005543 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5544 continue;
5545 }
5546 if (memcmp(type,mng_SAVE,4) == 0)
5547 {
5548 for (i=1; i < MNG_MAX_OBJECTS; i++)
5549 if (mng_info->exists[i])
5550 {
5551 mng_info->frozen[i]=MagickTrue;
5552#ifdef MNG_OBJECT_BUFFERS
5553 if (mng_info->ob[i] != (MngBuffer *) NULL)
5554 mng_info->ob[i]->frozen=MagickTrue;
5555#endif
5556 }
glennrp0fe50b42010-11-16 03:52:51 +00005557
cristy3ed852e2009-09-05 21:47:34 +00005558 if (length)
5559 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005560
cristy3ed852e2009-09-05 21:47:34 +00005561 continue;
5562 }
5563
5564 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5565 {
glennrp47b9dd52010-11-24 18:12:06 +00005566 /* Read DISC or SEEK. */
5567
cristy3ed852e2009-09-05 21:47:34 +00005568 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5569 {
5570 for (i=1; i < MNG_MAX_OBJECTS; i++)
5571 MngInfoDiscardObject(mng_info,i);
5572 }
glennrp0fe50b42010-11-16 03:52:51 +00005573
cristy3ed852e2009-09-05 21:47:34 +00005574 else
5575 {
cristybb503372010-05-27 20:51:26 +00005576 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005577 j;
5578
cristybb503372010-05-27 20:51:26 +00005579 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005580 {
5581 i=p[j] << 8 | p[j+1];
5582 MngInfoDiscardObject(mng_info,i);
5583 }
5584 }
glennrp0fe50b42010-11-16 03:52:51 +00005585
cristy3ed852e2009-09-05 21:47:34 +00005586 if (length)
5587 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005588
cristy3ed852e2009-09-05 21:47:34 +00005589 continue;
5590 }
glennrp47b9dd52010-11-24 18:12:06 +00005591
cristy3ed852e2009-09-05 21:47:34 +00005592 if (memcmp(type,mng_MOVE,4) == 0)
5593 {
cristybb503372010-05-27 20:51:26 +00005594 size_t
cristy3ed852e2009-09-05 21:47:34 +00005595 first_object,
5596 last_object;
5597
glennrp47b9dd52010-11-24 18:12:06 +00005598 /* read MOVE */
5599
cristy3ed852e2009-09-05 21:47:34 +00005600 first_object=(p[0] << 8) | p[1];
5601 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005602 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005603 {
5604 if (mng_info->exists[i] && !mng_info->frozen[i])
5605 {
5606 MngPair
5607 new_pair;
5608
5609 MngPair
5610 old_pair;
5611
5612 old_pair.a=mng_info->x_off[i];
5613 old_pair.b=mng_info->y_off[i];
5614 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5615 mng_info->x_off[i]=new_pair.a;
5616 mng_info->y_off[i]=new_pair.b;
5617 }
5618 }
glennrp47b9dd52010-11-24 18:12:06 +00005619
cristy3ed852e2009-09-05 21:47:34 +00005620 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5621 continue;
5622 }
5623
5624 if (memcmp(type,mng_LOOP,4) == 0)
5625 {
cristybb503372010-05-27 20:51:26 +00005626 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005627 loop_level=chunk[0];
5628 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005629
5630 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005631 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005632
cristy3ed852e2009-09-05 21:47:34 +00005633 if (logging != MagickFalse)
5634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005635 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5636 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005637
cristy3ed852e2009-09-05 21:47:34 +00005638 if (loop_iters == 0)
5639 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005640
cristy3ed852e2009-09-05 21:47:34 +00005641 else
5642 {
5643 mng_info->loop_jump[loop_level]=TellBlob(image);
5644 mng_info->loop_count[loop_level]=loop_iters;
5645 }
glennrp0fe50b42010-11-16 03:52:51 +00005646
cristy3ed852e2009-09-05 21:47:34 +00005647 mng_info->loop_iteration[loop_level]=0;
5648 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5649 continue;
5650 }
glennrp47b9dd52010-11-24 18:12:06 +00005651
cristy3ed852e2009-09-05 21:47:34 +00005652 if (memcmp(type,mng_ENDL,4) == 0)
5653 {
5654 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005655
cristy3ed852e2009-09-05 21:47:34 +00005656 if (skipping_loop > 0)
5657 {
5658 if (skipping_loop == loop_level)
5659 {
5660 /*
5661 Found end of zero-iteration loop.
5662 */
5663 skipping_loop=(-1);
5664 mng_info->loop_active[loop_level]=0;
5665 }
5666 }
glennrp47b9dd52010-11-24 18:12:06 +00005667
cristy3ed852e2009-09-05 21:47:34 +00005668 else
5669 {
5670 if (mng_info->loop_active[loop_level] == 1)
5671 {
5672 mng_info->loop_count[loop_level]--;
5673 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005674
cristy3ed852e2009-09-05 21:47:34 +00005675 if (logging != MagickFalse)
5676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005677 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005678 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005679 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005680
cristy3ed852e2009-09-05 21:47:34 +00005681 if (mng_info->loop_count[loop_level] != 0)
5682 {
5683 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5684 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005685
cristy3ed852e2009-09-05 21:47:34 +00005686 if (offset < 0)
5687 ThrowReaderException(CorruptImageError,
5688 "ImproperImageHeader");
5689 }
glennrp47b9dd52010-11-24 18:12:06 +00005690
cristy3ed852e2009-09-05 21:47:34 +00005691 else
5692 {
5693 short
5694 last_level;
5695
5696 /*
5697 Finished loop.
5698 */
5699 mng_info->loop_active[loop_level]=0;
5700 last_level=(-1);
5701 for (i=0; i < loop_level; i++)
5702 if (mng_info->loop_active[i] == 1)
5703 last_level=(short) i;
5704 loop_level=last_level;
5705 }
5706 }
5707 }
glennrp47b9dd52010-11-24 18:12:06 +00005708
cristy3ed852e2009-09-05 21:47:34 +00005709 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5710 continue;
5711 }
glennrp47b9dd52010-11-24 18:12:06 +00005712
cristy3ed852e2009-09-05 21:47:34 +00005713 if (memcmp(type,mng_CLON,4) == 0)
5714 {
5715 if (mng_info->clon_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005716 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005717 CoderError,"CLON is not implemented yet","`%s'",
5718 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005719
cristy3ed852e2009-09-05 21:47:34 +00005720 mng_info->clon_warning++;
5721 }
glennrp47b9dd52010-11-24 18:12:06 +00005722
cristy3ed852e2009-09-05 21:47:34 +00005723 if (memcmp(type,mng_MAGN,4) == 0)
5724 {
5725 png_uint_16
5726 magn_first,
5727 magn_last,
5728 magn_mb,
5729 magn_ml,
5730 magn_mr,
5731 magn_mt,
5732 magn_mx,
5733 magn_my,
5734 magn_methx,
5735 magn_methy;
5736
5737 if (length > 1)
5738 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005739
cristy3ed852e2009-09-05 21:47:34 +00005740 else
5741 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005742
cristy3ed852e2009-09-05 21:47:34 +00005743 if (length > 3)
5744 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005745
cristy3ed852e2009-09-05 21:47:34 +00005746 else
5747 magn_last=magn_first;
5748#ifndef MNG_OBJECT_BUFFERS
5749 if (magn_first || magn_last)
5750 if (mng_info->magn_warning == 0)
5751 {
cristy16ea1392012-03-21 20:38:41 +00005752 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005753 GetMagickModule(),CoderError,
5754 "MAGN is not implemented yet for nonzero objects",
5755 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005756
cristy3ed852e2009-09-05 21:47:34 +00005757 mng_info->magn_warning++;
5758 }
5759#endif
5760 if (length > 4)
5761 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005762
cristy3ed852e2009-09-05 21:47:34 +00005763 else
5764 magn_methx=0;
5765
5766 if (length > 6)
5767 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005768
cristy3ed852e2009-09-05 21:47:34 +00005769 else
5770 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005771
cristy3ed852e2009-09-05 21:47:34 +00005772 if (magn_mx == 0)
5773 magn_mx=1;
5774
5775 if (length > 8)
5776 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005777
cristy3ed852e2009-09-05 21:47:34 +00005778 else
5779 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005780
cristy3ed852e2009-09-05 21:47:34 +00005781 if (magn_my == 0)
5782 magn_my=1;
5783
5784 if (length > 10)
5785 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005786
cristy3ed852e2009-09-05 21:47:34 +00005787 else
5788 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005789
cristy3ed852e2009-09-05 21:47:34 +00005790 if (magn_ml == 0)
5791 magn_ml=1;
5792
5793 if (length > 12)
5794 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005795
cristy3ed852e2009-09-05 21:47:34 +00005796 else
5797 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005798
cristy3ed852e2009-09-05 21:47:34 +00005799 if (magn_mr == 0)
5800 magn_mr=1;
5801
5802 if (length > 14)
5803 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005804
cristy3ed852e2009-09-05 21:47:34 +00005805 else
5806 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005807
cristy3ed852e2009-09-05 21:47:34 +00005808 if (magn_mt == 0)
5809 magn_mt=1;
5810
5811 if (length > 16)
5812 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005813
cristy3ed852e2009-09-05 21:47:34 +00005814 else
5815 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005816
cristy3ed852e2009-09-05 21:47:34 +00005817 if (magn_mb == 0)
5818 magn_mb=1;
5819
5820 if (length > 17)
5821 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005822
cristy3ed852e2009-09-05 21:47:34 +00005823 else
5824 magn_methy=magn_methx;
5825
glennrp47b9dd52010-11-24 18:12:06 +00005826
cristy3ed852e2009-09-05 21:47:34 +00005827 if (magn_methx > 5 || magn_methy > 5)
5828 if (mng_info->magn_warning == 0)
5829 {
cristy16ea1392012-03-21 20:38:41 +00005830 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005831 GetMagickModule(),CoderError,
5832 "Unknown MAGN method in MNG datastream","`%s'",
5833 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristy3ed852e2009-09-05 21:47:34 +00005835 mng_info->magn_warning++;
5836 }
5837#ifdef MNG_OBJECT_BUFFERS
5838 /* Magnify existing objects in the range magn_first to magn_last */
5839#endif
5840 if (magn_first == 0 || magn_last == 0)
5841 {
5842 /* Save the magnification factors for object 0 */
5843 mng_info->magn_mb=magn_mb;
5844 mng_info->magn_ml=magn_ml;
5845 mng_info->magn_mr=magn_mr;
5846 mng_info->magn_mt=magn_mt;
5847 mng_info->magn_mx=magn_mx;
5848 mng_info->magn_my=magn_my;
5849 mng_info->magn_methx=magn_methx;
5850 mng_info->magn_methy=magn_methy;
5851 }
5852 }
glennrp47b9dd52010-11-24 18:12:06 +00005853
cristy3ed852e2009-09-05 21:47:34 +00005854 if (memcmp(type,mng_PAST,4) == 0)
5855 {
5856 if (mng_info->past_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005857 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005858 CoderError,"PAST is not implemented yet","`%s'",
5859 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005860
cristy3ed852e2009-09-05 21:47:34 +00005861 mng_info->past_warning++;
5862 }
glennrp47b9dd52010-11-24 18:12:06 +00005863
cristy3ed852e2009-09-05 21:47:34 +00005864 if (memcmp(type,mng_SHOW,4) == 0)
5865 {
5866 if (mng_info->show_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005867 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005868 CoderError,"SHOW is not implemented yet","`%s'",
5869 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005870
cristy3ed852e2009-09-05 21:47:34 +00005871 mng_info->show_warning++;
5872 }
glennrp47b9dd52010-11-24 18:12:06 +00005873
cristy3ed852e2009-09-05 21:47:34 +00005874 if (memcmp(type,mng_sBIT,4) == 0)
5875 {
5876 if (length < 4)
5877 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005878
cristy3ed852e2009-09-05 21:47:34 +00005879 else
5880 {
5881 mng_info->global_sbit.gray=p[0];
5882 mng_info->global_sbit.red=p[0];
5883 mng_info->global_sbit.green=p[1];
5884 mng_info->global_sbit.blue=p[2];
5885 mng_info->global_sbit.alpha=p[3];
5886 mng_info->have_global_sbit=MagickTrue;
5887 }
5888 }
5889 if (memcmp(type,mng_pHYs,4) == 0)
5890 {
5891 if (length > 8)
5892 {
5893 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005894 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005895 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005896 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005897 mng_info->global_phys_unit_type=p[8];
5898 mng_info->have_global_phys=MagickTrue;
5899 }
glennrp47b9dd52010-11-24 18:12:06 +00005900
cristy3ed852e2009-09-05 21:47:34 +00005901 else
5902 mng_info->have_global_phys=MagickFalse;
5903 }
5904 if (memcmp(type,mng_pHYg,4) == 0)
5905 {
5906 if (mng_info->phyg_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005907 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005908 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005909
cristy3ed852e2009-09-05 21:47:34 +00005910 mng_info->phyg_warning++;
5911 }
5912 if (memcmp(type,mng_BASI,4) == 0)
5913 {
5914 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005915
cristy3ed852e2009-09-05 21:47:34 +00005916 if (mng_info->basi_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005917 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005918 CoderError,"BASI is not implemented yet","`%s'",
5919 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005920
cristy3ed852e2009-09-05 21:47:34 +00005921 mng_info->basi_warning++;
5922#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005923 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005924 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005925 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005926 (p[6] << 8) | p[7]);
5927 basi_color_type=p[8];
5928 basi_compression_method=p[9];
5929 basi_filter_type=p[10];
5930 basi_interlace_method=p[11];
5931 if (length > 11)
5932 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005933
cristy3ed852e2009-09-05 21:47:34 +00005934 else
5935 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005936
cristy3ed852e2009-09-05 21:47:34 +00005937 if (length > 13)
5938 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005939
cristy3ed852e2009-09-05 21:47:34 +00005940 else
5941 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005942
cristy3ed852e2009-09-05 21:47:34 +00005943 if (length > 15)
5944 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005945
cristy3ed852e2009-09-05 21:47:34 +00005946 else
5947 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005948
cristy3ed852e2009-09-05 21:47:34 +00005949 if (length > 17)
5950 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005951
cristy3ed852e2009-09-05 21:47:34 +00005952 else
5953 {
5954 if (basi_sample_depth == 16)
5955 basi_alpha=65535L;
5956 else
5957 basi_alpha=255;
5958 }
glennrp47b9dd52010-11-24 18:12:06 +00005959
cristy3ed852e2009-09-05 21:47:34 +00005960 if (length > 19)
5961 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005962
cristy3ed852e2009-09-05 21:47:34 +00005963 else
5964 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005965
cristy3ed852e2009-09-05 21:47:34 +00005966#endif
5967 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5968 continue;
5969 }
glennrp47b9dd52010-11-24 18:12:06 +00005970
cristy3ed852e2009-09-05 21:47:34 +00005971 if (memcmp(type,mng_IHDR,4)
5972#if defined(JNG_SUPPORTED)
5973 && memcmp(type,mng_JHDR,4)
5974#endif
5975 )
5976 {
5977 /* Not an IHDR or JHDR chunk */
5978 if (length)
5979 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005980
cristy3ed852e2009-09-05 21:47:34 +00005981 continue;
5982 }
5983/* Process IHDR */
5984 if (logging != MagickFalse)
5985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5986 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005987
cristy3ed852e2009-09-05 21:47:34 +00005988 mng_info->exists[object_id]=MagickTrue;
5989 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005990
cristy3ed852e2009-09-05 21:47:34 +00005991 if (mng_info->invisible[object_id])
5992 {
5993 if (logging != MagickFalse)
5994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5995 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005996
cristy3ed852e2009-09-05 21:47:34 +00005997 skip_to_iend=MagickTrue;
5998 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5999 continue;
6000 }
6001#if defined(MNG_INSERT_LAYERS)
6002 if (length < 8)
6003 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00006004
cristy8182b072010-05-30 20:10:53 +00006005 image_width=(size_t) mng_get_long(p);
6006 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00006007#endif
6008 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6009
6010 /*
6011 Insert a transparent background layer behind the entire animation
6012 if it is not full screen.
6013 */
6014#if defined(MNG_INSERT_LAYERS)
6015 if (insert_layers && mng_type && first_mng_object)
6016 {
6017 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6018 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00006019 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00006020 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00006021 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00006022 {
cristy16ea1392012-03-21 20:38:41 +00006023 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006024 {
6025 /*
6026 Allocate next image structure.
6027 */
cristy16ea1392012-03-21 20:38:41 +00006028 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006029
cristy3ed852e2009-09-05 21:47:34 +00006030 if (GetNextImageInList(image) == (Image *) NULL)
6031 {
6032 image=DestroyImageList(image);
6033 MngInfoFreeStruct(mng_info,&have_mng_structure);
6034 return((Image *) NULL);
6035 }
glennrp47b9dd52010-11-24 18:12:06 +00006036
cristy3ed852e2009-09-05 21:47:34 +00006037 image=SyncNextImageInList(image);
6038 }
6039 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00006040
cristy3ed852e2009-09-05 21:47:34 +00006041 if (term_chunk_found)
6042 {
6043 image->start_loop=MagickTrue;
6044 image->iterations=mng_iterations;
6045 term_chunk_found=MagickFalse;
6046 }
glennrp47b9dd52010-11-24 18:12:06 +00006047
cristy3ed852e2009-09-05 21:47:34 +00006048 else
6049 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006050
6051 /* Make a background rectangle. */
6052
cristy3ed852e2009-09-05 21:47:34 +00006053 image->delay=0;
6054 image->columns=mng_info->mng_width;
6055 image->rows=mng_info->mng_height;
6056 image->page.width=mng_info->mng_width;
6057 image->page.height=mng_info->mng_height;
6058 image->page.x=0;
6059 image->page.y=0;
6060 image->background_color=mng_background_color;
cristy16ea1392012-03-21 20:38:41 +00006061 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006062 if (logging != MagickFalse)
6063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006064 " Inserted transparent background layer, W=%.20g, H=%.20g",
6065 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00006066 }
6067 }
6068 /*
6069 Insert a background layer behind the upcoming image if
6070 framing_mode is 3, and we haven't already inserted one.
6071 */
6072 if (insert_layers && (mng_info->framing_mode == 3) &&
6073 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6074 (simplicity & 0x08)))
6075 {
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 }
glennrp0fe50b42010-11-16 03:52:51 +00006092
cristy3ed852e2009-09-05 21:47:34 +00006093 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00006094
cristy3ed852e2009-09-05 21:47:34 +00006095 if (term_chunk_found)
6096 {
6097 image->start_loop=MagickTrue;
6098 image->iterations=mng_iterations;
6099 term_chunk_found=MagickFalse;
6100 }
glennrp0fe50b42010-11-16 03:52:51 +00006101
cristy3ed852e2009-09-05 21:47:34 +00006102 else
6103 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006104
cristy3ed852e2009-09-05 21:47:34 +00006105 image->delay=0;
6106 image->columns=subframe_width;
6107 image->rows=subframe_height;
6108 image->page.width=subframe_width;
6109 image->page.height=subframe_height;
6110 image->page.x=mng_info->clip.left;
6111 image->page.y=mng_info->clip.top;
6112 image->background_color=mng_background_color;
6113 image->matte=MagickFalse;
cristy16ea1392012-03-21 20:38:41 +00006114 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006115
cristy3ed852e2009-09-05 21:47:34 +00006116 if (logging != MagickFalse)
6117 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00006118 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00006119 (double) mng_info->clip.left,(double) mng_info->clip.right,
6120 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00006121 }
6122#endif /* MNG_INSERT_LAYERS */
6123 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006124
cristy16ea1392012-03-21 20:38:41 +00006125 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006126 {
6127 /*
6128 Allocate next image structure.
6129 */
cristy16ea1392012-03-21 20:38:41 +00006130 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006131
cristy3ed852e2009-09-05 21:47:34 +00006132 if (GetNextImageInList(image) == (Image *) NULL)
6133 {
6134 image=DestroyImageList(image);
6135 MngInfoFreeStruct(mng_info,&have_mng_structure);
6136 return((Image *) NULL);
6137 }
glennrp47b9dd52010-11-24 18:12:06 +00006138
cristy3ed852e2009-09-05 21:47:34 +00006139 image=SyncNextImageInList(image);
6140 }
6141 mng_info->image=image;
6142 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6143 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006144
cristy3ed852e2009-09-05 21:47:34 +00006145 if (status == MagickFalse)
6146 break;
glennrp0fe50b42010-11-16 03:52:51 +00006147
cristy3ed852e2009-09-05 21:47:34 +00006148 if (term_chunk_found)
6149 {
6150 image->start_loop=MagickTrue;
6151 term_chunk_found=MagickFalse;
6152 }
glennrp0fe50b42010-11-16 03:52:51 +00006153
cristy3ed852e2009-09-05 21:47:34 +00006154 else
6155 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006156
cristy3ed852e2009-09-05 21:47:34 +00006157 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6158 {
6159 image->delay=frame_delay;
6160 frame_delay=default_frame_delay;
6161 }
glennrp0fe50b42010-11-16 03:52:51 +00006162
cristy3ed852e2009-09-05 21:47:34 +00006163 else
6164 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006165
cristy3ed852e2009-09-05 21:47:34 +00006166 image->page.width=mng_info->mng_width;
6167 image->page.height=mng_info->mng_height;
6168 image->page.x=mng_info->x_off[object_id];
6169 image->page.y=mng_info->y_off[object_id];
6170 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006171
cristy3ed852e2009-09-05 21:47:34 +00006172 /*
6173 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6174 */
glennrp47b9dd52010-11-24 18:12:06 +00006175
cristy3ed852e2009-09-05 21:47:34 +00006176 if (logging != MagickFalse)
6177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6178 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6179 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006180
cristybb503372010-05-27 20:51:26 +00006181 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006182
cristy3ed852e2009-09-05 21:47:34 +00006183 if (offset < 0)
6184 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6185 }
6186
6187 previous=image;
6188 mng_info->image=image;
6189 mng_info->mng_type=mng_type;
6190 mng_info->object_id=object_id;
6191
6192 if (memcmp(type,mng_IHDR,4) == 0)
6193 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006194
cristy3ed852e2009-09-05 21:47:34 +00006195#if defined(JNG_SUPPORTED)
6196 else
6197 image=ReadOneJNGImage(mng_info,image_info,exception);
6198#endif
6199
6200 if (image == (Image *) NULL)
6201 {
6202 if (IsImageObject(previous) != MagickFalse)
6203 {
6204 (void) DestroyImageList(previous);
6205 (void) CloseBlob(previous);
6206 }
glennrp47b9dd52010-11-24 18:12:06 +00006207
cristy3ed852e2009-09-05 21:47:34 +00006208 MngInfoFreeStruct(mng_info,&have_mng_structure);
6209 return((Image *) NULL);
6210 }
glennrp0fe50b42010-11-16 03:52:51 +00006211
cristy3ed852e2009-09-05 21:47:34 +00006212 if (image->columns == 0 || image->rows == 0)
6213 {
6214 (void) CloseBlob(image);
6215 image=DestroyImageList(image);
6216 MngInfoFreeStruct(mng_info,&have_mng_structure);
6217 return((Image *) NULL);
6218 }
glennrp0fe50b42010-11-16 03:52:51 +00006219
cristy3ed852e2009-09-05 21:47:34 +00006220 mng_info->image=image;
6221
6222 if (mng_type)
6223 {
6224 MngBox
6225 crop_box;
6226
6227 if (mng_info->magn_methx || mng_info->magn_methy)
6228 {
6229 png_uint_32
6230 magnified_height,
6231 magnified_width;
6232
6233 if (logging != MagickFalse)
6234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6235 " Processing MNG MAGN chunk");
6236
6237 if (mng_info->magn_methx == 1)
6238 {
6239 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006240
cristy3ed852e2009-09-05 21:47:34 +00006241 if (image->columns > 1)
6242 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006243
cristy3ed852e2009-09-05 21:47:34 +00006244 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006245 magnified_width += (png_uint_32)
6246 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006247 }
glennrp47b9dd52010-11-24 18:12:06 +00006248
cristy3ed852e2009-09-05 21:47:34 +00006249 else
6250 {
cristy4e5bc842010-06-09 13:56:01 +00006251 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006252
cristy3ed852e2009-09-05 21:47:34 +00006253 if (image->columns > 1)
6254 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006255
cristy3ed852e2009-09-05 21:47:34 +00006256 if (image->columns > 2)
6257 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006258
cristy3ed852e2009-09-05 21:47:34 +00006259 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006260 magnified_width += (png_uint_32)
6261 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006262 }
glennrp47b9dd52010-11-24 18:12:06 +00006263
cristy3ed852e2009-09-05 21:47:34 +00006264 if (mng_info->magn_methy == 1)
6265 {
6266 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006267
cristy3ed852e2009-09-05 21:47:34 +00006268 if (image->rows > 1)
6269 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006270
cristy3ed852e2009-09-05 21:47:34 +00006271 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006272 magnified_height += (png_uint_32)
6273 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006274 }
glennrp47b9dd52010-11-24 18:12:06 +00006275
cristy3ed852e2009-09-05 21:47:34 +00006276 else
6277 {
cristy4e5bc842010-06-09 13:56:01 +00006278 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006279
cristy3ed852e2009-09-05 21:47:34 +00006280 if (image->rows > 1)
6281 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006282
cristy3ed852e2009-09-05 21:47:34 +00006283 if (image->rows > 2)
6284 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006285
cristy3ed852e2009-09-05 21:47:34 +00006286 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006287 magnified_height += (png_uint_32)
6288 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006289 }
glennrp47b9dd52010-11-24 18:12:06 +00006290
cristy3ed852e2009-09-05 21:47:34 +00006291 if (magnified_height > image->rows ||
6292 magnified_width > image->columns)
6293 {
6294 Image
6295 *large_image;
6296
6297 int
6298 yy;
6299
cristy16ea1392012-03-21 20:38:41 +00006300 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006301 *next,
6302 *prev;
6303
6304 png_uint_16
6305 magn_methx,
6306 magn_methy;
6307
cristy16ea1392012-03-21 20:38:41 +00006308 ssize_t
6309 m,
6310 y;
6311
6312 register Quantum
6313 *n,
6314 *q;
6315
6316 register ssize_t
6317 x;
6318
glennrp47b9dd52010-11-24 18:12:06 +00006319 /* Allocate next image structure. */
6320
cristy3ed852e2009-09-05 21:47:34 +00006321 if (logging != MagickFalse)
6322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6323 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006324
cristy16ea1392012-03-21 20:38:41 +00006325 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006326
cristy3ed852e2009-09-05 21:47:34 +00006327 if (GetNextImageInList(image) == (Image *) NULL)
6328 {
6329 image=DestroyImageList(image);
6330 MngInfoFreeStruct(mng_info,&have_mng_structure);
6331 return((Image *) NULL);
6332 }
6333
6334 large_image=SyncNextImageInList(image);
6335
6336 large_image->columns=magnified_width;
6337 large_image->rows=magnified_height;
6338
6339 magn_methx=mng_info->magn_methx;
6340 magn_methy=mng_info->magn_methy;
6341
glennrp3faa9a32011-04-23 14:00:25 +00006342#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006343#define QM unsigned short
6344 if (magn_methx != 1 || magn_methy != 1)
6345 {
6346 /*
6347 Scale pixels to unsigned shorts to prevent
6348 overflow of intermediate values of interpolations
6349 */
cristybb503372010-05-27 20:51:26 +00006350 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006351 {
6352 q=GetAuthenticPixels(image,0,y,image->columns,1,
6353 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006354
cristybb503372010-05-27 20:51:26 +00006355 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006356 {
cristy16ea1392012-03-21 20:38:41 +00006357 SetPixelRed(image,ScaleQuantumToShort(
6358 GetPixelRed(image,q)),q);
6359 SetPixelGreen(image,ScaleQuantumToShort(
6360 GetPixelGreen(image,q)),q);
6361 SetPixelBlue(image,ScaleQuantumToShort(
6362 GetPixelBlue(image,q)),q);
6363 SetPixelAlpha(image,ScaleQuantumToShort(
6364 GetPixelAlpha(image,q)),q);
6365 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006366 }
glennrp47b9dd52010-11-24 18:12:06 +00006367
cristy3ed852e2009-09-05 21:47:34 +00006368 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6369 break;
6370 }
6371 }
6372#else
6373#define QM Quantum
6374#endif
6375
6376 if (image->matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006377 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006378
cristy3ed852e2009-09-05 21:47:34 +00006379 else
6380 {
cristy16ea1392012-03-21 20:38:41 +00006381 large_image->background_color.alpha=OpaqueAlpha;
6382 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006383
cristy3ed852e2009-09-05 21:47:34 +00006384 if (magn_methx == 4)
6385 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006386
cristy3ed852e2009-09-05 21:47:34 +00006387 if (magn_methx == 5)
6388 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006389
cristy3ed852e2009-09-05 21:47:34 +00006390 if (magn_methy == 4)
6391 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006392
cristy3ed852e2009-09-05 21:47:34 +00006393 if (magn_methy == 5)
6394 magn_methy=3;
6395 }
6396
6397 /* magnify the rows into the right side of the large image */
6398
6399 if (logging != MagickFalse)
6400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006401 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006402 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006403 yy=0;
cristy16ea1392012-03-21 20:38:41 +00006404 length=(size_t) image->columns*GetPixelChannels(image);
6405 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6406 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006407
cristy16ea1392012-03-21 20:38:41 +00006408 if ((prev == (Quantum *) NULL) ||
6409 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006410 {
6411 image=DestroyImageList(image);
6412 MngInfoFreeStruct(mng_info,&have_mng_structure);
6413 ThrowReaderException(ResourceLimitError,
6414 "MemoryAllocationFailed");
6415 }
glennrp47b9dd52010-11-24 18:12:06 +00006416
cristy3ed852e2009-09-05 21:47:34 +00006417 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6418 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006419
cristybb503372010-05-27 20:51:26 +00006420 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006421 {
6422 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006423 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006424
cristybb503372010-05-27 20:51:26 +00006425 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6426 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006427
cristybb503372010-05-27 20:51:26 +00006428 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6429 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006430
cristybb503372010-05-27 20:51:26 +00006431 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006432 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006433
cristy3ed852e2009-09-05 21:47:34 +00006434 else
cristybb503372010-05-27 20:51:26 +00006435 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006436
cristy3ed852e2009-09-05 21:47:34 +00006437 n=prev;
6438 prev=next;
6439 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006440
cristybb503372010-05-27 20:51:26 +00006441 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006442 {
6443 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6444 exception);
6445 (void) CopyMagickMemory(next,n,length);
6446 }
glennrp47b9dd52010-11-24 18:12:06 +00006447
cristy3ed852e2009-09-05 21:47:34 +00006448 for (i=0; i < m; i++, yy++)
6449 {
cristy16ea1392012-03-21 20:38:41 +00006450 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006451 *pixels;
6452
cristybb503372010-05-27 20:51:26 +00006453 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006454 pixels=prev;
6455 n=next;
6456 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006457 1,exception);
cristy16ea1392012-03-21 20:38:41 +00006458 q+=(large_image->columns-image->columns)*
6459 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006460
cristybb503372010-05-27 20:51:26 +00006461 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006462 {
glennrpfd05d622011-02-25 04:10:33 +00006463 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006464 /*
6465 if (image->storage_class == PseudoClass)
6466 {
6467 }
6468 */
6469
6470 if (magn_methy <= 1)
6471 {
glennrpbb4f99d2011-05-22 11:13:17 +00006472 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006473 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6474 SetPixelGreen(large_image,GetPixelGreen(image,
6475 pixels),q);
6476 SetPixelBlue(large_image,GetPixelBlue(image,
6477 pixels),q);
6478 SetPixelAlpha(large_image,GetPixelAlpha(image,
6479 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006480 }
glennrp47b9dd52010-11-24 18:12:06 +00006481
cristy3ed852e2009-09-05 21:47:34 +00006482 else if (magn_methy == 2 || magn_methy == 4)
6483 {
6484 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006485 {
cristy16ea1392012-03-21 20:38:41 +00006486 SetPixelRed(large_image,GetPixelRed(image,
6487 pixels),q);
6488 SetPixelGreen(large_image,GetPixelGreen(image,
6489 pixels),q);
6490 SetPixelBlue(large_image,GetPixelBlue(image,
6491 pixels),q);
6492 SetPixelAlpha(large_image,GetPixelAlpha(image,
6493 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006494 }
glennrp47b9dd52010-11-24 18:12:06 +00006495
cristy3ed852e2009-09-05 21:47:34 +00006496 else
6497 {
6498 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006499 SetPixelRed(large_image,((QM) (((ssize_t)
6500 (2*i*(GetPixelRed(image,n)
6501 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006502 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006503 +GetPixelRed(image,pixels)))),q);
6504 SetPixelGreen(large_image,((QM) (((ssize_t)
6505 (2*i*(GetPixelGreen(image,n)
6506 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006507 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006508 +GetPixelGreen(image,pixels)))),q);
6509 SetPixelBlue(large_image,((QM) (((ssize_t)
6510 (2*i*(GetPixelBlue(image,n)
6511 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006512 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006513 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006514
cristy3ed852e2009-09-05 21:47:34 +00006515 if (image->matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006516 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6517 (2*i*(GetPixelAlpha(image,n)
6518 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006519 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006520 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006521 }
glennrp47b9dd52010-11-24 18:12:06 +00006522
cristy3ed852e2009-09-05 21:47:34 +00006523 if (magn_methy == 4)
6524 {
6525 /* Replicate nearest */
6526 if (i <= ((m+1) << 1))
cristy16ea1392012-03-21 20:38:41 +00006527 SetPixelAlpha(large_image,GetPixelAlpha(image,
6528 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006529 else
cristy16ea1392012-03-21 20:38:41 +00006530 SetPixelAlpha(large_image,GetPixelAlpha(image,
6531 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006532 }
6533 }
glennrp47b9dd52010-11-24 18:12:06 +00006534
cristy3ed852e2009-09-05 21:47:34 +00006535 else /* if (magn_methy == 3 || magn_methy == 5) */
6536 {
6537 /* Replicate nearest */
6538 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006539 {
cristy16ea1392012-03-21 20:38:41 +00006540 SetPixelRed(large_image,GetPixelRed(image,
6541 pixels),q);
6542 SetPixelGreen(large_image,GetPixelGreen(image,
6543 pixels),q);
6544 SetPixelBlue(large_image,GetPixelBlue(image,
6545 pixels),q);
6546 SetPixelAlpha(large_image,GetPixelAlpha(image,
6547 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006548 }
glennrp47b9dd52010-11-24 18:12:06 +00006549
cristy3ed852e2009-09-05 21:47:34 +00006550 else
glennrpbb4f99d2011-05-22 11:13:17 +00006551 {
cristy16ea1392012-03-21 20:38:41 +00006552 SetPixelRed(large_image,GetPixelRed(image,n),q);
6553 SetPixelGreen(large_image,GetPixelGreen(image,n),
6554 q);
6555 SetPixelBlue(large_image,GetPixelBlue(image,n),
6556 q);
6557 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6558 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006559 }
glennrp47b9dd52010-11-24 18:12:06 +00006560
cristy3ed852e2009-09-05 21:47:34 +00006561 if (magn_methy == 5)
6562 {
cristy16ea1392012-03-21 20:38:41 +00006563 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6564 (GetPixelAlpha(image,n)
6565 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006566 +m))/((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006567 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006568 }
6569 }
cristy16ea1392012-03-21 20:38:41 +00006570 n+=GetPixelChannels(image);
6571 q+=GetPixelChannels(large_image);
6572 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006573 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006574
cristy3ed852e2009-09-05 21:47:34 +00006575 if (SyncAuthenticPixels(large_image,exception) == 0)
6576 break;
glennrp47b9dd52010-11-24 18:12:06 +00006577
cristy3ed852e2009-09-05 21:47:34 +00006578 } /* i */
6579 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006580
cristy16ea1392012-03-21 20:38:41 +00006581 prev=(Quantum *) RelinquishMagickMemory(prev);
6582 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006583
6584 length=image->columns;
6585
6586 if (logging != MagickFalse)
6587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6588 " Delete original image");
6589
6590 DeleteImageFromList(&image);
6591
6592 image=large_image;
6593
6594 mng_info->image=image;
6595
6596 /* magnify the columns */
6597 if (logging != MagickFalse)
6598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006599 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006600
cristybb503372010-05-27 20:51:26 +00006601 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006602 {
cristy16ea1392012-03-21 20:38:41 +00006603 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006604 *pixels;
6605
6606 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00006607 pixels=q+(image->columns-length)*GetPixelChannels(image);
6608 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006609
cristybb503372010-05-27 20:51:26 +00006610 for (x=(ssize_t) (image->columns-length);
6611 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006612 {
cristy16ea1392012-03-21 20:38:41 +00006613 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006614
cristybb503372010-05-27 20:51:26 +00006615 if (x == (ssize_t) (image->columns-length))
6616 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006617
cristybb503372010-05-27 20:51:26 +00006618 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6619 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006620
cristybb503372010-05-27 20:51:26 +00006621 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6622 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006623
cristybb503372010-05-27 20:51:26 +00006624 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006625 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006626
cristy3ed852e2009-09-05 21:47:34 +00006627 else
cristybb503372010-05-27 20:51:26 +00006628 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006629
cristy3ed852e2009-09-05 21:47:34 +00006630 for (i=0; i < m; i++)
6631 {
6632 if (magn_methx <= 1)
6633 {
6634 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006635 SetPixelRed(image,GetPixelRed(image,pixels),q);
6636 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6637 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6638 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006639 }
glennrp47b9dd52010-11-24 18:12:06 +00006640
cristy3ed852e2009-09-05 21:47:34 +00006641 else if (magn_methx == 2 || magn_methx == 4)
6642 {
6643 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006644 {
cristy16ea1392012-03-21 20:38:41 +00006645 SetPixelRed(image,GetPixelRed(image,pixels),q);
6646 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6647 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6648 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006649 }
glennrp47b9dd52010-11-24 18:12:06 +00006650
cristy16ea1392012-03-21 20:38:41 +00006651 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006652 else
6653 {
6654 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006655 SetPixelRed(image,(QM) ((2*i*(
6656 GetPixelRed(image,n)
6657 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006658 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006659 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006660
cristy16ea1392012-03-21 20:38:41 +00006661 SetPixelGreen(image,(QM) ((2*i*(
6662 GetPixelGreen(image,n)
6663 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006664 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006665 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006666
cristy16ea1392012-03-21 20:38:41 +00006667 SetPixelBlue(image,(QM) ((2*i*(
6668 GetPixelBlue(image,n)
6669 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006670 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006671 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006672 if (image->matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006673 SetPixelAlpha(image,(QM) ((2*i*(
6674 GetPixelAlpha(image,n)
6675 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006676 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006677 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006678 }
glennrp47b9dd52010-11-24 18:12:06 +00006679
cristy3ed852e2009-09-05 21:47:34 +00006680 if (magn_methx == 4)
6681 {
6682 /* Replicate nearest */
6683 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006684 {
cristy16ea1392012-03-21 20:38:41 +00006685 SetPixelAlpha(image,
6686 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006687 }
cristy3ed852e2009-09-05 21:47:34 +00006688 else
glennrpbb4f99d2011-05-22 11:13:17 +00006689 {
cristy16ea1392012-03-21 20:38:41 +00006690 SetPixelAlpha(image,
6691 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006692 }
cristy3ed852e2009-09-05 21:47:34 +00006693 }
6694 }
glennrp47b9dd52010-11-24 18:12:06 +00006695
cristy3ed852e2009-09-05 21:47:34 +00006696 else /* if (magn_methx == 3 || magn_methx == 5) */
6697 {
6698 /* Replicate nearest */
6699 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006700 {
cristy16ea1392012-03-21 20:38:41 +00006701 SetPixelRed(image,GetPixelRed(image,pixels),q);
6702 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6703 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6704 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006705 }
glennrp47b9dd52010-11-24 18:12:06 +00006706
cristy3ed852e2009-09-05 21:47:34 +00006707 else
glennrpbb4f99d2011-05-22 11:13:17 +00006708 {
cristy16ea1392012-03-21 20:38:41 +00006709 SetPixelRed(image,GetPixelRed(image,n),q);
6710 SetPixelGreen(image,GetPixelGreen(image,n),q);
6711 SetPixelBlue(image,GetPixelBlue(image,n),q);
6712 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006713 }
glennrp47b9dd52010-11-24 18:12:06 +00006714
cristy3ed852e2009-09-05 21:47:34 +00006715 if (magn_methx == 5)
6716 {
6717 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006718 SetPixelAlpha(image,
6719 (QM) ((2*i*( GetPixelAlpha(image,n)
6720 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006721 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006722 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006723 }
6724 }
cristy16ea1392012-03-21 20:38:41 +00006725 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006726 }
cristy16ea1392012-03-21 20:38:41 +00006727 n+=GetPixelChannels(image);
6728 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006729 }
glennrp47b9dd52010-11-24 18:12:06 +00006730
cristy3ed852e2009-09-05 21:47:34 +00006731 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6732 break;
6733 }
glennrp3faa9a32011-04-23 14:00:25 +00006734#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006735 if (magn_methx != 1 || magn_methy != 1)
6736 {
6737 /*
6738 Rescale pixels to Quantum
6739 */
cristybb503372010-05-27 20:51:26 +00006740 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006741 {
6742 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006743
cristybb503372010-05-27 20:51:26 +00006744 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006745 {
cristy16ea1392012-03-21 20:38:41 +00006746 SetPixelRed(image,ScaleShortToQuantum(
6747 GetPixelRed(image,q)),q);
6748 SetPixelGreen(image,ScaleShortToQuantum(
6749 GetPixelGreen(image,q)),q);
6750 SetPixelBlue(image,ScaleShortToQuantum(
6751 GetPixelBlue(image,q)),q);
6752 SetPixelAlpha(image,ScaleShortToQuantum(
6753 GetPixelAlpha(image,q)),q);
6754 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006755 }
glennrp47b9dd52010-11-24 18:12:06 +00006756
cristy3ed852e2009-09-05 21:47:34 +00006757 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6758 break;
6759 }
6760 }
6761#endif
6762 if (logging != MagickFalse)
6763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6764 " Finished MAGN processing");
6765 }
6766 }
6767
6768 /*
6769 Crop_box is with respect to the upper left corner of the MNG.
6770 */
6771 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6772 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6773 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6774 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6775 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6776 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6777 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6778 if ((crop_box.left != (mng_info->image_box.left
6779 +mng_info->x_off[object_id])) ||
6780 (crop_box.right != (mng_info->image_box.right
6781 +mng_info->x_off[object_id])) ||
6782 (crop_box.top != (mng_info->image_box.top
6783 +mng_info->y_off[object_id])) ||
6784 (crop_box.bottom != (mng_info->image_box.bottom
6785 +mng_info->y_off[object_id])))
6786 {
6787 if (logging != MagickFalse)
6788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6789 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006790
cristy3ed852e2009-09-05 21:47:34 +00006791 if ((crop_box.left < crop_box.right) &&
6792 (crop_box.top < crop_box.bottom))
6793 {
6794 Image
6795 *im;
6796
6797 RectangleInfo
6798 crop_info;
6799
6800 /*
6801 Crop_info is with respect to the upper left corner of
6802 the image.
6803 */
6804 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6805 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006806 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6807 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006808 image->page.width=image->columns;
6809 image->page.height=image->rows;
6810 image->page.x=0;
6811 image->page.y=0;
6812 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006813
cristy3ed852e2009-09-05 21:47:34 +00006814 if (im != (Image *) NULL)
6815 {
6816 image->columns=im->columns;
6817 image->rows=im->rows;
6818 im=DestroyImage(im);
6819 image->page.width=image->columns;
6820 image->page.height=image->rows;
6821 image->page.x=crop_box.left;
6822 image->page.y=crop_box.top;
6823 }
6824 }
glennrp47b9dd52010-11-24 18:12:06 +00006825
cristy3ed852e2009-09-05 21:47:34 +00006826 else
6827 {
6828 /*
6829 No pixels in crop area. The MNG spec still requires
6830 a layer, though, so make a single transparent pixel in
6831 the top left corner.
6832 */
6833 image->columns=1;
6834 image->rows=1;
6835 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00006836 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006837 image->page.width=1;
6838 image->page.height=1;
6839 image->page.x=0;
6840 image->page.y=0;
6841 }
6842 }
6843#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6844 image=mng_info->image;
6845#endif
6846 }
6847
glennrp2b013e42010-11-24 16:55:50 +00006848#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6849 /* PNG does not handle depths greater than 16 so reduce it even
cristy16ea1392012-03-21 20:38:41 +00006850 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00006851 */
6852 if (image->depth > 16)
6853 image->depth=16;
6854#endif
6855
glennrp3faa9a32011-04-23 14:00:25 +00006856#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00006857 if (image->depth > 8)
6858 {
6859 /* To do: fill low byte properly */
6860 image->depth=16;
6861 }
6862
cristy16ea1392012-03-21 20:38:41 +00006863 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006864 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006865#endif
glennrpd6afd542010-11-19 01:53:05 +00006866
cristy3ed852e2009-09-05 21:47:34 +00006867 if (image_info->number_scenes != 0)
6868 {
6869 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006870 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006871 break;
6872 }
glennrpd6afd542010-11-19 01:53:05 +00006873
cristy3ed852e2009-09-05 21:47:34 +00006874 if (logging != MagickFalse)
6875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6876 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006877
cristy3ed852e2009-09-05 21:47:34 +00006878 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006879
cristy3ed852e2009-09-05 21:47:34 +00006880 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006881
cristy3ed852e2009-09-05 21:47:34 +00006882 if (logging != MagickFalse)
6883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6884 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006885
cristy3ed852e2009-09-05 21:47:34 +00006886#if defined(MNG_INSERT_LAYERS)
6887 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6888 (mng_info->mng_height))
6889 {
6890 /*
6891 Insert a background layer if nothing else was found.
6892 */
6893 if (logging != MagickFalse)
6894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6895 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006896
cristy16ea1392012-03-21 20:38:41 +00006897 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006898 {
6899 /*
6900 Allocate next image structure.
6901 */
cristy16ea1392012-03-21 20:38:41 +00006902 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006903 if (GetNextImageInList(image) == (Image *) NULL)
6904 {
6905 image=DestroyImageList(image);
6906 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006907
cristy3ed852e2009-09-05 21:47:34 +00006908 if (logging != MagickFalse)
6909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6910 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006911
cristy3ed852e2009-09-05 21:47:34 +00006912 return((Image *) NULL);
6913 }
6914 image=SyncNextImageInList(image);
6915 }
6916 image->columns=mng_info->mng_width;
6917 image->rows=mng_info->mng_height;
6918 image->page.width=mng_info->mng_width;
6919 image->page.height=mng_info->mng_height;
6920 image->page.x=0;
6921 image->page.y=0;
6922 image->background_color=mng_background_color;
6923 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006924
cristy3ed852e2009-09-05 21:47:34 +00006925 if (image_info->ping == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006926 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006927
cristy3ed852e2009-09-05 21:47:34 +00006928 mng_info->image_found++;
6929 }
6930#endif
6931 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006932
cristy3ed852e2009-09-05 21:47:34 +00006933 if (mng_iterations == 1)
6934 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006935
cristy3ed852e2009-09-05 21:47:34 +00006936 while (GetPreviousImageInList(image) != (Image *) NULL)
6937 {
6938 image_count++;
6939 if (image_count > 10*mng_info->image_found)
6940 {
6941 if (logging != MagickFalse)
6942 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006943
cristy16ea1392012-03-21 20:38:41 +00006944 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006945 CoderError,"Linked list is corrupted, beginning of list not found",
6946 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006947
cristy3ed852e2009-09-05 21:47:34 +00006948 return((Image *) NULL);
6949 }
glennrp0fe50b42010-11-16 03:52:51 +00006950
cristy3ed852e2009-09-05 21:47:34 +00006951 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006952
cristy3ed852e2009-09-05 21:47:34 +00006953 if (GetNextImageInList(image) == (Image *) NULL)
6954 {
6955 if (logging != MagickFalse)
6956 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006957
cristy16ea1392012-03-21 20:38:41 +00006958 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006959 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6960 image_info->filename);
6961 }
6962 }
glennrp47b9dd52010-11-24 18:12:06 +00006963
cristy3ed852e2009-09-05 21:47:34 +00006964 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6965 GetNextImageInList(image) ==
6966 (Image *) NULL)
6967 {
6968 if (logging != MagickFalse)
6969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6970 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006971
cristy16ea1392012-03-21 20:38:41 +00006972 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006973 CoderError,"image->next for first image is NULL but shouldn't be.",
6974 "`%s'",image_info->filename);
6975 }
glennrp47b9dd52010-11-24 18:12:06 +00006976
cristy3ed852e2009-09-05 21:47:34 +00006977 if (mng_info->image_found == 0)
6978 {
6979 if (logging != MagickFalse)
6980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6981 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006982
cristy16ea1392012-03-21 20:38:41 +00006983 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006984 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006985
cristy3ed852e2009-09-05 21:47:34 +00006986 if (image != (Image *) NULL)
6987 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006988
cristy3ed852e2009-09-05 21:47:34 +00006989 MngInfoFreeStruct(mng_info,&have_mng_structure);
6990 return((Image *) NULL);
6991 }
6992
6993 if (mng_info->ticks_per_second)
6994 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6995 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006996
cristy3ed852e2009-09-05 21:47:34 +00006997 else
6998 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006999
cristy3ed852e2009-09-05 21:47:34 +00007000 /* Find final nonzero image delay */
7001 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00007002
cristy3ed852e2009-09-05 21:47:34 +00007003 while (GetNextImageInList(image) != (Image *) NULL)
7004 {
7005 if (image->delay)
7006 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00007007
cristy3ed852e2009-09-05 21:47:34 +00007008 image=GetNextImageInList(image);
7009 }
glennrp0fe50b42010-11-16 03:52:51 +00007010
cristy3ed852e2009-09-05 21:47:34 +00007011 if (final_delay < final_image_delay)
7012 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00007013
cristy3ed852e2009-09-05 21:47:34 +00007014 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00007015
cristy3ed852e2009-09-05 21:47:34 +00007016 if (logging != MagickFalse)
7017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007018 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7019 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00007020
cristy3ed852e2009-09-05 21:47:34 +00007021 if (logging != MagickFalse)
7022 {
7023 int
7024 scene;
7025
7026 scene=0;
7027 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007028
cristy3ed852e2009-09-05 21:47:34 +00007029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7030 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007031
cristy3ed852e2009-09-05 21:47:34 +00007032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007033 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00007034
cristy3ed852e2009-09-05 21:47:34 +00007035 while (GetNextImageInList(image) != (Image *) NULL)
7036 {
7037 image=GetNextImageInList(image);
7038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007039 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00007040 }
7041 }
7042
7043 image=GetFirstImageInList(image);
7044#ifdef MNG_COALESCE_LAYERS
7045 if (insert_layers)
7046 {
7047 Image
7048 *next_image,
7049 *next;
7050
cristybb503372010-05-27 20:51:26 +00007051 size_t
cristy3ed852e2009-09-05 21:47:34 +00007052 scene;
7053
7054 if (logging != MagickFalse)
7055 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00007056
cristy3ed852e2009-09-05 21:47:34 +00007057 scene=image->scene;
cristy16ea1392012-03-21 20:38:41 +00007058 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00007059
cristy3ed852e2009-09-05 21:47:34 +00007060 if (next_image == (Image *) NULL)
7061 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00007062
cristy3ed852e2009-09-05 21:47:34 +00007063 image=DestroyImageList(image);
7064 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00007065
cristy3ed852e2009-09-05 21:47:34 +00007066 for (next=image; next != (Image *) NULL; next=next_image)
7067 {
7068 next->page.width=mng_info->mng_width;
7069 next->page.height=mng_info->mng_height;
7070 next->page.x=0;
7071 next->page.y=0;
7072 next->scene=scene++;
7073 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00007074
cristy3ed852e2009-09-05 21:47:34 +00007075 if (next_image == (Image *) NULL)
7076 break;
glennrp47b9dd52010-11-24 18:12:06 +00007077
cristy3ed852e2009-09-05 21:47:34 +00007078 if (next->delay == 0)
7079 {
7080 scene--;
7081 next_image->previous=GetPreviousImageInList(next);
7082 if (GetPreviousImageInList(next) == (Image *) NULL)
7083 image=next_image;
7084 else
7085 next->previous->next=next_image;
7086 next=DestroyImage(next);
7087 }
7088 }
7089 }
7090#endif
7091
7092 while (GetNextImageInList(image) != (Image *) NULL)
7093 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007094
cristy3ed852e2009-09-05 21:47:34 +00007095 image->dispose=BackgroundDispose;
7096
7097 if (logging != MagickFalse)
7098 {
7099 int
7100 scene;
7101
7102 scene=0;
7103 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007104
cristy3ed852e2009-09-05 21:47:34 +00007105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7106 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00007107
cristy3ed852e2009-09-05 21:47:34 +00007108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007109 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7110 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00007111
cristy3ed852e2009-09-05 21:47:34 +00007112 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00007113 {
7114 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00007115
cristyf2faecf2010-05-28 19:19:36 +00007116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00007117 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7118 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00007119 }
7120 }
glennrp47b9dd52010-11-24 18:12:06 +00007121
cristy3ed852e2009-09-05 21:47:34 +00007122 image=GetFirstImageInList(image);
7123 MngInfoFreeStruct(mng_info,&have_mng_structure);
7124 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00007125
cristy3ed852e2009-09-05 21:47:34 +00007126 if (logging != MagickFalse)
7127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00007128
cristy3ed852e2009-09-05 21:47:34 +00007129 return(GetFirstImageInList(image));
7130}
glennrp25c1e2b2010-03-25 01:39:56 +00007131#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007132static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7133{
7134 printf("Your PNG library is too old: You have libpng-%s\n",
7135 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007136
cristy3ed852e2009-09-05 21:47:34 +00007137 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7138 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007139
cristy3ed852e2009-09-05 21:47:34 +00007140 return(Image *) NULL;
7141}
glennrp47b9dd52010-11-24 18:12:06 +00007142
cristy3ed852e2009-09-05 21:47:34 +00007143static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7144{
7145 return(ReadPNGImage(image_info,exception));
7146}
glennrp25c1e2b2010-03-25 01:39:56 +00007147#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007148#endif
7149
7150/*
7151%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7152% %
7153% %
7154% %
7155% R e g i s t e r P N G I m a g e %
7156% %
7157% %
7158% %
7159%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7160%
7161% RegisterPNGImage() adds properties for the PNG image format to
7162% the list of supported formats. The properties include the image format
7163% tag, a method to read and/or write the format, whether the format
7164% supports the saving of more than one frame to the same file or blob,
7165% whether the format supports native in-memory I/O, and a brief
7166% description of the format.
7167%
7168% The format of the RegisterPNGImage method is:
7169%
cristybb503372010-05-27 20:51:26 +00007170% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007171%
7172*/
cristybb503372010-05-27 20:51:26 +00007173ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007174{
7175 char
7176 version[MaxTextExtent];
7177
7178 MagickInfo
7179 *entry;
7180
7181 static const char
7182 *PNGNote=
7183 {
7184 "See http://www.libpng.org/ for details about the PNG format."
7185 },
glennrp47b9dd52010-11-24 18:12:06 +00007186
cristy3ed852e2009-09-05 21:47:34 +00007187 *JNGNote=
7188 {
7189 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7190 "format."
7191 },
glennrp47b9dd52010-11-24 18:12:06 +00007192
cristy3ed852e2009-09-05 21:47:34 +00007193 *MNGNote=
7194 {
7195 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7196 "format."
7197 };
7198
7199 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007200
cristy3ed852e2009-09-05 21:47:34 +00007201#if defined(PNG_LIBPNG_VER_STRING)
7202 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7203 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007204
cristy3ed852e2009-09-05 21:47:34 +00007205 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7206 {
7207 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7208 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7209 MaxTextExtent);
7210 }
7211#endif
glennrp47b9dd52010-11-24 18:12:06 +00007212
cristy3ed852e2009-09-05 21:47:34 +00007213 entry=SetMagickInfo("MNG");
7214 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007215
cristy3ed852e2009-09-05 21:47:34 +00007216#if defined(MAGICKCORE_PNG_DELEGATE)
7217 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7218 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7219#endif
glennrp47b9dd52010-11-24 18:12:06 +00007220
cristy3ed852e2009-09-05 21:47:34 +00007221 entry->magick=(IsImageFormatHandler *) IsMNG;
7222 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007223
cristy3ed852e2009-09-05 21:47:34 +00007224 if (*version != '\0')
7225 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007226
cristy3ed852e2009-09-05 21:47:34 +00007227 entry->module=ConstantString("PNG");
7228 entry->note=ConstantString(MNGNote);
7229 (void) RegisterMagickInfo(entry);
7230
7231 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007232
cristy3ed852e2009-09-05 21:47:34 +00007233#if defined(MAGICKCORE_PNG_DELEGATE)
7234 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7235 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7236#endif
glennrp47b9dd52010-11-24 18:12:06 +00007237
cristy3ed852e2009-09-05 21:47:34 +00007238 entry->magick=(IsImageFormatHandler *) IsPNG;
7239 entry->adjoin=MagickFalse;
7240 entry->description=ConstantString("Portable Network Graphics");
7241 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007242
cristy3ed852e2009-09-05 21:47:34 +00007243 if (*version != '\0')
7244 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007245
cristy3ed852e2009-09-05 21:47:34 +00007246 entry->note=ConstantString(PNGNote);
7247 (void) RegisterMagickInfo(entry);
7248
7249 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007250
cristy3ed852e2009-09-05 21:47:34 +00007251#if defined(MAGICKCORE_PNG_DELEGATE)
7252 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7253 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7254#endif
glennrp47b9dd52010-11-24 18:12:06 +00007255
cristy3ed852e2009-09-05 21:47:34 +00007256 entry->magick=(IsImageFormatHandler *) IsPNG;
7257 entry->adjoin=MagickFalse;
7258 entry->description=ConstantString(
7259 "8-bit indexed with optional binary transparency");
7260 entry->module=ConstantString("PNG");
7261 (void) RegisterMagickInfo(entry);
7262
7263 entry=SetMagickInfo("PNG24");
7264 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007265
cristy3ed852e2009-09-05 21:47:34 +00007266#if defined(ZLIB_VERSION)
7267 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7268 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007269
cristy3ed852e2009-09-05 21:47:34 +00007270 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7271 {
7272 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7273 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7274 }
7275#endif
glennrp47b9dd52010-11-24 18:12:06 +00007276
cristy3ed852e2009-09-05 21:47:34 +00007277 if (*version != '\0')
7278 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007279
cristy3ed852e2009-09-05 21:47:34 +00007280#if defined(MAGICKCORE_PNG_DELEGATE)
7281 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7282 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7283#endif
glennrp47b9dd52010-11-24 18:12:06 +00007284
cristy3ed852e2009-09-05 21:47:34 +00007285 entry->magick=(IsImageFormatHandler *) IsPNG;
7286 entry->adjoin=MagickFalse;
7287 entry->description=ConstantString("opaque 24-bit RGB");
7288 entry->module=ConstantString("PNG");
7289 (void) RegisterMagickInfo(entry);
7290
7291 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007292
cristy3ed852e2009-09-05 21:47:34 +00007293#if defined(MAGICKCORE_PNG_DELEGATE)
7294 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7295 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7296#endif
glennrp47b9dd52010-11-24 18:12:06 +00007297
cristy3ed852e2009-09-05 21:47:34 +00007298 entry->magick=(IsImageFormatHandler *) IsPNG;
7299 entry->adjoin=MagickFalse;
7300 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7301 entry->module=ConstantString("PNG");
7302 (void) RegisterMagickInfo(entry);
7303
7304 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007305
cristy3ed852e2009-09-05 21:47:34 +00007306#if defined(JNG_SUPPORTED)
7307#if defined(MAGICKCORE_PNG_DELEGATE)
7308 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7309 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7310#endif
7311#endif
glennrp47b9dd52010-11-24 18:12:06 +00007312
cristy3ed852e2009-09-05 21:47:34 +00007313 entry->magick=(IsImageFormatHandler *) IsJNG;
7314 entry->adjoin=MagickFalse;
7315 entry->description=ConstantString("JPEG Network Graphics");
7316 entry->module=ConstantString("PNG");
7317 entry->note=ConstantString(JNGNote);
7318 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007319
glennrpedaa0382012-04-12 14:16:21 +00007320#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007321 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007322#endif
glennrp47b9dd52010-11-24 18:12:06 +00007323
cristy3ed852e2009-09-05 21:47:34 +00007324 return(MagickImageCoderSignature);
7325}
7326
7327/*
7328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7329% %
7330% %
7331% %
7332% U n r e g i s t e r P N G I m a g e %
7333% %
7334% %
7335% %
7336%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7337%
7338% UnregisterPNGImage() removes format registrations made by the
7339% PNG module from the list of supported formats.
7340%
7341% The format of the UnregisterPNGImage method is:
7342%
7343% UnregisterPNGImage(void)
7344%
7345*/
7346ModuleExport void UnregisterPNGImage(void)
7347{
7348 (void) UnregisterMagickInfo("MNG");
7349 (void) UnregisterMagickInfo("PNG");
7350 (void) UnregisterMagickInfo("PNG8");
7351 (void) UnregisterMagickInfo("PNG24");
7352 (void) UnregisterMagickInfo("PNG32");
7353 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007354
glennrpedaa0382012-04-12 14:16:21 +00007355#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007356 if (ping_semaphore != (SemaphoreInfo *) NULL)
7357 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007358#endif
7359}
7360
7361#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007362#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007363/*
7364%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7365% %
7366% %
7367% %
7368% W r i t e M N G I m a g e %
7369% %
7370% %
7371% %
7372%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7373%
7374% WriteMNGImage() writes an image in the Portable Network Graphics
7375% Group's "Multiple-image Network Graphics" encoded image format.
7376%
7377% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7378%
7379% The format of the WriteMNGImage method is:
7380%
cristy16ea1392012-03-21 20:38:41 +00007381% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7382% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007383%
7384% A description of each parameter follows.
7385%
7386% o image_info: the image info.
7387%
7388% o image: The image.
7389%
cristy16ea1392012-03-21 20:38:41 +00007390% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007391%
7392% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7393% "To do" under ReadPNGImage):
7394%
cristy3ed852e2009-09-05 21:47:34 +00007395% Preserve all unknown and not-yet-handled known chunks found in input
7396% PNG file and copy them into output PNG files according to the PNG
7397% copying rules.
7398%
7399% Write the iCCP chunk at MNG level when (icc profile length > 0)
7400%
7401% Improve selection of color type (use indexed-colour or indexed-colour
7402% with tRNS when 256 or fewer unique RGBA values are present).
7403%
7404% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7405% This will be complicated if we limit ourselves to generating MNG-LC
7406% files. For now we ignore disposal method 3 and simply overlay the next
7407% image on it.
7408%
7409% Check for identical PLTE's or PLTE/tRNS combinations and use a
7410% global MNG PLTE or PLTE/tRNS combination when appropriate.
7411% [mostly done 15 June 1999 but still need to take care of tRNS]
7412%
7413% Check for identical sRGB and replace with a global sRGB (and remove
7414% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7415% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7416% local gAMA/cHRM with local sRGB if appropriate).
7417%
7418% Check for identical sBIT chunks and write global ones.
7419%
7420% Provide option to skip writing the signature tEXt chunks.
7421%
7422% Use signatures to detect identical objects and reuse the first
7423% instance of such objects instead of writing duplicate objects.
7424%
7425% Use a smaller-than-32k value of compression window size when
7426% appropriate.
7427%
7428% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7429% ancillary text chunks and save profiles.
7430%
7431% Provide an option to force LC files (to ensure exact framing rate)
7432% instead of VLC.
7433%
7434% Provide an option to force VLC files instead of LC, even when offsets
7435% are present. This will involve expanding the embedded images with a
7436% transparent region at the top and/or left.
7437*/
7438
cristy3ed852e2009-09-05 21:47:34 +00007439static void
glennrpcf002022011-01-30 02:38:15 +00007440Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007441 png_info *ping_info, unsigned char *profile_type, unsigned char
7442 *profile_description, unsigned char *profile_data, png_uint_32 length)
7443{
cristy3ed852e2009-09-05 21:47:34 +00007444 png_textp
7445 text;
7446
cristybb503372010-05-27 20:51:26 +00007447 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007448 i;
7449
7450 unsigned char
7451 *sp;
7452
7453 png_charp
7454 dp;
7455
7456 png_uint_32
7457 allocated_length,
7458 description_length;
7459
7460 unsigned char
7461 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007462
cristy3ed852e2009-09-05 21:47:34 +00007463 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7464 return;
7465
7466 if (image_info->verbose)
7467 {
glennrp0fe50b42010-11-16 03:52:51 +00007468 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7469 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007470 }
glennrp0fe50b42010-11-16 03:52:51 +00007471
cristya865ccd2012-07-28 00:33:10 +00007472#if PNG_LIBPNG_VER >= 14000
7473 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7474#else
7475 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7476#endif
cristy3ed852e2009-09-05 21:47:34 +00007477 description_length=(png_uint_32) strlen((const char *) profile_description);
7478 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7479 + description_length);
cristya865ccd2012-07-28 00:33:10 +00007480#if PNG_LIBPNG_VER >= 14000
7481 text[0].text=(png_charp) png_malloc(ping,
7482 (png_alloc_size_t) allocated_length);
7483 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7484#else
7485 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7486 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7487#endif
cristy3ed852e2009-09-05 21:47:34 +00007488 text[0].key[0]='\0';
7489 (void) ConcatenateMagickString(text[0].key,
7490 "Raw profile type ",MaxTextExtent);
7491 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7492 sp=profile_data;
7493 dp=text[0].text;
7494 *dp++='\n';
7495 (void) CopyMagickString(dp,(const char *) profile_description,
7496 allocated_length);
7497 dp+=description_length;
7498 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007499 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007500 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007501 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007502
cristybb503372010-05-27 20:51:26 +00007503 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007504 {
7505 if (i%36 == 0)
7506 *dp++='\n';
7507 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7508 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7509 }
glennrp47b9dd52010-11-24 18:12:06 +00007510
cristy3ed852e2009-09-05 21:47:34 +00007511 *dp++='\n';
7512 *dp='\0';
7513 text[0].text_length=(png_size_t) (dp-text[0].text);
7514 text[0].compression=image_info->compression == NoCompression ||
7515 (image_info->compression == UndefinedCompression &&
7516 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007517
cristy3ed852e2009-09-05 21:47:34 +00007518 if (text[0].text_length <= allocated_length)
7519 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007520
cristy3ed852e2009-09-05 21:47:34 +00007521 png_free(ping,text[0].text);
7522 png_free(ping,text[0].key);
7523 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007524}
7525
glennrpcf002022011-01-30 02:38:15 +00007526static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007527 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007528{
7529 char
7530 *name;
7531
7532 const StringInfo
7533 *profile;
7534
7535 unsigned char
7536 *data;
7537
7538 png_uint_32 length;
7539
7540 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007541
7542 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7543 {
cristy3ed852e2009-09-05 21:47:34 +00007544 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007545
cristy3ed852e2009-09-05 21:47:34 +00007546 if (profile != (const StringInfo *) NULL)
7547 {
7548 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007549 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007550
glennrp47b9dd52010-11-24 18:12:06 +00007551 if (LocaleNCompare(name,string,11) == 0)
7552 {
7553 if (logging != MagickFalse)
7554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7555 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007556
glennrpcf002022011-01-30 02:38:15 +00007557 ping_profile=CloneStringInfo(profile);
7558 data=GetStringInfoDatum(ping_profile),
7559 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007560 data[4]=data[3];
7561 data[3]=data[2];
7562 data[2]=data[1];
7563 data[1]=data[0];
7564 (void) WriteBlobMSBULong(image,length-5); /* data length */
7565 (void) WriteBlob(image,length-1,data+1);
7566 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007567 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007568 }
cristy3ed852e2009-09-05 21:47:34 +00007569 }
glennrp47b9dd52010-11-24 18:12:06 +00007570
cristy3ed852e2009-09-05 21:47:34 +00007571 name=GetNextImageProfile(image);
7572 }
glennrp47b9dd52010-11-24 18:12:06 +00007573
cristy3ed852e2009-09-05 21:47:34 +00007574 return(MagickTrue);
7575}
7576
glennrpb9cfe272010-12-21 15:08:06 +00007577
cristy3ed852e2009-09-05 21:47:34 +00007578/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007579static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +00007580 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007581{
cristy16ea1392012-03-21 20:38:41 +00007582 Image
7583 *image;
7584
7585 ImageInfo
7586 *image_info;
7587
cristy3ed852e2009-09-05 21:47:34 +00007588 char
7589 s[2];
7590
7591 const char
7592 *name,
7593 *property,
7594 *value;
7595
7596 const StringInfo
7597 *profile;
7598
cristy3ed852e2009-09-05 21:47:34 +00007599 int
cristy3ed852e2009-09-05 21:47:34 +00007600 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007601 pass;
7602
glennrpe9c26dc2010-05-30 01:56:35 +00007603 png_byte
7604 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007605
glennrp39992b42010-11-14 00:03:43 +00007606 png_color
7607 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007608
glennrp5af765f2010-03-30 11:12:18 +00007609 png_color_16
7610 ping_background,
7611 ping_trans_color;
7612
cristy3ed852e2009-09-05 21:47:34 +00007613 png_info
7614 *ping_info;
7615
7616 png_struct
7617 *ping;
7618
glennrp5af765f2010-03-30 11:12:18 +00007619 png_uint_32
7620 ping_height,
7621 ping_width;
7622
cristybb503372010-05-27 20:51:26 +00007623 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007624 y;
7625
7626 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007627 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007628 logging,
glennrp58e01762011-01-07 15:28:54 +00007629 matte,
7630
glennrpda8f3a72011-02-27 23:54:12 +00007631 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007632 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007633 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007634 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007635 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007636 ping_have_bKGD,
7637 ping_have_pHYs,
7638 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007639
7640 ping_exclude_bKGD,
7641 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007642 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007643 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007644 ping_exclude_gAMA,
7645 ping_exclude_iCCP,
7646 /* ping_exclude_iTXt, */
7647 ping_exclude_oFFs,
7648 ping_exclude_pHYs,
7649 ping_exclude_sRGB,
7650 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007651 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007652 ping_exclude_vpAg,
7653 ping_exclude_zCCP, /* hex-encoded iCCP */
7654 ping_exclude_zTXt,
7655
glennrp8d3d6e52011-04-19 04:39:51 +00007656 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007657 ping_need_colortype_warning,
7658
glennrp82b3c532011-03-22 19:20:54 +00007659 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007660 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007661 tried_333,
7662 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007663
7664 QuantumInfo
7665 *quantum_info;
7666
cristy16ea1392012-03-21 20:38:41 +00007667 PNGErrorInfo
7668 error_info;
7669
cristybb503372010-05-27 20:51:26 +00007670 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007671 i,
7672 x;
7673
7674 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007675 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007676
glennrp5af765f2010-03-30 11:12:18 +00007677 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007678 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007679 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007680 ping_color_type,
7681 ping_interlace_method,
7682 ping_compression_method,
7683 ping_filter_method,
7684 ping_num_trans;
7685
cristybb503372010-05-27 20:51:26 +00007686 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007687 image_depth,
7688 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007689
cristybb503372010-05-27 20:51:26 +00007690 size_t
cristy3ed852e2009-09-05 21:47:34 +00007691 quality,
7692 rowbytes,
7693 save_image_depth;
7694
glennrpdfd70802010-11-14 01:23:35 +00007695 int
glennrpfd05d622011-02-25 04:10:33 +00007696 j,
glennrpf09bded2011-01-08 01:15:59 +00007697 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007698 number_opaque,
7699 number_semitransparent,
7700 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007701 ping_pHYs_unit_type;
7702
7703 png_uint_32
7704 ping_pHYs_x_resolution,
7705 ping_pHYs_y_resolution;
7706
cristy3ed852e2009-09-05 21:47:34 +00007707 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007708 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007709
cristy16ea1392012-03-21 20:38:41 +00007710 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7711 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7712 if (image_info == (ImageInfo *) NULL)
7713 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007714
glennrp5af765f2010-03-30 11:12:18 +00007715 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007716 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007717 ping_color_type=0,
7718 ping_interlace_method=0,
7719 ping_compression_method=0,
7720 ping_filter_method=0,
7721 ping_num_trans = 0;
7722
7723 ping_background.red = 0;
7724 ping_background.green = 0;
7725 ping_background.blue = 0;
7726 ping_background.gray = 0;
7727 ping_background.index = 0;
7728
7729 ping_trans_color.red=0;
7730 ping_trans_color.green=0;
7731 ping_trans_color.blue=0;
7732 ping_trans_color.gray=0;
7733
glennrpdfd70802010-11-14 01:23:35 +00007734 ping_pHYs_unit_type = 0;
7735 ping_pHYs_x_resolution = 0;
7736 ping_pHYs_y_resolution = 0;
7737
glennrpda8f3a72011-02-27 23:54:12 +00007738 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007739 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007740 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007741 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007742 ping_have_bKGD=MagickFalse;
7743 ping_have_pHYs=MagickFalse;
7744 ping_have_tRNS=MagickFalse;
7745
glennrp0e8ea192010-12-24 18:00:33 +00007746 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7747 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007748 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007749 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007750 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007751 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7752 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7753 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7754 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7755 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7756 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007757 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007758 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7759 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7760 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7761
glennrp8d3d6e52011-04-19 04:39:51 +00007762 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007763 ping_need_colortype_warning = MagickFalse;
7764
cristy0d57eec2011-09-04 22:13:56 +00007765 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7766 * i.e., eliminate the ICC profile and set image->rendering_intent.
7767 * Note that this will not involve any changes to the actual pixels
7768 * but merely passes information to applications that read the resulting
7769 * PNG image.
7770 */
7771 if (ping_exclude_sRGB == MagickFalse)
7772 {
7773 char
7774 *name;
7775
7776 const StringInfo
7777 *profile;
7778
7779 ResetImageProfileIterator(image);
7780 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7781 {
7782 profile=GetImageProfile(image,name);
7783
7784 if (profile != (StringInfo *) NULL)
7785 {
7786 if ((LocaleCompare(name,"ICC") == 0) ||
cristy16ea1392012-03-21 20:38:41 +00007787 (LocaleCompare(name,"ICM") == 0))
7788 {
glennrpee7b4c02011-10-04 01:21:09 +00007789 int
7790 icheck;
7791
7792 /* 0: not a known sRGB profile
7793 * 1: HP-Microsoft sRGB v2
7794 * 2: ICC sRGB v4 perceptual
7795 * 3: ICC sRGB v2 perceptual no black-compensation
7796 */
7797 png_uint_32
7798 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7799 check_len[4] = {0, 3144, 60960, 3052};
7800
7801 png_uint_32
7802 length,
7803 profile_crc;
7804
cristy0d57eec2011-09-04 22:13:56 +00007805 unsigned char
7806 *data;
7807
glennrp29a106e2011-09-06 17:11:42 +00007808 length=(png_uint_32) GetStringInfoLength(profile);
7809
glennrpee7b4c02011-10-04 01:21:09 +00007810 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007811 {
glennrpee7b4c02011-10-04 01:21:09 +00007812 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007813 {
glennrpee7b4c02011-10-04 01:21:09 +00007814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7815 " Got a %lu-byte ICC profile (potentially sRGB)",
7816 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007817
glennrpee7b4c02011-10-04 01:21:09 +00007818 data=GetStringInfoDatum(profile);
7819 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007820
glennrpee7b4c02011-10-04 01:21:09 +00007821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007822 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007823
7824 if (profile_crc == check_crc[icheck])
7825 {
7826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7827 " It is sRGB.");
7828 if (image->rendering_intent==UndefinedIntent)
7829 image->rendering_intent=PerceptualIntent;
7830 break;
7831 }
glennrp29a106e2011-09-06 17:11:42 +00007832 }
glennrp29a106e2011-09-06 17:11:42 +00007833 }
glennrpee7b4c02011-10-04 01:21:09 +00007834 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007836 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007837 (unsigned long) length);
7838 }
cristy0d57eec2011-09-04 22:13:56 +00007839 }
7840 name=GetNextImageProfile(image);
7841 }
7842 }
7843
glennrp8bb3a022010-12-13 20:40:04 +00007844 number_opaque = 0;
7845 number_semitransparent = 0;
7846 number_transparent = 0;
7847
glennrpfd05d622011-02-25 04:10:33 +00007848 if (logging != MagickFalse)
7849 {
7850 if (image->storage_class == UndefinedClass)
7851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7852 " storage_class=UndefinedClass");
7853 if (image->storage_class == DirectClass)
7854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7855 " storage_class=DirectClass");
7856 if (image->storage_class == PseudoClass)
7857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7858 " storage_class=PseudoClass");
7859 }
glennrp28af3712011-04-06 18:07:30 +00007860
glennrp750105b2012-04-25 16:20:45 +00007861 if (image->storage_class == PseudoClass &&
glennrp7e65e932011-08-19 02:31:16 +00007862 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7863 (mng_info->write_png_colortype != 0 &&
7864 mng_info->write_png_colortype != 4)))
7865 {
cristy16ea1392012-03-21 20:38:41 +00007866 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007867 image->storage_class = DirectClass;
7868 }
7869
glennrpc6c391a2011-04-27 02:23:56 +00007870 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007871 {
glennrpc6c391a2011-04-27 02:23:56 +00007872 if (image->storage_class != PseudoClass && image->colormap != NULL)
7873 {
7874 /* Free the bogus colormap; it can cause trouble later */
7875 if (logging != MagickFalse)
7876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7877 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007878 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007879 image->colormap=NULL;
7880 }
glennrp28af3712011-04-06 18:07:30 +00007881 }
glennrpbb4f99d2011-05-22 11:13:17 +00007882
cristy3d9f5ba2012-06-26 13:37:31 +00007883 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00007884 (void) TransformImageColorspace(image,sRGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007885
glennrp3241bd02010-12-12 04:36:28 +00007886 /*
7887 Sometimes we get PseudoClass images whose RGB values don't match
7888 the colors in the colormap. This code syncs the RGB values.
7889 */
7890 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristy16ea1392012-03-21 20:38:41 +00007891 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007892
glennrpa6a06632011-01-19 15:15:34 +00007893#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7894 if (image->depth > 8)
7895 {
7896 if (logging != MagickFalse)
7897 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7898 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7899
7900 image->depth=8;
7901 }
7902#endif
7903
glennrp8e58efd2011-05-20 12:16:29 +00007904 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007905 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7906 {
cristy16ea1392012-03-21 20:38:41 +00007907 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007908 *r;
7909
glennrp8e58efd2011-05-20 12:16:29 +00007910 if (image->depth > 8)
7911 {
7912#if MAGICKCORE_QUANTUM_DEPTH > 16
7913 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007914 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007915
7916 for (y=0; y < (ssize_t) image->rows; y++)
7917 {
cristy16ea1392012-03-21 20:38:41 +00007918 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007919
cristy16ea1392012-03-21 20:38:41 +00007920 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007921 break;
7922
7923 for (x=0; x < (ssize_t) image->columns; x++)
7924 {
cristy16ea1392012-03-21 20:38:41 +00007925 LBR16PixelRGBA(r);
7926 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007927 }
glennrpbb4f99d2011-05-22 11:13:17 +00007928
glennrp8e58efd2011-05-20 12:16:29 +00007929 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7930 break;
7931 }
7932
7933 if (image->storage_class == PseudoClass && image->colormap != NULL)
7934 {
cristy3e08f112011-05-24 13:19:30 +00007935 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007936 {
glennrp91d99252011-06-25 14:30:13 +00007937 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007938 }
7939 }
7940#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7941 }
7942
7943 else if (image->depth > 4)
7944 {
7945#if MAGICKCORE_QUANTUM_DEPTH > 8
7946 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007947 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007948
7949 for (y=0; y < (ssize_t) image->rows; y++)
7950 {
cristy16ea1392012-03-21 20:38:41 +00007951 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007952
cristy16ea1392012-03-21 20:38:41 +00007953 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007954 break;
7955
7956 for (x=0; x < (ssize_t) image->columns; x++)
7957 {
cristy16ea1392012-03-21 20:38:41 +00007958 LBR08PixelRGBA(r);
7959 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007960 }
glennrpbb4f99d2011-05-22 11:13:17 +00007961
glennrp8e58efd2011-05-20 12:16:29 +00007962 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7963 break;
7964 }
7965
7966 if (image->storage_class == PseudoClass && image->colormap != NULL)
7967 {
cristy3e08f112011-05-24 13:19:30 +00007968 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007969 {
glennrp91d99252011-06-25 14:30:13 +00007970 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007971 }
7972 }
7973#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7974 }
7975 else
7976 if (image->depth > 2)
7977 {
7978 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007979 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007980
7981 for (y=0; y < (ssize_t) image->rows; y++)
7982 {
cristy16ea1392012-03-21 20:38:41 +00007983 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007984
cristy16ea1392012-03-21 20:38:41 +00007985 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007986 break;
7987
7988 for (x=0; x < (ssize_t) image->columns; x++)
7989 {
cristy16ea1392012-03-21 20:38:41 +00007990 LBR04PixelRGBA(r);
7991 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007992 }
glennrpbb4f99d2011-05-22 11:13:17 +00007993
glennrp8e58efd2011-05-20 12:16:29 +00007994 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7995 break;
7996 }
7997
7998 if (image->storage_class == PseudoClass && image->colormap != NULL)
7999 {
cristy3e08f112011-05-24 13:19:30 +00008000 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008001 {
glennrp91d99252011-06-25 14:30:13 +00008002 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008003 }
8004 }
8005 }
8006
8007 else if (image->depth > 1)
8008 {
8009 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00008010 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008011
8012 for (y=0; y < (ssize_t) image->rows; y++)
8013 {
cristy16ea1392012-03-21 20:38:41 +00008014 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008015
cristy16ea1392012-03-21 20:38:41 +00008016 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008017 break;
8018
8019 for (x=0; x < (ssize_t) image->columns; x++)
8020 {
cristy16ea1392012-03-21 20:38:41 +00008021 LBR02PixelRGBA(r);
8022 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008023 }
glennrpbb4f99d2011-05-22 11:13:17 +00008024
glennrp8e58efd2011-05-20 12:16:29 +00008025 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8026 break;
8027 }
8028
8029 if (image->storage_class == PseudoClass && image->colormap != NULL)
8030 {
cristy3e08f112011-05-24 13:19:30 +00008031 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008032 {
glennrp91d99252011-06-25 14:30:13 +00008033 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008034 }
8035 }
8036 }
8037 else
8038 {
8039 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00008040 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00008041
8042 for (y=0; y < (ssize_t) image->rows; y++)
8043 {
cristy16ea1392012-03-21 20:38:41 +00008044 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00008045
cristy16ea1392012-03-21 20:38:41 +00008046 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00008047 break;
8048
8049 for (x=0; x < (ssize_t) image->columns; x++)
8050 {
cristy16ea1392012-03-21 20:38:41 +00008051 LBR01PixelRGBA(r);
8052 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00008053 }
glennrpbb4f99d2011-05-22 11:13:17 +00008054
glennrp8e58efd2011-05-20 12:16:29 +00008055 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8056 break;
8057 }
8058
8059 if (image->storage_class == PseudoClass && image->colormap != NULL)
8060 {
cristy3e08f112011-05-24 13:19:30 +00008061 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00008062 {
glennrp91d99252011-06-25 14:30:13 +00008063 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00008064 }
8065 }
8066 }
glennrp9d0ea4d2011-04-22 18:35:57 +00008067 }
8068
glennrp67b9c1a2011-04-22 18:47:36 +00008069 /* To do: set to next higher multiple of 8 */
8070 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00008071 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00008072
glennrp2b013e42010-11-24 16:55:50 +00008073#if (MAGICKCORE_QUANTUM_DEPTH > 16)
8074 /* PNG does not handle depths greater than 16 so reduce it even
8075 * if lossy
8076 */
glennrp8e58efd2011-05-20 12:16:29 +00008077 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00008078 image->depth=16;
8079#endif
8080
glennrp3faa9a32011-04-23 14:00:25 +00008081#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00008082 if (image->depth > 8)
8083 {
8084 /* To do: fill low byte properly */
8085 image->depth=16;
8086 }
8087
glennrpc722dd82011-02-24 05:13:21 +00008088 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristy16ea1392012-03-21 20:38:41 +00008089 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00008090 image->depth = 8;
8091#endif
8092
glennrpc8c2f062011-02-25 19:00:33 +00008093 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00008094 * we reduce the transparency to binary and run again, then if there
8095 * 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 +00008096 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8097 * palette. Then (To do) we take care of a final reduction that is only
8098 * needed if there are still 256 colors present and one of them has both
8099 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00008100 */
glennrp82b3c532011-03-22 19:20:54 +00008101
glennrp8ca51ad2011-05-12 21:22:32 +00008102 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008103 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00008104 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00008105
glennrp8ca51ad2011-05-12 21:22:32 +00008106 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00008107 {
8108 /* BUILD_PALETTE
8109 *
8110 * Sometimes we get DirectClass images that have 256 colors or fewer.
8111 * This code will build a colormap.
8112 *
8113 * Also, sometimes we get PseudoClass images with an out-of-date
8114 * colormap. This code will replace the colormap with a new one.
8115 * Sometimes we get PseudoClass images that have more than 256 colors.
8116 * This code will delete the colormap and change the image to
8117 * DirectClass.
8118 *
cristy16ea1392012-03-21 20:38:41 +00008119 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00008120 * even though it sometimes contains left-over non-opaque values.
8121 *
8122 * Also we gather some information (number of opaque, transparent,
8123 * and semitransparent pixels, and whether the image has any non-gray
8124 * pixels or only black-and-white pixels) that we might need later.
8125 *
8126 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8127 * we need to check for bogus non-opaque values, at least.
8128 */
glennrp3c218112010-11-27 15:31:26 +00008129
glennrpd71e86a2011-02-24 01:28:37 +00008130 int
8131 n;
glennrp3c218112010-11-27 15:31:26 +00008132
cristy16ea1392012-03-21 20:38:41 +00008133 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008134 opaque[260],
8135 semitransparent[260],
8136 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00008137
cristy16ea1392012-03-21 20:38:41 +00008138 register const Quantum
8139 *s;
glennrp8bb3a022010-12-13 20:40:04 +00008140
cristy16ea1392012-03-21 20:38:41 +00008141 register Quantum
8142 *q,
glennrpfd05d622011-02-25 04:10:33 +00008143 *r;
8144
glennrpd71e86a2011-02-24 01:28:37 +00008145 if (logging != MagickFalse)
8146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8147 " Enter BUILD_PALETTE:");
8148
8149 if (logging != MagickFalse)
8150 {
glennrp03812ae2010-12-24 01:31:34 +00008151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008152 " image->columns=%.20g",(double) image->columns);
8153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8154 " image->rows=%.20g",(double) image->rows);
8155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8156 " image->matte=%.20g",(double) image->matte);
8157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8158 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008159
glennrpfd05d622011-02-25 04:10:33 +00008160 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008161 {
8162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008163 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008165 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008166
glennrpd71e86a2011-02-24 01:28:37 +00008167 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008168 {
glennrpd71e86a2011-02-24 01:28:37 +00008169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8170 " %d (%d,%d,%d,%d)",
8171 (int) i,
8172 (int) image->colormap[i].red,
8173 (int) image->colormap[i].green,
8174 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008175 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008176 }
glennrp2cc891a2010-12-24 13:44:32 +00008177
glennrpd71e86a2011-02-24 01:28:37 +00008178 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8179 {
8180 if (i > 255)
8181 {
8182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8183 " %d (%d,%d,%d,%d)",
8184 (int) i,
8185 (int) image->colormap[i].red,
8186 (int) image->colormap[i].green,
8187 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008188 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008189 }
8190 }
glennrp03812ae2010-12-24 01:31:34 +00008191 }
glennrp7ddcc222010-12-11 05:01:05 +00008192
glennrpd71e86a2011-02-24 01:28:37 +00008193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8194 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008195
glennrpd71e86a2011-02-24 01:28:37 +00008196 if (image->colors == 0)
cristy16ea1392012-03-21 20:38:41 +00008197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8198 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008199
glennrp8d3d6e52011-04-19 04:39:51 +00008200 if (ping_preserve_colormap == MagickFalse)
8201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8202 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008203 }
8204
glennrpd71e86a2011-02-24 01:28:37 +00008205 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008206 number_opaque = 0;
8207 number_semitransparent = 0;
8208 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008209
8210 for (y=0; y < (ssize_t) image->rows; y++)
8211 {
8212 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8213
cristy16ea1392012-03-21 20:38:41 +00008214 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008215 break;
8216
8217 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008218 {
glennrp4737d522011-04-29 03:33:42 +00008219 if (image->matte == MagickFalse ||
cristy16ea1392012-03-21 20:38:41 +00008220 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008221 {
8222 if (number_opaque < 259)
8223 {
8224 if (number_opaque == 0)
8225 {
cristy16ea1392012-03-21 20:38:41 +00008226 GetPixelInfoPixel(image, q, opaque);
8227 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008228 number_opaque=1;
8229 }
glennrp2cc891a2010-12-24 13:44:32 +00008230
glennrpd71e86a2011-02-24 01:28:37 +00008231 for (i=0; i< (ssize_t) number_opaque; i++)
8232 {
cristy16ea1392012-03-21 20:38:41 +00008233 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008234 break;
8235 }
glennrp7ddcc222010-12-11 05:01:05 +00008236
cristy16ea1392012-03-21 20:38:41 +00008237 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008238 {
8239 number_opaque++;
cristy16ea1392012-03-21 20:38:41 +00008240 GetPixelInfoPixel(image, q, opaque+i);
8241 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008242 }
8243 }
8244 }
cristy16ea1392012-03-21 20:38:41 +00008245 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008246 {
8247 if (number_transparent < 259)
8248 {
8249 if (number_transparent == 0)
8250 {
cristy16ea1392012-03-21 20:38:41 +00008251 GetPixelInfoPixel(image, q, transparent);
8252 ping_trans_color.red=(unsigned short)
8253 GetPixelRed(image,q);
8254 ping_trans_color.green=(unsigned short)
8255 GetPixelGreen(image,q);
8256 ping_trans_color.blue=(unsigned short)
8257 GetPixelBlue(image,q);
8258 ping_trans_color.gray=(unsigned short)
cristy972d1c42012-07-14 23:29:14 +00008259 GetPixelGray(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008260 number_transparent = 1;
8261 }
8262
8263 for (i=0; i< (ssize_t) number_transparent; i++)
8264 {
cristy16ea1392012-03-21 20:38:41 +00008265 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008266 break;
8267 }
8268
8269 if (i == (ssize_t) number_transparent &&
8270 number_transparent < 259)
8271 {
8272 number_transparent++;
cristy16ea1392012-03-21 20:38:41 +00008273 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008274 }
8275 }
8276 }
8277 else
8278 {
8279 if (number_semitransparent < 259)
8280 {
8281 if (number_semitransparent == 0)
8282 {
cristy16ea1392012-03-21 20:38:41 +00008283 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008284 number_semitransparent = 1;
8285 }
8286
8287 for (i=0; i< (ssize_t) number_semitransparent; i++)
8288 {
cristy16ea1392012-03-21 20:38:41 +00008289 if (IsPixelEquivalent(image,q, semitransparent+i)
8290 && GetPixelAlpha(image,q) ==
8291 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008292 break;
8293 }
8294
8295 if (i == (ssize_t) number_semitransparent &&
8296 number_semitransparent < 259)
8297 {
8298 number_semitransparent++;
cristy16ea1392012-03-21 20:38:41 +00008299 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008300 }
8301 }
8302 }
cristy16ea1392012-03-21 20:38:41 +00008303 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008304 }
8305 }
8306
cristy4054bfb2011-08-29 23:41:39 +00008307 if (mng_info->write_png8 == MagickFalse &&
8308 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008309 {
8310 /* Add the background color to the palette, if it
8311 * isn't already there.
8312 */
glennrpc6c391a2011-04-27 02:23:56 +00008313 if (logging != MagickFalse)
8314 {
8315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8316 " Check colormap for background (%d,%d,%d)",
8317 (int) image->background_color.red,
8318 (int) image->background_color.green,
8319 (int) image->background_color.blue);
8320 }
glennrpd71e86a2011-02-24 01:28:37 +00008321 for (i=0; i<number_opaque; i++)
8322 {
glennrpca7ad3a2011-04-26 04:44:54 +00008323 if (opaque[i].red == image->background_color.red &&
8324 opaque[i].green == image->background_color.green &&
8325 opaque[i].blue == image->background_color.blue)
8326 break;
glennrpd71e86a2011-02-24 01:28:37 +00008327 }
glennrpd71e86a2011-02-24 01:28:37 +00008328 if (number_opaque < 259 && i == number_opaque)
8329 {
glennrp8e045c82011-04-27 16:40:27 +00008330 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008331 ping_background.index = i;
glennrp388a8c82012-06-27 13:41:51 +00008332 number_opaque++;
glennrpc6c391a2011-04-27 02:23:56 +00008333 if (logging != MagickFalse)
8334 {
8335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8336 " background_color index is %d",(int) i);
8337 }
8338
glennrpd71e86a2011-02-24 01:28:37 +00008339 }
glennrpa080bc32011-03-11 18:03:44 +00008340 else if (logging != MagickFalse)
8341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8342 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008343 }
8344
8345 image_colors=number_opaque+number_transparent+number_semitransparent;
8346
glennrpa080bc32011-03-11 18:03:44 +00008347 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8348 {
8349 /* No room for the background color; remove it. */
8350 number_opaque--;
8351 image_colors--;
8352 }
8353
glennrpd71e86a2011-02-24 01:28:37 +00008354 if (logging != MagickFalse)
8355 {
8356 if (image_colors > 256)
8357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8358 " image has more than 256 colors");
8359
8360 else
8361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8362 " image has %d colors",image_colors);
8363 }
8364
glennrp8d3d6e52011-04-19 04:39:51 +00008365 if (ping_preserve_colormap != MagickFalse)
8366 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008367
glennrpfd05d622011-02-25 04:10:33 +00008368 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008369 {
8370 ping_have_color=MagickFalse;
8371 ping_have_non_bw=MagickFalse;
8372
glennrp0fa25802012-07-20 14:01:06 +00008373 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8374 {
8375 ping_have_color=MagickTrue;
8376 ping_have_non_bw=MagickFalse;
8377 }
8378
8379 if (IssRGBColorspace(image->colorspace) != MagickFalse)
8380 {
8381 ping_have_color=MagickTrue;
8382 ping_have_non_bw=MagickTrue;
8383 }
8384
glennrpd71e86a2011-02-24 01:28:37 +00008385 if(image_colors > 256)
8386 {
8387 for (y=0; y < (ssize_t) image->rows; y++)
8388 {
8389 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8390
cristy16ea1392012-03-21 20:38:41 +00008391 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008392 break;
8393
glennrpe5e6b802011-07-20 14:44:40 +00008394 s=q;
8395 for (x=0; x < (ssize_t) image->columns; x++)
8396 {
cristy16ea1392012-03-21 20:38:41 +00008397 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8398 GetPixelRed(image,s) != GetPixelBlue(image,s))
glennrpe5e6b802011-07-20 14:44:40 +00008399 {
8400 ping_have_color=MagickTrue;
8401 ping_have_non_bw=MagickTrue;
8402 break;
8403 }
cristy16ea1392012-03-21 20:38:41 +00008404 s+=GetPixelChannels(image);
glennrpe5e6b802011-07-20 14:44:40 +00008405 }
8406
8407 if (ping_have_color != MagickFalse)
8408 break;
8409
glennrpd71e86a2011-02-24 01:28:37 +00008410 /* Worst case is black-and-white; we are looking at every
8411 * pixel twice.
8412 */
8413
glennrpd71e86a2011-02-24 01:28:37 +00008414 if (ping_have_non_bw == MagickFalse)
8415 {
8416 s=q;
8417 for (x=0; x < (ssize_t) image->columns; x++)
8418 {
cristy16ea1392012-03-21 20:38:41 +00008419 if (GetPixelRed(image,s) != 0 &&
8420 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008421 {
8422 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008423 break;
glennrpd71e86a2011-02-24 01:28:37 +00008424 }
cristy16ea1392012-03-21 20:38:41 +00008425 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008426 }
glennrpe5e6b802011-07-20 14:44:40 +00008427 }
glennrpd71e86a2011-02-24 01:28:37 +00008428 }
glennrpbb4f99d2011-05-22 11:13:17 +00008429 }
8430 }
glennrpd71e86a2011-02-24 01:28:37 +00008431
8432 if (image_colors < 257)
8433 {
cristy16ea1392012-03-21 20:38:41 +00008434 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008435 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008436
glennrpd71e86a2011-02-24 01:28:37 +00008437 /*
8438 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008439 */
8440
glennrpd71e86a2011-02-24 01:28:37 +00008441 if (logging != MagickFalse)
8442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8443 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008444
glennrpd71e86a2011-02-24 01:28:37 +00008445 /* Sort palette, transparent first */;
8446
8447 n = 0;
8448
8449 for (i=0; i<number_transparent; i++)
8450 colormap[n++] = transparent[i];
8451
8452 for (i=0; i<number_semitransparent; i++)
8453 colormap[n++] = semitransparent[i];
8454
8455 for (i=0; i<number_opaque; i++)
8456 colormap[n++] = opaque[i];
8457
glennrpc6c391a2011-04-27 02:23:56 +00008458 ping_background.index +=
8459 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008460
glennrpd71e86a2011-02-24 01:28:37 +00008461 /* image_colors < 257; search the colormap instead of the pixels
8462 * to get ping_have_color and ping_have_non_bw
8463 */
8464 for (i=0; i<n; i++)
8465 {
8466 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008467 {
glennrpd71e86a2011-02-24 01:28:37 +00008468 if (colormap[i].red != colormap[i].green ||
8469 colormap[i].red != colormap[i].blue)
8470 {
8471 ping_have_color=MagickTrue;
8472 ping_have_non_bw=MagickTrue;
8473 break;
8474 }
8475 }
8476
8477 if (ping_have_non_bw == MagickFalse)
8478 {
8479 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008480 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008481 }
glennrp8bb3a022010-12-13 20:40:04 +00008482 }
8483
glennrpd71e86a2011-02-24 01:28:37 +00008484 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8485 (number_transparent == 0 && number_semitransparent == 0)) &&
8486 (((mng_info->write_png_colortype-1) ==
8487 PNG_COLOR_TYPE_PALETTE) ||
8488 (mng_info->write_png_colortype == 0)))
8489 {
glennrp6185c532011-01-14 17:58:40 +00008490 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008491 {
glennrpd71e86a2011-02-24 01:28:37 +00008492 if (n != (ssize_t) image_colors)
8493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8494 " image_colors (%d) and n (%d) don't match",
8495 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008496
glennrpd71e86a2011-02-24 01:28:37 +00008497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8498 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008499 }
glennrp03812ae2010-12-24 01:31:34 +00008500
glennrpd71e86a2011-02-24 01:28:37 +00008501 image->colors = image_colors;
8502
cristy16ea1392012-03-21 20:38:41 +00008503 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008504 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008505 ThrowWriterException(ResourceLimitError,
8506 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008507
8508 for (i=0; i< (ssize_t) image_colors; i++)
8509 image->colormap[i] = colormap[i];
8510
8511 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008512 {
glennrpd71e86a2011-02-24 01:28:37 +00008513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8514 " image->colors=%d (%d)",
8515 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008516
glennrpd71e86a2011-02-24 01:28:37 +00008517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8518 " Update the pixel indexes");
8519 }
glennrp6185c532011-01-14 17:58:40 +00008520
glennrpfd05d622011-02-25 04:10:33 +00008521 /* Sync the pixel indices with the new colormap */
8522
glennrpd71e86a2011-02-24 01:28:37 +00008523 for (y=0; y < (ssize_t) image->rows; y++)
8524 {
cristy16ea1392012-03-21 20:38:41 +00008525 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008526
cristy16ea1392012-03-21 20:38:41 +00008527 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008528 break;
glennrp6185c532011-01-14 17:58:40 +00008529
glennrpd71e86a2011-02-24 01:28:37 +00008530 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008531 {
glennrpd71e86a2011-02-24 01:28:37 +00008532 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008533 {
glennrpd71e86a2011-02-24 01:28:37 +00008534 if ((image->matte == MagickFalse ||
cristy16ea1392012-03-21 20:38:41 +00008535 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8536 image->colormap[i].red == GetPixelRed(image,q) &&
8537 image->colormap[i].green == GetPixelGreen(image,q) &&
8538 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008539 {
cristy16ea1392012-03-21 20:38:41 +00008540 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008541 break;
glennrp6185c532011-01-14 17:58:40 +00008542 }
glennrp6185c532011-01-14 17:58:40 +00008543 }
cristy16ea1392012-03-21 20:38:41 +00008544 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008545 }
glennrp6185c532011-01-14 17:58:40 +00008546
glennrpd71e86a2011-02-24 01:28:37 +00008547 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8548 break;
8549 }
8550 }
8551 }
8552
8553 if (logging != MagickFalse)
8554 {
8555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8556 " image->colors=%d", (int) image->colors);
8557
8558 if (image->colormap != NULL)
8559 {
8560 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008561 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008562
8563 for (i=0; i < (ssize_t) image->colors; i++)
8564 {
cristy72988482011-03-29 16:34:38 +00008565 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008566 {
8567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8568 " %d (%d,%d,%d,%d)",
8569 (int) i,
8570 (int) image->colormap[i].red,
8571 (int) image->colormap[i].green,
8572 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008573 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008574 }
glennrp6185c532011-01-14 17:58:40 +00008575 }
8576 }
glennrp03812ae2010-12-24 01:31:34 +00008577
glennrpd71e86a2011-02-24 01:28:37 +00008578 if (number_transparent < 257)
8579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8580 " number_transparent = %d",
8581 number_transparent);
8582 else
glennrp03812ae2010-12-24 01:31:34 +00008583
glennrpd71e86a2011-02-24 01:28:37 +00008584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8585 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008586
glennrpd71e86a2011-02-24 01:28:37 +00008587 if (number_opaque < 257)
8588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8589 " number_opaque = %d",
8590 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008591
glennrpd71e86a2011-02-24 01:28:37 +00008592 else
8593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8594 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008595
glennrpd71e86a2011-02-24 01:28:37 +00008596 if (number_semitransparent < 257)
8597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8598 " number_semitransparent = %d",
8599 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008600
glennrpd71e86a2011-02-24 01:28:37 +00008601 else
8602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8603 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008604
glennrpd71e86a2011-02-24 01:28:37 +00008605 if (ping_have_non_bw == MagickFalse)
8606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8607 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008608
glennrpd71e86a2011-02-24 01:28:37 +00008609 else if (ping_have_color == MagickFalse)
8610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8611 " All pixels and the background are gray");
8612
8613 else
8614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8615 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008616
glennrp03812ae2010-12-24 01:31:34 +00008617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8618 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008619 }
glennrpfd05d622011-02-25 04:10:33 +00008620
glennrpc8c2f062011-02-25 19:00:33 +00008621 if (mng_info->write_png8 == MagickFalse)
8622 break;
glennrpfd05d622011-02-25 04:10:33 +00008623
glennrpc8c2f062011-02-25 19:00:33 +00008624 /* Make any reductions necessary for the PNG8 format */
8625 if (image_colors <= 256 &&
8626 image_colors != 0 && image->colormap != NULL &&
8627 number_semitransparent == 0 &&
8628 number_transparent <= 1)
8629 break;
8630
8631 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008632 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8633 * transparent color so if more than one is transparent we merge
8634 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008635 */
glennrp130fc452011-08-20 03:43:18 +00008636 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008637 {
8638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8639 " Thresholding the alpha channel to binary");
8640
8641 for (y=0; y < (ssize_t) image->rows; y++)
8642 {
cristy16ea1392012-03-21 20:38:41 +00008643 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008644
cristy16ea1392012-03-21 20:38:41 +00008645 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008646 break;
8647
8648 for (x=0; x < (ssize_t) image->columns; x++)
8649 {
cristy16ea1392012-03-21 20:38:41 +00008650 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008651 {
cristy16ea1392012-03-21 20:38:41 +00008652 SetPixelInfoPixel(image,&image->background_color,r);
8653 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008654 }
8655 else
cristy16ea1392012-03-21 20:38:41 +00008656 SetPixelAlpha(image,OpaqueAlpha,r);
8657 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008658 }
glennrpbb4f99d2011-05-22 11:13:17 +00008659
glennrpc8c2f062011-02-25 19:00:33 +00008660 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8661 break;
8662
8663 if (image_colors != 0 && image_colors <= 256 &&
8664 image->colormap != NULL)
8665 for (i=0; i<image_colors; i++)
cristy16ea1392012-03-21 20:38:41 +00008666 image->colormap[i].alpha =
8667 (image->colormap[i].alpha > TransparentAlpha/2 ?
8668 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008669 }
8670 continue;
8671 }
8672
8673 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008674 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8675 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8676 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008677 */
glennrpd3371642011-03-22 19:42:23 +00008678 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8679 {
8680 if (logging != MagickFalse)
8681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8682 " Quantizing the background color to 4-4-4");
8683
8684 tried_444 = MagickTrue;
8685
glennrp91d99252011-06-25 14:30:13 +00008686 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008687
8688 if (logging != MagickFalse)
8689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8690 " Quantizing the pixel colors to 4-4-4");
8691
8692 if (image->colormap == NULL)
8693 {
8694 for (y=0; y < (ssize_t) image->rows; y++)
8695 {
cristy16ea1392012-03-21 20:38:41 +00008696 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00008697
cristy16ea1392012-03-21 20:38:41 +00008698 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008699 break;
8700
8701 for (x=0; x < (ssize_t) image->columns; x++)
8702 {
cristy16ea1392012-03-21 20:38:41 +00008703 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008704 LBR04PixelRGB(r);
cristy16ea1392012-03-21 20:38:41 +00008705 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00008706 }
glennrpbb4f99d2011-05-22 11:13:17 +00008707
glennrpd3371642011-03-22 19:42:23 +00008708 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8709 break;
8710 }
8711 }
8712
8713 else /* Should not reach this; colormap already exists and
8714 must be <= 256 */
8715 {
8716 if (logging != MagickFalse)
8717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8718 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008719
glennrpd3371642011-03-22 19:42:23 +00008720 for (i=0; i<image_colors; i++)
8721 {
glennrp91d99252011-06-25 14:30:13 +00008722 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008723 }
8724 }
8725 continue;
8726 }
8727
glennrp82b3c532011-03-22 19:20:54 +00008728 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8729 {
8730 if (logging != MagickFalse)
8731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8732 " Quantizing the background color to 3-3-3");
8733
8734 tried_333 = MagickTrue;
8735
glennrp91d99252011-06-25 14:30:13 +00008736 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008737
8738 if (logging != MagickFalse)
8739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008740 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008741
8742 if (image->colormap == NULL)
8743 {
8744 for (y=0; y < (ssize_t) image->rows; y++)
8745 {
cristy16ea1392012-03-21 20:38:41 +00008746 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00008747
cristy16ea1392012-03-21 20:38:41 +00008748 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008749 break;
8750
8751 for (x=0; x < (ssize_t) image->columns; x++)
8752 {
cristy16ea1392012-03-21 20:38:41 +00008753 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8754 LBR03RGB(r);
8755 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00008756 }
glennrpbb4f99d2011-05-22 11:13:17 +00008757
glennrp82b3c532011-03-22 19:20:54 +00008758 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8759 break;
8760 }
8761 }
8762
8763 else /* Should not reach this; colormap already exists and
8764 must be <= 256 */
8765 {
8766 if (logging != MagickFalse)
8767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008768 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008769 for (i=0; i<image_colors; i++)
8770 {
glennrp91d99252011-06-25 14:30:13 +00008771 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008772 }
glennrpd3371642011-03-22 19:42:23 +00008773 }
8774 continue;
glennrp82b3c532011-03-22 19:20:54 +00008775 }
glennrpc8c2f062011-02-25 19:00:33 +00008776
glennrp8ca51ad2011-05-12 21:22:32 +00008777 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008778 {
8779 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008781 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008782
glennrp8ca51ad2011-05-12 21:22:32 +00008783 tried_332 = MagickTrue;
8784
glennrp3faa9a32011-04-23 14:00:25 +00008785 /* Red and green were already done so we only quantize the blue
8786 * channel
8787 */
8788
glennrp91d99252011-06-25 14:30:13 +00008789 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008790
glennrpc8c2f062011-02-25 19:00:33 +00008791 if (logging != MagickFalse)
8792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008793 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008794
glennrpc8c2f062011-02-25 19:00:33 +00008795 if (image->colormap == NULL)
8796 {
8797 for (y=0; y < (ssize_t) image->rows; y++)
8798 {
cristy16ea1392012-03-21 20:38:41 +00008799 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008800
cristy16ea1392012-03-21 20:38:41 +00008801 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008802 break;
8803
8804 for (x=0; x < (ssize_t) image->columns; x++)
8805 {
cristy16ea1392012-03-21 20:38:41 +00008806 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008807 LBR02PixelBlue(r);
cristy16ea1392012-03-21 20:38:41 +00008808 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008809 }
glennrpbb4f99d2011-05-22 11:13:17 +00008810
glennrpc8c2f062011-02-25 19:00:33 +00008811 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8812 break;
8813 }
8814 }
glennrpfd05d622011-02-25 04:10:33 +00008815
glennrpc8c2f062011-02-25 19:00:33 +00008816 else /* Should not reach this; colormap already exists and
8817 must be <= 256 */
8818 {
8819 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008820 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008821 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008822 for (i=0; i<image_colors; i++)
8823 {
glennrp91d99252011-06-25 14:30:13 +00008824 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008825 }
8826 }
8827 continue;
8828 }
8829 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008830
8831 if (image_colors == 0 || image_colors > 256)
8832 {
8833 /* Take care of special case with 256 colors + 1 transparent
8834 * color. We don't need to quantize to 2-3-2-1; we only need to
8835 * eliminate one color, so we'll merge the two darkest red
8836 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8837 */
8838 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8839 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8840 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8841 {
8842 image->background_color.red=ScaleCharToQuantum(0x24);
8843 }
glennrpbb4f99d2011-05-22 11:13:17 +00008844
glennrp8ca51ad2011-05-12 21:22:32 +00008845 if (image->colormap == NULL)
8846 {
8847 for (y=0; y < (ssize_t) image->rows; y++)
8848 {
cristy16ea1392012-03-21 20:38:41 +00008849 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008850
cristy16ea1392012-03-21 20:38:41 +00008851 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008852 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008853
glennrp8ca51ad2011-05-12 21:22:32 +00008854 for (x=0; x < (ssize_t) image->columns; x++)
8855 {
cristy16ea1392012-03-21 20:38:41 +00008856 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8857 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8858 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8859 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008860 {
cristy16ea1392012-03-21 20:38:41 +00008861 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008862 }
cristy16ea1392012-03-21 20:38:41 +00008863 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008864 }
glennrpbb4f99d2011-05-22 11:13:17 +00008865
glennrp8ca51ad2011-05-12 21:22:32 +00008866 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8867 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008868
glennrp8ca51ad2011-05-12 21:22:32 +00008869 }
8870 }
8871
8872 else
8873 {
8874 for (i=0; i<image_colors; i++)
8875 {
8876 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8877 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8878 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8879 {
8880 image->colormap[i].red=ScaleCharToQuantum(0x24);
8881 }
8882 }
8883 }
8884 }
glennrpd71e86a2011-02-24 01:28:37 +00008885 }
glennrpfd05d622011-02-25 04:10:33 +00008886 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008887
glennrpfd05d622011-02-25 04:10:33 +00008888 /* If we are excluding the tRNS chunk and there is transparency,
8889 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8890 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008891 */
glennrp0e8ea192010-12-24 18:00:33 +00008892 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8893 (number_transparent != 0 || number_semitransparent != 0))
8894 {
glennrpd17915c2011-04-29 14:24:22 +00008895 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008896
8897 if (ping_have_color == MagickFalse)
8898 mng_info->write_png_colortype = 5;
8899
8900 else
8901 mng_info->write_png_colortype = 7;
8902
glennrp8d579662011-02-23 02:05:02 +00008903 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008904 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008905 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008906
glennrp0e8ea192010-12-24 18:00:33 +00008907 }
8908
glennrpfd05d622011-02-25 04:10:33 +00008909 /* See if cheap transparency is possible. It is only possible
8910 * when there is a single transparent color, no semitransparent
8911 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008912 * as the transparent color. We only need this information if
8913 * we are writing a PNG with colortype 0 or 2, and we have not
8914 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008915 */
glennrp5a39f372011-02-25 04:52:16 +00008916 if (number_transparent == 1 &&
8917 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008918 {
8919 ping_have_cheap_transparency = MagickTrue;
8920
8921 if (number_semitransparent != 0)
8922 ping_have_cheap_transparency = MagickFalse;
8923
8924 else if (image_colors == 0 || image_colors > 256 ||
8925 image->colormap == NULL)
8926 {
cristy16ea1392012-03-21 20:38:41 +00008927 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008928 *q;
8929
glennrpfd05d622011-02-25 04:10:33 +00008930 for (y=0; y < (ssize_t) image->rows; y++)
8931 {
8932 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8933
cristy16ea1392012-03-21 20:38:41 +00008934 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008935 break;
8936
8937 for (x=0; x < (ssize_t) image->columns; x++)
8938 {
cristy16ea1392012-03-21 20:38:41 +00008939 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8940 (unsigned short) GetPixelRed(image,q) ==
8941 ping_trans_color.red &&
8942 (unsigned short) GetPixelGreen(image,q) ==
8943 ping_trans_color.green &&
8944 (unsigned short) GetPixelBlue(image,q) ==
8945 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008946 {
8947 ping_have_cheap_transparency = MagickFalse;
8948 break;
8949 }
8950
cristy16ea1392012-03-21 20:38:41 +00008951 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008952 }
glennrpbb4f99d2011-05-22 11:13:17 +00008953
glennrpfd05d622011-02-25 04:10:33 +00008954 if (ping_have_cheap_transparency == MagickFalse)
8955 break;
8956 }
8957 }
8958 else
8959 {
glennrp67b9c1a2011-04-22 18:47:36 +00008960 /* Assuming that image->colormap[0] is the one transparent color
8961 * and that all others are opaque.
8962 */
glennrpfd05d622011-02-25 04:10:33 +00008963 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008964 for (i=1; i<image_colors; i++)
8965 if (image->colormap[i].red == image->colormap[0].red &&
8966 image->colormap[i].green == image->colormap[0].green &&
8967 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008968 {
glennrp67b9c1a2011-04-22 18:47:36 +00008969 ping_have_cheap_transparency = MagickFalse;
8970 break;
glennrpfd05d622011-02-25 04:10:33 +00008971 }
8972 }
glennrpbb4f99d2011-05-22 11:13:17 +00008973
glennrpfd05d622011-02-25 04:10:33 +00008974 if (logging != MagickFalse)
8975 {
8976 if (ping_have_cheap_transparency == MagickFalse)
8977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8978 " Cheap transparency is not possible.");
8979
8980 else
8981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8982 " Cheap transparency is possible.");
8983 }
8984 }
8985 else
8986 ping_have_cheap_transparency = MagickFalse;
8987
glennrp8640fb52010-11-23 15:48:26 +00008988 image_depth=image->depth;
8989
glennrp26c990a2010-11-23 02:23:20 +00008990 quantum_info = (QuantumInfo *) NULL;
8991 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008992 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008993 image_matte=image->matte;
8994
glennrp0fe50b42010-11-16 03:52:51 +00008995 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008996 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008997
glennrp52a479c2011-02-26 21:14:38 +00008998 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8999 (image->colors == 0 || image->colormap == NULL))
9000 {
cristy16ea1392012-03-21 20:38:41 +00009001 image_info=DestroyImageInfo(image_info);
9002 image=DestroyImage(image);
9003 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00009004 "Cannot write PNG8 or color-type 3; colormap is NULL",
cristy16ea1392012-03-21 20:38:41 +00009005 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00009006 return(MagickFalse);
9007 }
9008
cristy3ed852e2009-09-05 21:47:34 +00009009 /*
9010 Allocate the PNG structures
9011 */
9012#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00009013 error_info.image=image;
9014 error_info.exception=exception;
9015 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009016 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9017 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00009018
cristy3ed852e2009-09-05 21:47:34 +00009019#else
cristy16ea1392012-03-21 20:38:41 +00009020 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00009021 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00009022
cristy3ed852e2009-09-05 21:47:34 +00009023#endif
9024 if (ping == (png_struct *) NULL)
9025 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00009026
cristy3ed852e2009-09-05 21:47:34 +00009027 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00009028
cristy3ed852e2009-09-05 21:47:34 +00009029 if (ping_info == (png_info *) NULL)
9030 {
9031 png_destroy_write_struct(&ping,(png_info **) NULL);
9032 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9033 }
glennrp0fe50b42010-11-16 03:52:51 +00009034
cristy3ed852e2009-09-05 21:47:34 +00009035 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00009036 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00009037
glennrp5af765f2010-03-30 11:12:18 +00009038 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00009039 {
9040 /*
9041 PNG write failed.
9042 */
9043#ifdef PNG_DEBUG
9044 if (image_info->verbose)
9045 (void) printf("PNG write has failed.\n");
9046#endif
9047 png_destroy_write_struct(&ping,&ping_info);
glennrpedaa0382012-04-12 14:16:21 +00009048#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00009049 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00009050#endif
glennrpedaa0382012-04-12 14:16:21 +00009051
9052 if (ping_pixels != (unsigned char *) NULL)
9053 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
9054
9055 if (quantum_info != (QuantumInfo *) NULL)
9056 quantum_info=DestroyQuantumInfo(quantum_info);
9057
cristy16ea1392012-03-21 20:38:41 +00009058 if (ping_have_blob != MagickFalse)
9059 (void) CloseBlob(image);
9060 image_info=DestroyImageInfo(image_info);
9061 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00009062 return(MagickFalse);
9063 }
glennrpedaa0382012-04-12 14:16:21 +00009064
9065 /* { For navigation to end of SETJMP-protected block. Within this
9066 * block, use png_error() instead of Throwing an Exception, to ensure
9067 * that libpng is able to clean up, and that the semaphore is unlocked.
9068 */
9069
9070#ifdef PNG_SETJMP_NOT_THREAD_SAFE
9071 LockSemaphoreInfo(ping_semaphore);
9072#endif
9073
cristy3ed852e2009-09-05 21:47:34 +00009074 /*
9075 Prepare PNG for writing.
9076 */
glennrp9bf97b62012-06-06 21:03:14 +00009077
cristy3ed852e2009-09-05 21:47:34 +00009078#if defined(PNG_MNG_FEATURES_SUPPORTED)
9079 if (mng_info->write_mng)
glennrp25024a62012-06-07 11:38:34 +00009080 {
cristy3ed852e2009-09-05 21:47:34 +00009081 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp25024a62012-06-07 11:38:34 +00009082# ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9083 /* Disable new libpng-1.5.10 feature when writing a MNG because
9084 * zero-length PLTE is OK
9085 */
9086 png_set_check_for_invalid_index (ping, 0);
9087# endif
9088 }
glennrp2b013e42010-11-24 16:55:50 +00009089
cristy3ed852e2009-09-05 21:47:34 +00009090#else
9091# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9092 if (mng_info->write_mng)
9093 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00009094
cristy3ed852e2009-09-05 21:47:34 +00009095# endif
9096#endif
glennrp2b013e42010-11-24 16:55:50 +00009097
cristy3ed852e2009-09-05 21:47:34 +00009098 x=0;
glennrp2b013e42010-11-24 16:55:50 +00009099
cristy4e5bc842010-06-09 13:56:01 +00009100 ping_width=(png_uint_32) image->columns;
9101 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00009102
cristy3ed852e2009-09-05 21:47:34 +00009103 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9104 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009105
cristy3ed852e2009-09-05 21:47:34 +00009106 if (mng_info->write_png_depth != 0)
9107 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009108
cristy3ed852e2009-09-05 21:47:34 +00009109 /* Adjust requested depth to next higher valid depth if necessary */
9110 if (image_depth > 8)
9111 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009112
cristy3ed852e2009-09-05 21:47:34 +00009113 if ((image_depth > 4) && (image_depth < 8))
9114 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009115
cristy3ed852e2009-09-05 21:47:34 +00009116 if (image_depth == 3)
9117 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00009118
cristy3ed852e2009-09-05 21:47:34 +00009119 if (logging != MagickFalse)
9120 {
9121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009122 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00009123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009124 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00009125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009126 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00009127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009128 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00009129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009130 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00009131 }
glennrp8640fb52010-11-23 15:48:26 +00009132
cristy3ed852e2009-09-05 21:47:34 +00009133 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00009134 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00009135
glennrp26f37912010-12-23 16:22:42 +00009136
cristy3ed852e2009-09-05 21:47:34 +00009137#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00009138 if (ping_exclude_pHYs == MagickFalse)
9139 {
cristy16ea1392012-03-21 20:38:41 +00009140 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00009141 (!mng_info->write_mng || !mng_info->equal_physs))
9142 {
glennrp0fe50b42010-11-16 03:52:51 +00009143 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9145 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00009146
9147 if (image->units == PixelsPerInchResolution)
9148 {
glennrpdfd70802010-11-14 01:23:35 +00009149 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00009150 ping_pHYs_x_resolution=
cristy16ea1392012-03-21 20:38:41 +00009151 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00009152 ping_pHYs_y_resolution=
cristy16ea1392012-03-21 20:38:41 +00009153 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00009154 }
glennrpdfd70802010-11-14 01:23:35 +00009155
cristy3ed852e2009-09-05 21:47:34 +00009156 else if (image->units == PixelsPerCentimeterResolution)
9157 {
glennrpdfd70802010-11-14 01:23:35 +00009158 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy16ea1392012-03-21 20:38:41 +00009159 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9160 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00009161 }
glennrp991d11d2010-11-12 21:55:28 +00009162
cristy3ed852e2009-09-05 21:47:34 +00009163 else
9164 {
glennrpdfd70802010-11-14 01:23:35 +00009165 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy16ea1392012-03-21 20:38:41 +00009166 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9167 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009168 }
glennrp991d11d2010-11-12 21:55:28 +00009169
glennrp823b55c2011-03-14 18:46:46 +00009170 if (logging != MagickFalse)
9171 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9172 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9173 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9174 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009175 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009176 }
glennrp26f37912010-12-23 16:22:42 +00009177 }
cristy3ed852e2009-09-05 21:47:34 +00009178#endif
glennrpa521b2f2010-10-29 04:11:03 +00009179
glennrp26f37912010-12-23 16:22:42 +00009180 if (ping_exclude_bKGD == MagickFalse)
9181 {
glennrpa521b2f2010-10-29 04:11:03 +00009182 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009183 {
glennrpa521b2f2010-10-29 04:11:03 +00009184 unsigned int
9185 mask;
cristy3ed852e2009-09-05 21:47:34 +00009186
glennrpa521b2f2010-10-29 04:11:03 +00009187 mask=0xffff;
9188 if (ping_bit_depth == 8)
9189 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009190
glennrpa521b2f2010-10-29 04:11:03 +00009191 if (ping_bit_depth == 4)
9192 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009193
glennrpa521b2f2010-10-29 04:11:03 +00009194 if (ping_bit_depth == 2)
9195 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009196
glennrpa521b2f2010-10-29 04:11:03 +00009197 if (ping_bit_depth == 1)
9198 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009199
glennrpa521b2f2010-10-29 04:11:03 +00009200 ping_background.red=(png_uint_16)
9201 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009202
glennrpa521b2f2010-10-29 04:11:03 +00009203 ping_background.green=(png_uint_16)
9204 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009205
glennrpa521b2f2010-10-29 04:11:03 +00009206 ping_background.blue=(png_uint_16)
9207 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009208
9209 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009210 }
cristy3ed852e2009-09-05 21:47:34 +00009211
glennrp0fe50b42010-11-16 03:52:51 +00009212 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009213 {
9214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9215 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9217 " background_color index is %d",
9218 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009219
9220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9221 " ping_bit_depth=%d",ping_bit_depth);
9222 }
glennrp0fe50b42010-11-16 03:52:51 +00009223
9224 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009225 }
glennrp0fe50b42010-11-16 03:52:51 +00009226
cristy3ed852e2009-09-05 21:47:34 +00009227 /*
9228 Select the color type.
9229 */
9230 matte=image_matte;
9231 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009232
glennrp1273f7b2011-02-24 03:20:30 +00009233 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009234 {
glennrp0fe50b42010-11-16 03:52:51 +00009235
glennrpfd05d622011-02-25 04:10:33 +00009236 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009237 for reducing the sample depth from 8. */
9238
glennrp0fe50b42010-11-16 03:52:51 +00009239 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009240
glennrp8bb3a022010-12-13 20:40:04 +00009241 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009242
9243 /*
9244 Set image palette.
9245 */
9246 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9247
glennrp0fe50b42010-11-16 03:52:51 +00009248 if (logging != MagickFalse)
9249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9250 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009251 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009252
9253 for (i=0; i < (ssize_t) number_colors; i++)
9254 {
9255 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9256 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9257 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9258 if (logging != MagickFalse)
9259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009260#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009261 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009262#else
9263 " %5ld (%5d,%5d,%5d)",
9264#endif
glennrp0fe50b42010-11-16 03:52:51 +00009265 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9266
9267 }
glennrp2b013e42010-11-24 16:55:50 +00009268
glennrp8bb3a022010-12-13 20:40:04 +00009269 ping_have_PLTE=MagickTrue;
9270 image_depth=ping_bit_depth;
9271 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009272
glennrp58e01762011-01-07 15:28:54 +00009273 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009274 {
glennrp0fe50b42010-11-16 03:52:51 +00009275 /*
9276 Identify which colormap entry is transparent.
9277 */
9278 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009279 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009280
glennrp8bb3a022010-12-13 20:40:04 +00009281 for (i=0; i < (ssize_t) number_transparent; i++)
9282 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009283
glennrp0fe50b42010-11-16 03:52:51 +00009284
glennrp2cc891a2010-12-24 13:44:32 +00009285 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009286 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009287
9288 if (ping_num_trans == 0)
9289 ping_have_tRNS=MagickFalse;
9290
glennrp8bb3a022010-12-13 20:40:04 +00009291 else
9292 ping_have_tRNS=MagickTrue;
9293 }
glennrp0fe50b42010-11-16 03:52:51 +00009294
glennrp1273f7b2011-02-24 03:20:30 +00009295 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009296 {
glennrp1273f7b2011-02-24 03:20:30 +00009297 /*
9298 * Identify which colormap entry is the background color.
9299 */
9300
glennrp4f25bd02011-01-01 18:51:28 +00009301 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9302 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9303 break;
glennrp0fe50b42010-11-16 03:52:51 +00009304
glennrp4f25bd02011-01-01 18:51:28 +00009305 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009306
9307 if (logging != MagickFalse)
9308 {
9309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9310 " background_color index is %d",
9311 (int) ping_background.index);
9312 }
glennrp4f25bd02011-01-01 18:51:28 +00009313 }
cristy3ed852e2009-09-05 21:47:34 +00009314 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009315
glennrp7e65e932011-08-19 02:31:16 +00009316 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009317 {
9318 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009319 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009320 }
glennrp0fe50b42010-11-16 03:52:51 +00009321
glennrp7e65e932011-08-19 02:31:16 +00009322 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009323 {
9324 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009325 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009326 }
glennrp0fe50b42010-11-16 03:52:51 +00009327
glennrp8bb3a022010-12-13 20:40:04 +00009328 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009329 {
glennrp5af765f2010-03-30 11:12:18 +00009330 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009331
glennrp8bb3a022010-12-13 20:40:04 +00009332 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009333 {
glennrp5af765f2010-03-30 11:12:18 +00009334 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009335
glennrp5af765f2010-03-30 11:12:18 +00009336 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9337 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009338 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009339
glennrp8bb3a022010-12-13 20:40:04 +00009340 else
9341 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009342
9343 if (logging != MagickFalse)
9344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9345 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009346 }
glennrp0fe50b42010-11-16 03:52:51 +00009347
glennrp7c4c9e62011-03-21 20:23:32 +00009348 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009349 {
9350 if (logging != MagickFalse)
9351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009352 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009353
glennrpd6bf1612010-12-17 17:28:54 +00009354 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009355 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009356
glennrpd6bf1612010-12-17 17:28:54 +00009357 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009358 {
glennrp5af765f2010-03-30 11:12:18 +00009359 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009360 image_matte=MagickFalse;
9361 }
glennrp0fe50b42010-11-16 03:52:51 +00009362
glennrpd6bf1612010-12-17 17:28:54 +00009363 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009364 {
glennrp5af765f2010-03-30 11:12:18 +00009365 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009366 image_matte=MagickTrue;
9367 }
glennrp0fe50b42010-11-16 03:52:51 +00009368
glennrp5aa37f62011-01-02 03:07:57 +00009369 if (image_info->type == PaletteType ||
9370 image_info->type == PaletteMatteType)
9371 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9372
glennrp7c4c9e62011-03-21 20:23:32 +00009373 if (mng_info->write_png_colortype == 0 &&
9374 (image_info->type == UndefinedType ||
9375 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009376 {
glennrp5aa37f62011-01-02 03:07:57 +00009377 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009378 {
glennrp5aa37f62011-01-02 03:07:57 +00009379 if (image_matte == MagickFalse)
9380 {
9381 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9382 image_matte=MagickFalse;
9383 }
glennrp0fe50b42010-11-16 03:52:51 +00009384
glennrp0b206f52011-01-07 04:55:32 +00009385 else
glennrp5aa37f62011-01-02 03:07:57 +00009386 {
9387 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9388 image_matte=MagickTrue;
9389 }
9390 }
9391 else
glennrp8bb3a022010-12-13 20:40:04 +00009392 {
glennrp5aa37f62011-01-02 03:07:57 +00009393 if (image_matte == MagickFalse)
9394 {
9395 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9396 image_matte=MagickFalse;
9397 }
glennrp8bb3a022010-12-13 20:40:04 +00009398
glennrp0b206f52011-01-07 04:55:32 +00009399 else
glennrp5aa37f62011-01-02 03:07:57 +00009400 {
9401 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9402 image_matte=MagickTrue;
9403 }
9404 }
glennrp0fe50b42010-11-16 03:52:51 +00009405 }
glennrp5aa37f62011-01-02 03:07:57 +00009406
cristy3ed852e2009-09-05 21:47:34 +00009407 }
glennrp0fe50b42010-11-16 03:52:51 +00009408
cristy3ed852e2009-09-05 21:47:34 +00009409 if (logging != MagickFalse)
9410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009411 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009412
glennrp5af765f2010-03-30 11:12:18 +00009413 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009414 {
9415 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9416 ping_color_type == PNG_COLOR_TYPE_RGB ||
9417 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9418 ping_bit_depth=8;
9419 }
cristy3ed852e2009-09-05 21:47:34 +00009420
glennrpd6bf1612010-12-17 17:28:54 +00009421 old_bit_depth=ping_bit_depth;
9422
glennrp5af765f2010-03-30 11:12:18 +00009423 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009424 {
glennrp8d579662011-02-23 02:05:02 +00009425 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9426 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009427 }
glennrp8640fb52010-11-23 15:48:26 +00009428
glennrp5af765f2010-03-30 11:12:18 +00009429 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009430 {
cristy35ef8242010-06-03 16:24:13 +00009431 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009432 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009433
9434 if (image->colors == 0)
9435 {
glennrp0fe50b42010-11-16 03:52:51 +00009436 /* DO SOMETHING */
glennrpedaa0382012-04-12 14:16:21 +00009437 png_error(ping,"image has 0 colors");
glennrp0f111982010-07-07 20:18:33 +00009438 }
9439
cristy35ef8242010-06-03 16:24:13 +00009440 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009441 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009442 }
glennrp2b013e42010-11-24 16:55:50 +00009443
glennrpd6bf1612010-12-17 17:28:54 +00009444 if (logging != MagickFalse)
9445 {
9446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9447 " Number of colors: %.20g",(double) image_colors);
9448
9449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9450 " Tentative PNG bit depth: %d",ping_bit_depth);
9451 }
9452
9453 if (ping_bit_depth < (int) mng_info->write_png_depth)
9454 ping_bit_depth = mng_info->write_png_depth;
9455 }
glennrp2cc891a2010-12-24 13:44:32 +00009456
glennrp5af765f2010-03-30 11:12:18 +00009457 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009458
cristy3ed852e2009-09-05 21:47:34 +00009459 if (logging != MagickFalse)
9460 {
9461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +00009462 " Tentative PNG color type: %s (%.20g)",
9463 PngColorTypeToString(ping_color_type),
9464 (double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009465
cristy3ed852e2009-09-05 21:47:34 +00009466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009467 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009468
cristy3ed852e2009-09-05 21:47:34 +00009469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009470 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009471
cristy3ed852e2009-09-05 21:47:34 +00009472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009473
glennrp8640fb52010-11-23 15:48:26 +00009474 " image->depth: %.20g",(double) image->depth);
9475
9476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009477 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009478 }
9479
glennrp58e01762011-01-07 15:28:54 +00009480 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009481 {
glennrp4f25bd02011-01-01 18:51:28 +00009482 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009483 {
glennrp7c4c9e62011-03-21 20:23:32 +00009484 if (mng_info->write_png_colortype == 0)
9485 {
9486 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009487
glennrp7c4c9e62011-03-21 20:23:32 +00009488 if (ping_have_color != MagickFalse)
9489 ping_color_type=PNG_COLOR_TYPE_RGBA;
9490 }
glennrp4f25bd02011-01-01 18:51:28 +00009491
9492 /*
9493 * Determine if there is any transparent color.
9494 */
9495 if (number_transparent + number_semitransparent == 0)
9496 {
9497 /*
9498 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9499 */
glennrpa6a06632011-01-19 15:15:34 +00009500
glennrp4f25bd02011-01-01 18:51:28 +00009501 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009502
9503 if (mng_info->write_png_colortype == 0)
9504 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009505 }
9506
9507 else
9508 {
9509 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009510 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009511
9512 mask=0xffff;
9513
9514 if (ping_bit_depth == 8)
9515 mask=0x00ff;
9516
9517 if (ping_bit_depth == 4)
9518 mask=0x000f;
9519
9520 if (ping_bit_depth == 2)
9521 mask=0x0003;
9522
9523 if (ping_bit_depth == 1)
9524 mask=0x0001;
9525
9526 ping_trans_color.red=(png_uint_16)
9527 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9528
9529 ping_trans_color.green=(png_uint_16)
9530 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9531
9532 ping_trans_color.blue=(png_uint_16)
9533 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9534
9535 ping_trans_color.gray=(png_uint_16)
cristy16ea1392012-03-21 20:38:41 +00009536 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009537 image->colormap)) & mask);
9538
9539 ping_trans_color.index=(png_byte) 0;
9540
9541 ping_have_tRNS=MagickTrue;
9542 }
9543
9544 if (ping_have_tRNS != MagickFalse)
9545 {
9546 /*
glennrpfd05d622011-02-25 04:10:33 +00009547 * Determine if there is one and only one transparent color
9548 * and if so if it is fully transparent.
9549 */
9550 if (ping_have_cheap_transparency == MagickFalse)
9551 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009552 }
9553
9554 if (ping_have_tRNS != MagickFalse)
9555 {
glennrp7c4c9e62011-03-21 20:23:32 +00009556 if (mng_info->write_png_colortype == 0)
9557 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009558
9559 if (image_depth == 8)
9560 {
9561 ping_trans_color.red&=0xff;
9562 ping_trans_color.green&=0xff;
9563 ping_trans_color.blue&=0xff;
9564 ping_trans_color.gray&=0xff;
9565 }
9566 }
9567 }
cristy3ed852e2009-09-05 21:47:34 +00009568 else
9569 {
cristy3ed852e2009-09-05 21:47:34 +00009570 if (image_depth == 8)
9571 {
glennrp5af765f2010-03-30 11:12:18 +00009572 ping_trans_color.red&=0xff;
9573 ping_trans_color.green&=0xff;
9574 ping_trans_color.blue&=0xff;
9575 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009576 }
9577 }
9578 }
glennrp8640fb52010-11-23 15:48:26 +00009579
cristy3ed852e2009-09-05 21:47:34 +00009580 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009581
glennrp2e09f552010-11-14 00:38:48 +00009582 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009583 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009584
glennrp39992b42010-11-14 00:03:43 +00009585 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009586 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009587 ping_have_color == MagickFalse &&
9588 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009589 {
cristy35ef8242010-06-03 16:24:13 +00009590 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009591
cristy3ed852e2009-09-05 21:47:34 +00009592 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009593 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009594
glennrp7c4c9e62011-03-21 20:23:32 +00009595 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009596 {
glennrp5af765f2010-03-30 11:12:18 +00009597 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009598
cristy3ed852e2009-09-05 21:47:34 +00009599 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009600 {
9601 if (logging != MagickFalse)
9602 {
9603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9604 " Scaling ping_trans_color (0)");
9605 }
9606 ping_trans_color.gray*=0x0101;
9607 }
cristy3ed852e2009-09-05 21:47:34 +00009608 }
glennrp0fe50b42010-11-16 03:52:51 +00009609
cristy3ed852e2009-09-05 21:47:34 +00009610 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9611 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009612
glennrp136ee3a2011-04-27 15:47:45 +00009613 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009614 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009615 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009616
cristy3ed852e2009-09-05 21:47:34 +00009617 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009618 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009619
cristy3ed852e2009-09-05 21:47:34 +00009620 else
9621 {
glennrp5af765f2010-03-30 11:12:18 +00009622 ping_bit_depth=8;
9623 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009624 {
9625 if(!mng_info->write_png_depth)
9626 {
glennrp5af765f2010-03-30 11:12:18 +00009627 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009628
cristy35ef8242010-06-03 16:24:13 +00009629 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009630 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009631 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009632 }
9633 }
glennrp2b013e42010-11-24 16:55:50 +00009634
glennrp0fe50b42010-11-16 03:52:51 +00009635 else if (ping_color_type ==
9636 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009637 mng_info->IsPalette)
9638 {
cristy3ed852e2009-09-05 21:47:34 +00009639 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009640
cristy3ed852e2009-09-05 21:47:34 +00009641 int
9642 depth_4_ok=MagickTrue,
9643 depth_2_ok=MagickTrue,
9644 depth_1_ok=MagickTrue;
9645
cristybb503372010-05-27 20:51:26 +00009646 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009647 {
9648 unsigned char
9649 intensity;
9650
9651 intensity=ScaleQuantumToChar(image->colormap[i].red);
9652
9653 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9654 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9655 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9656 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009657 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009658 depth_1_ok=MagickFalse;
9659 }
glennrp2b013e42010-11-24 16:55:50 +00009660
cristy3ed852e2009-09-05 21:47:34 +00009661 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009662 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009663
cristy3ed852e2009-09-05 21:47:34 +00009664 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009665 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009666
cristy3ed852e2009-09-05 21:47:34 +00009667 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009668 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009669 }
9670 }
glennrp2b013e42010-11-24 16:55:50 +00009671
glennrp5af765f2010-03-30 11:12:18 +00009672 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009673 }
glennrp0fe50b42010-11-16 03:52:51 +00009674
cristy3ed852e2009-09-05 21:47:34 +00009675 else
glennrp0fe50b42010-11-16 03:52:51 +00009676
cristy3ed852e2009-09-05 21:47:34 +00009677 if (mng_info->IsPalette)
9678 {
glennrp17a14852010-05-10 03:01:59 +00009679 number_colors=image_colors;
9680
cristy3ed852e2009-09-05 21:47:34 +00009681 if (image_depth <= 8)
9682 {
cristy3ed852e2009-09-05 21:47:34 +00009683 /*
9684 Set image palette.
9685 */
glennrp5af765f2010-03-30 11:12:18 +00009686 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009687
glennrp58e01762011-01-07 15:28:54 +00009688 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009689 {
glennrp9c1eb072010-06-06 22:19:15 +00009690 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009691
glennrp3b51f0e2010-11-27 18:14:08 +00009692 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9694 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009695 }
glennrp0fe50b42010-11-16 03:52:51 +00009696
cristy3ed852e2009-09-05 21:47:34 +00009697 else
9698 {
cristybb503372010-05-27 20:51:26 +00009699 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009700 {
9701 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9702 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9703 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9704 }
glennrp0fe50b42010-11-16 03:52:51 +00009705
glennrp3b51f0e2010-11-27 18:14:08 +00009706 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009708 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009709 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009710
glennrp39992b42010-11-14 00:03:43 +00009711 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009712 }
glennrp0fe50b42010-11-16 03:52:51 +00009713
cristy3ed852e2009-09-05 21:47:34 +00009714 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009715 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009716 {
cristybefe4d22010-06-07 01:18:58 +00009717 size_t
9718 one;
9719
glennrp5af765f2010-03-30 11:12:18 +00009720 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009721 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009722
cristy16ea1392012-03-21 20:38:41 +00009723 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009724 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009725 }
glennrp0fe50b42010-11-16 03:52:51 +00009726
glennrp5af765f2010-03-30 11:12:18 +00009727 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009728
glennrp58e01762011-01-07 15:28:54 +00009729 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009730 {
glennrp0fe50b42010-11-16 03:52:51 +00009731 /*
glennrpd6bf1612010-12-17 17:28:54 +00009732 * Set up trans_colors array.
9733 */
glennrp0fe50b42010-11-16 03:52:51 +00009734 assert(number_colors <= 256);
9735
glennrpd6bf1612010-12-17 17:28:54 +00009736 ping_num_trans=(unsigned short) (number_transparent +
9737 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009738
9739 if (ping_num_trans == 0)
9740 ping_have_tRNS=MagickFalse;
9741
glennrpd6bf1612010-12-17 17:28:54 +00009742 else
glennrp0fe50b42010-11-16 03:52:51 +00009743 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009744 if (logging != MagickFalse)
9745 {
9746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9747 " Scaling ping_trans_color (1)");
9748 }
glennrpd6bf1612010-12-17 17:28:54 +00009749 ping_have_tRNS=MagickTrue;
9750
9751 for (i=0; i < ping_num_trans; i++)
9752 {
glennrp750105b2012-04-25 16:20:45 +00009753 ping_trans_alpha[i]= (png_byte)
cristy16ea1392012-03-21 20:38:41 +00009754 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009755 }
glennrp0fe50b42010-11-16 03:52:51 +00009756 }
9757 }
cristy3ed852e2009-09-05 21:47:34 +00009758 }
9759 }
glennrp0fe50b42010-11-16 03:52:51 +00009760
cristy3ed852e2009-09-05 21:47:34 +00009761 else
9762 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009763
cristy3ed852e2009-09-05 21:47:34 +00009764 if (image_depth < 8)
9765 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009766
cristy3ed852e2009-09-05 21:47:34 +00009767 if ((save_image_depth == 16) && (image_depth == 8))
9768 {
glennrp4f25bd02011-01-01 18:51:28 +00009769 if (logging != MagickFalse)
9770 {
9771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9772 " Scaling ping_trans_color from (%d,%d,%d)",
9773 (int) ping_trans_color.red,
9774 (int) ping_trans_color.green,
9775 (int) ping_trans_color.blue);
9776 }
9777
glennrp5af765f2010-03-30 11:12:18 +00009778 ping_trans_color.red*=0x0101;
9779 ping_trans_color.green*=0x0101;
9780 ping_trans_color.blue*=0x0101;
9781 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009782
9783 if (logging != MagickFalse)
9784 {
9785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9786 " to (%d,%d,%d)",
9787 (int) ping_trans_color.red,
9788 (int) ping_trans_color.green,
9789 (int) ping_trans_color.blue);
9790 }
cristy3ed852e2009-09-05 21:47:34 +00009791 }
9792 }
9793
cristy4383ec82011-01-05 15:42:32 +00009794 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9795 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009796
cristy3ed852e2009-09-05 21:47:34 +00009797 /*
9798 Adjust background and transparency samples in sub-8-bit grayscale files.
9799 */
glennrp5af765f2010-03-30 11:12:18 +00009800 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009801 PNG_COLOR_TYPE_GRAY)
9802 {
9803 png_uint_16
9804 maxval;
9805
cristy35ef8242010-06-03 16:24:13 +00009806 size_t
9807 one=1;
9808
cristy22ffd972010-06-03 16:51:47 +00009809 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009810
glennrp4f25bd02011-01-01 18:51:28 +00009811 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009812 {
cristy3ed852e2009-09-05 21:47:34 +00009813
cristy16ea1392012-03-21 20:38:41 +00009814 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9815 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9816 &image->background_color))) +.5)));
9817
cristy3ed852e2009-09-05 21:47:34 +00009818 if (logging != MagickFalse)
9819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00009820 " Setting up bKGD chunk (2)");
9821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9822 " background_color index is %d",
9823 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009824
glennrp991d11d2010-11-12 21:55:28 +00009825 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009826 }
cristy3ed852e2009-09-05 21:47:34 +00009827
glennrp3e3e20f2011-06-09 04:21:43 +00009828 if (logging != MagickFalse)
9829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9830 " Scaling ping_trans_color.gray from %d",
9831 (int)ping_trans_color.gray);
9832
glennrp9be9b1c2011-06-09 12:21:45 +00009833 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009834 ping_trans_color.gray)+.5);
9835
9836 if (logging != MagickFalse)
9837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9838 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009839 }
glennrp17a14852010-05-10 03:01:59 +00009840
glennrp26f37912010-12-23 16:22:42 +00009841 if (ping_exclude_bKGD == MagickFalse)
9842 {
glennrp1273f7b2011-02-24 03:20:30 +00009843 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009844 {
9845 /*
9846 Identify which colormap entry is the background color.
9847 */
9848
glennrp17a14852010-05-10 03:01:59 +00009849 number_colors=image_colors;
9850
glennrpa521b2f2010-10-29 04:11:03 +00009851 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9852 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009853 break;
9854
9855 ping_background.index=(png_byte) i;
9856
glennrp3b51f0e2010-11-27 18:14:08 +00009857 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009858 {
9859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009860 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009861 }
glennrp0fe50b42010-11-16 03:52:51 +00009862
cristy13d07042010-11-21 20:56:18 +00009863 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009864 {
9865 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009866
9867 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009868 {
9869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9870 " background =(%d,%d,%d)",
9871 (int) ping_background.red,
9872 (int) ping_background.green,
9873 (int) ping_background.blue);
9874 }
9875 }
glennrpa521b2f2010-10-29 04:11:03 +00009876
glennrpd6bf1612010-12-17 17:28:54 +00009877 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009878 {
glennrp3b51f0e2010-11-27 18:14:08 +00009879 if (logging != MagickFalse)
9880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9881 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009882 ping_have_bKGD = MagickFalse;
9883 }
glennrp17a14852010-05-10 03:01:59 +00009884 }
glennrp26f37912010-12-23 16:22:42 +00009885 }
glennrp17a14852010-05-10 03:01:59 +00009886
cristy3ed852e2009-09-05 21:47:34 +00009887 if (logging != MagickFalse)
9888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a56e9c2012-04-25 17:06:57 +00009889 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
9890 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009891 /*
9892 Initialize compression level and filtering.
9893 */
9894 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009895 {
9896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9897 " Setting up deflate compression");
9898
9899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9900 " Compression buffer size: 32768");
9901 }
9902
cristy3ed852e2009-09-05 21:47:34 +00009903 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009904
cristy3ed852e2009-09-05 21:47:34 +00009905 if (logging != MagickFalse)
9906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9907 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009908
cristy4054bfb2011-08-29 23:41:39 +00009909 png_set_compression_mem_level(ping, 9);
9910
glennrp10d739e2011-06-29 18:00:52 +00009911 /* Untangle the "-quality" setting:
9912
9913 Undefined is 0; the default is used.
9914 Default is 75
9915
9916 10's digit:
9917
9918 0: Use Z_HUFFMAN_ONLY strategy with the
9919 zlib default compression level
9920
9921 1-9: the zlib compression level
9922
9923 1's digit:
9924
9925 0-4: the PNG filter method
9926
9927 5: libpng adaptive filtering if compression level > 5
9928 libpng filter type "none" if compression level <= 5
9929 or if image is grayscale or palette
glennrp750105b2012-04-25 16:20:45 +00009930
glennrp10d739e2011-06-29 18:00:52 +00009931 6: libpng adaptive filtering
9932
9933 7: "LOCO" filtering (intrapixel differing) if writing
9934 a MNG, othewise "none". Did not work in IM-6.7.0-9
9935 and earlier because of a missing "else".
9936
9937 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009938 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009939
9940 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009941 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009942
9943 Note that using the -quality option, not all combinations of
9944 PNG filter type, zlib compression level, and zlib compression
cristy16ea1392012-03-21 20:38:41 +00009945 strategy are possible. This will be addressed soon in a
9946 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009947
9948 */
9949
cristy3ed852e2009-09-05 21:47:34 +00009950 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9951 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009952
glennrp18682582011-06-30 18:11:47 +00009953 if (quality <= 9)
9954 {
9955 if (mng_info->write_png_compression_strategy == 0)
9956 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9957 }
glennrp750105b2012-04-25 16:20:45 +00009958
glennrp18682582011-06-30 18:11:47 +00009959 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009960 {
9961 int
9962 level;
9963
cristybb503372010-05-27 20:51:26 +00009964 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009965
glennrp18682582011-06-30 18:11:47 +00009966 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009967 }
glennrp0fe50b42010-11-16 03:52:51 +00009968
glennrp18682582011-06-30 18:11:47 +00009969 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009970 {
glennrp18682582011-06-30 18:11:47 +00009971 if ((quality %10) == 8 || (quality %10) == 9)
glennrpa24b2452012-06-27 11:38:38 +00009972#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
9973 mng_info->write_png_compression_strategy=Z_RLE+1;
9974#else
9975 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
9976#endif
cristy3ed852e2009-09-05 21:47:34 +00009977 }
glennrp0fe50b42010-11-16 03:52:51 +00009978
glennrp18682582011-06-30 18:11:47 +00009979 if (mng_info->write_png_compression_filter == 0)
9980 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9981
cristy3ed852e2009-09-05 21:47:34 +00009982 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009983 {
glennrp18682582011-06-30 18:11:47 +00009984 if (mng_info->write_png_compression_level)
9985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9986 " Compression level: %d",
9987 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009988
glennrp18682582011-06-30 18:11:47 +00009989 if (mng_info->write_png_compression_strategy)
9990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9991 " Compression strategy: %d",
9992 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009993
glennrp18682582011-06-30 18:11:47 +00009994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9995 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009996
cristy4054bfb2011-08-29 23:41:39 +00009997 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9999 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +000010000 else if (mng_info->write_png_compression_filter == 0 ||
10001 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +000010002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10003 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +000010004 else
10005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10006 " Base filter method: %d",
10007 (int) mng_info->write_png_compression_filter-1);
10008 }
glennrp2b013e42010-11-24 16:55:50 +000010009
glennrp18682582011-06-30 18:11:47 +000010010 if (mng_info->write_png_compression_level != 0)
10011 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10012
10013 if (mng_info->write_png_compression_filter == 6)
10014 {
10015 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10016 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10017 (quality < 50))
10018 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10019 else
10020 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10021 }
cristy4054bfb2011-08-29 23:41:39 +000010022 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +000010023 mng_info->write_png_compression_filter == 10)
10024 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10025
10026 else if (mng_info->write_png_compression_filter == 8)
10027 {
10028#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10029 if (mng_info->write_mng)
10030 {
10031 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10032 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10033 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10034 }
10035#endif
cristy4054bfb2011-08-29 23:41:39 +000010036 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +000010037 }
10038
10039 else if (mng_info->write_png_compression_filter == 9)
10040 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10041
10042 else if (mng_info->write_png_compression_filter != 0)
10043 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10044 mng_info->write_png_compression_filter-1);
10045
10046 if (mng_info->write_png_compression_strategy != 0)
10047 png_set_compression_strategy(ping,
10048 mng_info->write_png_compression_strategy-1);
10049
cristy0d57eec2011-09-04 22:13:56 +000010050 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10051 if (ping_exclude_sRGB != MagickFalse ||
10052 (image->rendering_intent == UndefinedIntent))
10053 {
10054 if ((ping_exclude_tEXt == MagickFalse ||
10055 ping_exclude_zTXt == MagickFalse) &&
10056 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +000010057 {
10058 ResetImageProfileIterator(image);
10059 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +000010060 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010061 profile=GetImageProfile(image,name);
10062
10063 if (profile != (StringInfo *) NULL)
10064 {
glennrp5af765f2010-03-30 11:12:18 +000010065#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +000010066 if ((LocaleCompare(name,"ICC") == 0) ||
10067 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +000010068 {
glennrpc8cbc5d2011-01-01 00:12:34 +000010069
10070 if (ping_exclude_iCCP == MagickFalse)
10071 {
cristy16ea1392012-03-21 20:38:41 +000010072 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +000010073#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +000010074 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +000010075#else
10076 (png_const_bytep) GetStringInfoDatum(profile),
10077#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010078 (png_uint_32) GetStringInfoLength(profile));
10079 }
glennrp26f37912010-12-23 16:22:42 +000010080 }
glennrp0fe50b42010-11-16 03:52:51 +000010081
glennrpc8cbc5d2011-01-01 00:12:34 +000010082 else
cristy3ed852e2009-09-05 21:47:34 +000010083#endif
glennrpc8cbc5d2011-01-01 00:12:34 +000010084 if (ping_exclude_zCCP == MagickFalse)
10085 {
glennrpcf002022011-01-30 02:38:15 +000010086 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +000010087 (unsigned char *) name,(unsigned char *) name,
10088 GetStringInfoDatum(profile),
10089 (png_uint_32) GetStringInfoLength(profile));
10090 }
10091 }
glennrp0b206f52011-01-07 04:55:32 +000010092
glennrpc8cbc5d2011-01-01 00:12:34 +000010093 if (logging != MagickFalse)
10094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10095 " Setting up text chunk with %s profile",name);
10096
10097 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +000010098 }
cristy0d57eec2011-09-04 22:13:56 +000010099 }
cristy3ed852e2009-09-05 21:47:34 +000010100 }
10101
10102#if defined(PNG_WRITE_sRGB_SUPPORTED)
10103 if ((mng_info->have_write_global_srgb == 0) &&
glennrp5f11bf92012-05-05 03:21:03 +000010104 (image->rendering_intent != UndefinedIntent))
cristy3ed852e2009-09-05 21:47:34 +000010105 {
glennrp26f37912010-12-23 16:22:42 +000010106 if (ping_exclude_sRGB == MagickFalse)
10107 {
10108 /*
10109 Note image rendering intent.
10110 */
10111 if (logging != MagickFalse)
10112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10113 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +000010114
glennrp26f37912010-12-23 16:22:42 +000010115 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +000010116 Magick_RenderingIntent_to_PNG_RenderingIntent(
10117 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +000010118 }
cristy3ed852e2009-09-05 21:47:34 +000010119 }
glennrp26f37912010-12-23 16:22:42 +000010120
glennrp5af765f2010-03-30 11:12:18 +000010121 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +000010122#endif
10123 {
glennrp2cc891a2010-12-24 13:44:32 +000010124 if (ping_exclude_gAMA == MagickFalse &&
10125 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +000010126 (image->gamma < .45 || image->gamma > .46)))
10127 {
cristy3ed852e2009-09-05 21:47:34 +000010128 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10129 {
10130 /*
10131 Note image gamma.
10132 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10133 */
10134 if (logging != MagickFalse)
10135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10136 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010137
cristy3ed852e2009-09-05 21:47:34 +000010138 png_set_gAMA(ping,ping_info,image->gamma);
10139 }
glennrp26f37912010-12-23 16:22:42 +000010140 }
glennrp2b013e42010-11-24 16:55:50 +000010141
glennrp26f37912010-12-23 16:22:42 +000010142 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010143 {
glennrp26f37912010-12-23 16:22:42 +000010144 if ((mng_info->have_write_global_chrm == 0) &&
10145 (image->chromaticity.red_primary.x != 0.0))
10146 {
10147 /*
10148 Note image chromaticity.
10149 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10150 */
10151 PrimaryInfo
10152 bp,
10153 gp,
10154 rp,
10155 wp;
cristy3ed852e2009-09-05 21:47:34 +000010156
glennrp26f37912010-12-23 16:22:42 +000010157 wp=image->chromaticity.white_point;
10158 rp=image->chromaticity.red_primary;
10159 gp=image->chromaticity.green_primary;
10160 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +000010161
glennrp26f37912010-12-23 16:22:42 +000010162 if (logging != MagickFalse)
10163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10164 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +000010165
glennrp26f37912010-12-23 16:22:42 +000010166 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10167 bp.x,bp.y);
10168 }
10169 }
cristy3ed852e2009-09-05 21:47:34 +000010170 }
glennrpdfd70802010-11-14 01:23:35 +000010171
glennrp5af765f2010-03-30 11:12:18 +000010172 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010173
10174 if (mng_info->write_mng)
10175 png_set_sig_bytes(ping,8);
10176
cristy5d6fc9c2011-12-27 03:10:42 +000010177 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
cristy3ed852e2009-09-05 21:47:34 +000010178
glennrpd6bf1612010-12-17 17:28:54 +000010179 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010180 {
10181 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010182 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010183 {
glennrp5af765f2010-03-30 11:12:18 +000010184 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010185
glennrp5af765f2010-03-30 11:12:18 +000010186 if (ping_bit_depth < 8)
10187 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010188 }
glennrp0fe50b42010-11-16 03:52:51 +000010189
cristy3ed852e2009-09-05 21:47:34 +000010190 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010191 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010192 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010193 }
10194
glennrp0e8ea192010-12-24 18:00:33 +000010195 if (ping_need_colortype_warning != MagickFalse ||
10196 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010197 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010198 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010199 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010200 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010201 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010202 {
10203 if (logging != MagickFalse)
10204 {
glennrp0e8ea192010-12-24 18:00:33 +000010205 if (ping_need_colortype_warning != MagickFalse)
10206 {
10207 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10208 " Image has transparency but tRNS chunk was excluded");
10209 }
10210
cristy3ed852e2009-09-05 21:47:34 +000010211 if (mng_info->write_png_depth)
10212 {
10213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010214 " Defined png:bit-depth=%u, Computed depth=%u",
cristy3ed852e2009-09-05 21:47:34 +000010215 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010216 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010217 }
glennrp0e8ea192010-12-24 18:00:33 +000010218
cristy3ed852e2009-09-05 21:47:34 +000010219 if (mng_info->write_png_colortype)
10220 {
10221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010222 " Defined png:color-type=%u, Computed color type=%u",
cristy3ed852e2009-09-05 21:47:34 +000010223 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010224 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010225 }
10226 }
glennrp0e8ea192010-12-24 18:00:33 +000010227
glennrp3bd2e412010-08-10 13:34:52 +000010228 png_warning(ping,
cristy5d6fc9c2011-12-27 03:10:42 +000010229 "Cannot write image with defined png:bit-depth or png:color-type.");
cristy3ed852e2009-09-05 21:47:34 +000010230 }
10231
glennrp58e01762011-01-07 15:28:54 +000010232 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010233 {
10234 /* Add an opaque matte channel */
10235 image->matte = MagickTrue;
cristy16ea1392012-03-21 20:38:41 +000010236 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010237
glennrpb4a13412010-05-05 12:47:19 +000010238 if (logging != MagickFalse)
10239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10240 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010241 }
10242
glennrp0e319732011-01-25 21:53:13 +000010243 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010244 {
glennrp991d11d2010-11-12 21:55:28 +000010245 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010246 {
glennrp991d11d2010-11-12 21:55:28 +000010247 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010248 if (logging != MagickFalse)
10249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10250 " Setting ping_have_tRNS=MagickTrue.");
10251 }
glennrpe9c26dc2010-05-30 01:56:35 +000010252 }
10253
cristy3ed852e2009-09-05 21:47:34 +000010254 if (logging != MagickFalse)
10255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10256 " Writing PNG header chunks");
10257
glennrp5af765f2010-03-30 11:12:18 +000010258 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10259 ping_bit_depth,ping_color_type,
10260 ping_interlace_method,ping_compression_method,
10261 ping_filter_method);
10262
glennrp39992b42010-11-14 00:03:43 +000010263 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10264 {
glennrpf09bded2011-01-08 01:15:59 +000010265 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010266
glennrp3b51f0e2010-11-27 18:14:08 +000010267 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010268 {
glennrp8640fb52010-11-23 15:48:26 +000010269 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010270 {
glennrpd6bf1612010-12-17 17:28:54 +000010271 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010273 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10274 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010275 (int) palette[i].red,
10276 (int) palette[i].green,
10277 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010278 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010279 (int) ping_trans_alpha[i]);
10280 else
10281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010282 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010283 (int) i,
10284 (int) palette[i].red,
10285 (int) palette[i].green,
10286 (int) palette[i].blue);
10287 }
glennrp39992b42010-11-14 00:03:43 +000010288 }
glennrp39992b42010-11-14 00:03:43 +000010289 }
10290
glennrp26f37912010-12-23 16:22:42 +000010291 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010292 {
glennrp26f37912010-12-23 16:22:42 +000010293 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010294 {
glennrp26f37912010-12-23 16:22:42 +000010295 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010296 if (logging)
10297 {
10298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10299 " Setting up bKGD chunk");
10300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10301 " background color = (%d,%d,%d)",
10302 (int) ping_background.red,
10303 (int) ping_background.green,
10304 (int) ping_background.blue);
10305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10306 " index = %d, gray=%d",
10307 (int) ping_background.index,
10308 (int) ping_background.gray);
10309 }
10310 }
glennrp26f37912010-12-23 16:22:42 +000010311 }
10312
10313 if (ping_exclude_pHYs == MagickFalse)
10314 {
10315 if (ping_have_pHYs != MagickFalse)
10316 {
10317 png_set_pHYs(ping,ping_info,
10318 ping_pHYs_x_resolution,
10319 ping_pHYs_y_resolution,
10320 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010321
10322 if (logging)
10323 {
10324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10325 " Setting up pHYs chunk");
10326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10327 " x_resolution=%lu",
10328 (unsigned long) ping_pHYs_x_resolution);
10329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10330 " y_resolution=%lu",
10331 (unsigned long) ping_pHYs_y_resolution);
10332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10333 " unit_type=%lu",
10334 (unsigned long) ping_pHYs_unit_type);
10335 }
glennrp26f37912010-12-23 16:22:42 +000010336 }
glennrpdfd70802010-11-14 01:23:35 +000010337 }
10338
10339#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010340 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010341 {
glennrp26f37912010-12-23 16:22:42 +000010342 if (image->page.x || image->page.y)
10343 {
10344 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10345 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010346
glennrp26f37912010-12-23 16:22:42 +000010347 if (logging != MagickFalse)
10348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10349 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10350 (int) image->page.x, (int) image->page.y);
10351 }
glennrpdfd70802010-11-14 01:23:35 +000010352 }
10353#endif
10354
glennrpda8f3a72011-02-27 23:54:12 +000010355 if (mng_info->need_blob != MagickFalse)
10356 {
cristy16ea1392012-03-21 20:38:41 +000010357 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010358 MagickFalse)
10359 png_error(ping,"WriteBlob Failed");
10360
10361 ping_have_blob=MagickTrue;
10362 }
10363
cristy3ed852e2009-09-05 21:47:34 +000010364 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010365
glennrp39992b42010-11-14 00:03:43 +000010366 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010367 {
glennrp3b51f0e2010-11-27 18:14:08 +000010368 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010369 {
10370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10371 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10372 }
10373
10374 if (ping_color_type == 3)
10375 (void) png_set_tRNS(ping, ping_info,
10376 ping_trans_alpha,
10377 ping_num_trans,
10378 NULL);
10379
10380 else
10381 {
10382 (void) png_set_tRNS(ping, ping_info,
10383 NULL,
10384 0,
10385 &ping_trans_color);
10386
glennrp3b51f0e2010-11-27 18:14:08 +000010387 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010388 {
10389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010390 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010391 (int) ping_trans_color.red,
10392 (int) ping_trans_color.green,
10393 (int) ping_trans_color.blue);
10394 }
10395 }
glennrp991d11d2010-11-12 21:55:28 +000010396 }
10397
cristy3ed852e2009-09-05 21:47:34 +000010398 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010399 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010400
cristy3ed852e2009-09-05 21:47:34 +000010401 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010402
cristy3ed852e2009-09-05 21:47:34 +000010403 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010404 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010405
glennrp26f37912010-12-23 16:22:42 +000010406 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010407 {
glennrp4f25bd02011-01-01 18:51:28 +000010408 if ((image->page.width != 0 && image->page.width != image->columns) ||
10409 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010410 {
10411 unsigned char
10412 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010413
glennrp26f37912010-12-23 16:22:42 +000010414 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10415 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010416 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010417 PNGLong(chunk+4,(png_uint_32) image->page.width);
10418 PNGLong(chunk+8,(png_uint_32) image->page.height);
10419 chunk[12]=0; /* unit = pixels */
10420 (void) WriteBlob(image,13,chunk);
10421 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10422 }
cristy3ed852e2009-09-05 21:47:34 +000010423 }
10424
10425#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010426 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010427#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010428 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010429#undef PNG_HAVE_IDAT
10430#endif
10431
10432 png_set_packing(ping);
10433 /*
10434 Allocate memory.
10435 */
10436 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010437 if (image_depth > 8)
10438 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010439 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010440 {
glennrpb4a13412010-05-05 12:47:19 +000010441 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010442 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010443 break;
glennrp0fe50b42010-11-16 03:52:51 +000010444
glennrpb4a13412010-05-05 12:47:19 +000010445 case PNG_COLOR_TYPE_GRAY_ALPHA:
10446 rowbytes*=2;
10447 break;
glennrp0fe50b42010-11-16 03:52:51 +000010448
glennrpb4a13412010-05-05 12:47:19 +000010449 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010450 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010451 break;
glennrp0fe50b42010-11-16 03:52:51 +000010452
glennrpb4a13412010-05-05 12:47:19 +000010453 default:
10454 break;
cristy3ed852e2009-09-05 21:47:34 +000010455 }
glennrp3b51f0e2010-11-27 18:14:08 +000010456
10457 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010458 {
10459 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10460 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010461
glennrpb4a13412010-05-05 12:47:19 +000010462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010463 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010464 }
glennrpcf002022011-01-30 02:38:15 +000010465 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10466 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010467
glennrpcf002022011-01-30 02:38:15 +000010468 if (ping_pixels == (unsigned char *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010469 png_error(ping,"Allocation of memory for pixels failed");
glennrp0fe50b42010-11-16 03:52:51 +000010470
cristy3ed852e2009-09-05 21:47:34 +000010471 /*
10472 Initialize image scanlines.
10473 */
cristyed552522009-10-16 14:04:35 +000010474 quantum_info=AcquireQuantumInfo(image_info,image);
10475 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010476 png_error(ping,"Memory allocation for quantum_info failed");
cristy3ed852e2009-09-05 21:47:34 +000010477 quantum_info->format=UndefinedQuantumFormat;
10478 quantum_info->depth=image_depth;
10479 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010480
cristy3ed852e2009-09-05 21:47:34 +000010481 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010482 !mng_info->write_png32) &&
10483 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010484 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010485 image_matte == MagickFalse &&
10486 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010487 {
glennrp8bb3a022010-12-13 20:40:04 +000010488 /* Palette, Bilevel, or Opaque Monochrome */
cristy16ea1392012-03-21 20:38:41 +000010489 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010490 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010491
cristy3ed852e2009-09-05 21:47:34 +000010492 quantum_info->depth=8;
10493 for (pass=0; pass < num_passes; pass++)
10494 {
10495 /*
10496 Convert PseudoClass image to a PNG monochrome image.
10497 */
cristybb503372010-05-27 20:51:26 +000010498 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010499 {
glennrpd71e86a2011-02-24 01:28:37 +000010500 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10502 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010503
cristy16ea1392012-03-21 20:38:41 +000010504 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010505
cristy16ea1392012-03-21 20:38:41 +000010506 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010507 break;
glennrp0fe50b42010-11-16 03:52:51 +000010508
cristy3ed852e2009-09-05 21:47:34 +000010509 if (mng_info->IsPalette)
10510 {
cristy16ea1392012-03-21 20:38:41 +000010511 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10512 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010513 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10514 mng_info->write_png_depth &&
10515 mng_info->write_png_depth != old_bit_depth)
10516 {
10517 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010518 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010519 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010520 >> (8-old_bit_depth));
10521 }
10522 }
glennrp0fe50b42010-11-16 03:52:51 +000010523
cristy3ed852e2009-09-05 21:47:34 +000010524 else
10525 {
cristy16ea1392012-03-21 20:38:41 +000010526 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10527 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010528 }
glennrp0fe50b42010-11-16 03:52:51 +000010529
cristy3ed852e2009-09-05 21:47:34 +000010530 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010531 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010532 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010533 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010534
glennrp3b51f0e2010-11-27 18:14:08 +000010535 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10537 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010538
glennrpcf002022011-01-30 02:38:15 +000010539 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010540 }
10541 if (image->previous == (Image *) NULL)
10542 {
10543 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10544 if (status == MagickFalse)
10545 break;
10546 }
10547 }
10548 }
glennrp0fe50b42010-11-16 03:52:51 +000010549
glennrp8bb3a022010-12-13 20:40:04 +000010550 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010551 {
glennrp0fe50b42010-11-16 03:52:51 +000010552 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010553 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010554 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010555 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010556 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010557 {
cristy16ea1392012-03-21 20:38:41 +000010558 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010559 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010560
glennrp8bb3a022010-12-13 20:40:04 +000010561 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010562 {
glennrp8bb3a022010-12-13 20:40:04 +000010563
cristybb503372010-05-27 20:51:26 +000010564 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010565 {
cristy16ea1392012-03-21 20:38:41 +000010566 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010567
cristy16ea1392012-03-21 20:38:41 +000010568 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010569 break;
glennrp2cc891a2010-12-24 13:44:32 +000010570
glennrp5af765f2010-03-30 11:12:18 +000010571 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010572 {
glennrp8bb3a022010-12-13 20:40:04 +000010573 if (mng_info->IsPalette)
cristy16ea1392012-03-21 20:38:41 +000010574 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10575 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010576
glennrp8bb3a022010-12-13 20:40:04 +000010577 else
cristy16ea1392012-03-21 20:38:41 +000010578 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10579 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010580
glennrp3b51f0e2010-11-27 18:14:08 +000010581 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010583 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010584 }
glennrp2cc891a2010-12-24 13:44:32 +000010585
glennrp8bb3a022010-12-13 20:40:04 +000010586 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10587 {
10588 if (logging != MagickFalse && y == 0)
10589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10590 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010591
cristy16ea1392012-03-21 20:38:41 +000010592 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10593 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010594 }
glennrp2cc891a2010-12-24 13:44:32 +000010595
glennrp3b51f0e2010-11-27 18:14:08 +000010596 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010598 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010599
glennrpcf002022011-01-30 02:38:15 +000010600 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010601 }
glennrp2cc891a2010-12-24 13:44:32 +000010602
glennrp8bb3a022010-12-13 20:40:04 +000010603 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010604 {
glennrp8bb3a022010-12-13 20:40:04 +000010605 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10606 if (status == MagickFalse)
10607 break;
cristy3ed852e2009-09-05 21:47:34 +000010608 }
cristy3ed852e2009-09-05 21:47:34 +000010609 }
10610 }
glennrp8bb3a022010-12-13 20:40:04 +000010611
10612 else
10613 {
cristy16ea1392012-03-21 20:38:41 +000010614 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010615 *p;
10616
10617 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010618 {
glennrp8bb3a022010-12-13 20:40:04 +000010619 if ((image_depth > 8) || (mng_info->write_png24 ||
10620 mng_info->write_png32 ||
10621 (!mng_info->write_png8 && !mng_info->IsPalette)))
10622 {
10623 for (y=0; y < (ssize_t) image->rows; y++)
10624 {
cristy862a33c2012-05-17 22:49:37 +000010625 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010626
cristy16ea1392012-03-21 20:38:41 +000010627 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010628 break;
glennrp2cc891a2010-12-24 13:44:32 +000010629
glennrp8bb3a022010-12-13 20:40:04 +000010630 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10631 {
10632 if (image->storage_class == DirectClass)
cristy16ea1392012-03-21 20:38:41 +000010633 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10634 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010635
glennrp8bb3a022010-12-13 20:40:04 +000010636 else
cristy16ea1392012-03-21 20:38:41 +000010637 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10638 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010639 }
glennrp2cc891a2010-12-24 13:44:32 +000010640
glennrp8bb3a022010-12-13 20:40:04 +000010641 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10642 {
cristy16ea1392012-03-21 20:38:41 +000010643 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010644 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000010645 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010646
glennrp8bb3a022010-12-13 20:40:04 +000010647 if (logging != MagickFalse && y == 0)
10648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10649 " Writing GRAY_ALPHA PNG pixels (3)");
10650 }
glennrp2cc891a2010-12-24 13:44:32 +000010651
glennrp8bb3a022010-12-13 20:40:04 +000010652 else if (image_matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +000010653 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10654 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010655
glennrp8bb3a022010-12-13 20:40:04 +000010656 else
cristy16ea1392012-03-21 20:38:41 +000010657 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10658 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010659
glennrp8bb3a022010-12-13 20:40:04 +000010660 if (logging != MagickFalse && y == 0)
10661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10662 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010663
glennrpcf002022011-01-30 02:38:15 +000010664 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010665 }
10666 }
glennrp2cc891a2010-12-24 13:44:32 +000010667
glennrp8bb3a022010-12-13 20:40:04 +000010668 else
10669 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10670 mng_info->write_png32 ||
10671 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10672 {
10673 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10674 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10675 {
10676 if (logging != MagickFalse)
10677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10678 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010679
glennrp8bb3a022010-12-13 20:40:04 +000010680 quantum_info->depth=8;
10681 image_depth=8;
10682 }
glennrp2cc891a2010-12-24 13:44:32 +000010683
glennrp8bb3a022010-12-13 20:40:04 +000010684 for (y=0; y < (ssize_t) image->rows; y++)
10685 {
10686 if (logging != MagickFalse && y == 0)
10687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10688 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010689
cristy16ea1392012-03-21 20:38:41 +000010690 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010691
cristy16ea1392012-03-21 20:38:41 +000010692 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010693 break;
glennrp2cc891a2010-12-24 13:44:32 +000010694
glennrp8bb3a022010-12-13 20:40:04 +000010695 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010696 {
glennrp4bf89732011-03-21 13:48:28 +000010697 quantum_info->depth=image->depth;
10698
cristy16ea1392012-03-21 20:38:41 +000010699 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10700 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010701 }
glennrp2cc891a2010-12-24 13:44:32 +000010702
glennrp8bb3a022010-12-13 20:40:04 +000010703 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10704 {
10705 if (logging != MagickFalse && y == 0)
10706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10707 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010708
cristy16ea1392012-03-21 20:38:41 +000010709 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010710 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000010711 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010712 }
glennrp2cc891a2010-12-24 13:44:32 +000010713
glennrp8bb3a022010-12-13 20:40:04 +000010714 else
glennrp8bb3a022010-12-13 20:40:04 +000010715 {
cristy16ea1392012-03-21 20:38:41 +000010716 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10717 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010718
10719 if (logging != MagickFalse && y <= 2)
10720 {
10721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010722 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010723
10724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10725 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10726 (int)ping_pixels[0],(int)ping_pixels[1]);
10727 }
glennrp8bb3a022010-12-13 20:40:04 +000010728 }
glennrpcf002022011-01-30 02:38:15 +000010729 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010730 }
10731 }
glennrp2cc891a2010-12-24 13:44:32 +000010732
glennrp8bb3a022010-12-13 20:40:04 +000010733 if (image->previous == (Image *) NULL)
10734 {
10735 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10736 if (status == MagickFalse)
10737 break;
10738 }
cristy3ed852e2009-09-05 21:47:34 +000010739 }
glennrp8bb3a022010-12-13 20:40:04 +000010740 }
10741 }
10742
cristyb32b90a2009-09-07 21:45:48 +000010743 if (quantum_info != (QuantumInfo *) NULL)
10744 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010745
10746 if (logging != MagickFalse)
10747 {
10748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010749 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010750
cristy3ed852e2009-09-05 21:47:34 +000010751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010752 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010753
cristy3ed852e2009-09-05 21:47:34 +000010754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010755 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010756
cristy3ed852e2009-09-05 21:47:34 +000010757 if (mng_info->write_png_depth)
10758 {
10759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010760 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010761 }
glennrp0fe50b42010-11-16 03:52:51 +000010762
cristy3ed852e2009-09-05 21:47:34 +000010763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010764 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010765
cristy3ed852e2009-09-05 21:47:34 +000010766 if (mng_info->write_png_colortype)
10767 {
10768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010769 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010770 }
glennrp0fe50b42010-11-16 03:52:51 +000010771
cristy3ed852e2009-09-05 21:47:34 +000010772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010773 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010774
cristy3ed852e2009-09-05 21:47:34 +000010775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010776 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010777 }
10778 /*
glennrpa0ed0092011-04-18 16:36:29 +000010779 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010780 */
glennrp823b55c2011-03-14 18:46:46 +000010781 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010782 {
glennrp26f37912010-12-23 16:22:42 +000010783 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010784 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010785 while (property != (const char *) NULL)
10786 {
10787 png_textp
10788 text;
glennrp2cc891a2010-12-24 13:44:32 +000010789
cristy16ea1392012-03-21 20:38:41 +000010790 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010791
10792 /* Don't write any "png:" properties; those are just for "identify" */
10793 if (LocaleNCompare(property,"png:",4) != 0 &&
10794
10795 /* Suppress density and units if we wrote a pHYs chunk */
10796 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010797 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010798 LocaleCompare(property,"units") != 0) &&
10799
10800 /* Suppress the IM-generated Date:create and Date:modify */
10801 (ping_exclude_date == MagickFalse ||
10802 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010803 {
glennrpc70af4a2011-03-07 00:08:23 +000010804 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010805 {
cristya865ccd2012-07-28 00:33:10 +000010806
10807#if PNG_LIBPNG_VER >= 14000
10808 text=(png_textp) png_malloc(ping,
10809 (png_alloc_size_t) sizeof(png_text));
10810#else
10811 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
10812#endif
glennrpc70af4a2011-03-07 00:08:23 +000010813 text[0].key=(char *) property;
10814 text[0].text=(char *) value;
10815 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010816
glennrpc70af4a2011-03-07 00:08:23 +000010817 if (ping_exclude_tEXt != MagickFalse)
10818 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10819
10820 else if (ping_exclude_zTXt != MagickFalse)
10821 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10822
10823 else
glennrp26f37912010-12-23 16:22:42 +000010824 {
glennrpc70af4a2011-03-07 00:08:23 +000010825 text[0].compression=image_info->compression == NoCompression ||
10826 (image_info->compression == UndefinedCompression &&
10827 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10828 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010829 }
glennrp2cc891a2010-12-24 13:44:32 +000010830
glennrpc70af4a2011-03-07 00:08:23 +000010831 if (logging != MagickFalse)
10832 {
10833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10834 " Setting up text chunk");
10835
10836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10837 " keyword: %s",text[0].key);
10838 }
10839
10840 png_set_text(ping,ping_info,text,1);
10841 png_free(ping,text);
10842 }
glennrp26f37912010-12-23 16:22:42 +000010843 }
10844 property=GetNextImageProperty(image);
10845 }
cristy3ed852e2009-09-05 21:47:34 +000010846 }
10847
10848 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010849 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010850
10851 if (logging != MagickFalse)
10852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10853 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010854
cristy3ed852e2009-09-05 21:47:34 +000010855 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010856
cristy3ed852e2009-09-05 21:47:34 +000010857 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10858 {
10859 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010860 (ping_width != mng_info->page.width) ||
10861 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010862 {
10863 unsigned char
10864 chunk[32];
10865
10866 /*
10867 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10868 */
10869 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10870 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010871 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010872 chunk[4]=4;
10873 chunk[5]=0; /* frame name separator (no name) */
10874 chunk[6]=1; /* flag for changing delay, for next frame only */
10875 chunk[7]=0; /* flag for changing frame timeout */
10876 chunk[8]=1; /* flag for changing frame clipping for next frame */
10877 chunk[9]=0; /* flag for changing frame sync_id */
10878 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10879 chunk[14]=0; /* clipping boundaries delta type */
10880 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10881 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010882 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010883 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10884 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010885 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010886 (void) WriteBlob(image,31,chunk);
10887 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10888 mng_info->old_framing_mode=4;
10889 mng_info->framing_mode=1;
10890 }
glennrp0fe50b42010-11-16 03:52:51 +000010891
cristy3ed852e2009-09-05 21:47:34 +000010892 else
10893 mng_info->framing_mode=3;
10894 }
10895 if (mng_info->write_mng && !mng_info->need_fram &&
10896 ((int) image->dispose == 3))
glennrpedaa0382012-04-12 14:16:21 +000010897 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
glennrp0fe50b42010-11-16 03:52:51 +000010898
cristy3ed852e2009-09-05 21:47:34 +000010899 /*
10900 Free PNG resources.
10901 */
glennrp5af765f2010-03-30 11:12:18 +000010902
cristy3ed852e2009-09-05 21:47:34 +000010903 png_destroy_write_struct(&ping,&ping_info);
10904
glennrpcf002022011-01-30 02:38:15 +000010905 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010906
cristy16ea1392012-03-21 20:38:41 +000010907 if (ping_have_blob != MagickFalse)
10908 (void) CloseBlob(image);
10909
10910 image_info=DestroyImageInfo(image_info);
10911 image=DestroyImage(image);
10912
glennrpb9cfe272010-12-21 15:08:06 +000010913 /* Store bit depth actually written */
10914 s[0]=(char) ping_bit_depth;
10915 s[1]='\0';
10916
cristy16ea1392012-03-21 20:38:41 +000010917 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010918
cristy3ed852e2009-09-05 21:47:34 +000010919 if (logging != MagickFalse)
10920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10921 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010922
glennrpedaa0382012-04-12 14:16:21 +000010923#ifdef PNG_SETJMP_NOT_THREAD_SAFE
10924 UnlockSemaphoreInfo(ping_semaphore);
10925#endif
10926
10927 /* } for navigation to beginning of SETJMP-protected block. Revert to
10928 * Throwing an Exception when an error occurs.
10929 */
10930
cristy3ed852e2009-09-05 21:47:34 +000010931 return(MagickTrue);
10932/* End write one PNG image */
glennrpedaa0382012-04-12 14:16:21 +000010933
cristy3ed852e2009-09-05 21:47:34 +000010934}
10935
10936/*
10937%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10938% %
10939% %
10940% %
10941% W r i t e P N G I m a g e %
10942% %
10943% %
10944% %
10945%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10946%
10947% WritePNGImage() writes a Portable Network Graphics (PNG) or
10948% Multiple-image Network Graphics (MNG) image file.
10949%
10950% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10951%
10952% The format of the WritePNGImage method is:
10953%
cristy16ea1392012-03-21 20:38:41 +000010954% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10955% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010956%
10957% A description of each parameter follows:
10958%
10959% o image_info: the image info.
10960%
10961% o image: The image.
10962%
cristy16ea1392012-03-21 20:38:41 +000010963% o exception: return any errors or warnings in this structure.
10964%
cristy3ed852e2009-09-05 21:47:34 +000010965% Returns MagickTrue on success, MagickFalse on failure.
10966%
10967% Communicating with the PNG encoder:
10968%
10969% While the datastream written is always in PNG format and normally would
10970% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000010971% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000010972%
glennrp5a39f372011-02-25 04:52:16 +000010973% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10974% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010975% is present, the tRNS chunk must only have values 0 and 255
10976% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010977% transparent). If other values are present they will be
10978% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010979% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010980% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10981% of any resulting fully-transparent pixels is changed to
10982% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010983%
10984% If you want better quantization or dithering of the colors
10985% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010986% PNG encoder. The pixels contain 8-bit indices even if
10987% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010988% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010989% PNG grayscale type might be slightly more efficient. Please
10990% note that writing to the PNG8 format may result in loss
10991% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010992%
10993% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10994% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010995% one of the colors as transparent. The only loss incurred
10996% is reduction of sample depth to 8. If the image has more
10997% than one transparent color, has semitransparent pixels, or
10998% has an opaque pixel with the same RGB components as the
10999% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000011000%
11001% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11002% transparency is permitted, i.e., the alpha sample for
11003% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000011004% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000011005% The only loss in data is the reduction of the sample depth
11006% to 8.
cristy3ed852e2009-09-05 21:47:34 +000011007%
11008% o -define: For more precise control of the PNG output, you can use the
11009% Image options "png:bit-depth" and "png:color-type". These
11010% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000011011% from the application programming interfaces. The options
11012% are case-independent and are converted to lowercase before
11013% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000011014%
11015% png:color-type can be 0, 2, 3, 4, or 6.
11016%
11017% When png:color-type is 0 (Grayscale), png:bit-depth can
11018% be 1, 2, 4, 8, or 16.
11019%
11020% When png:color-type is 2 (RGB), png:bit-depth can
11021% be 8 or 16.
11022%
11023% When png:color-type is 3 (Indexed), png:bit-depth can
11024% be 1, 2, 4, or 8. This refers to the number of bits
11025% used to store the index. The color samples always have
11026% bit-depth 8 in indexed PNG files.
11027%
11028% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11029% png:bit-depth can be 8 or 16.
11030%
glennrp5a39f372011-02-25 04:52:16 +000011031% If the image cannot be written without loss with the requested bit-depth
11032% and color-type, a PNG file will not be written, and the encoder will
11033% return MagickFalse.
11034%
cristy3ed852e2009-09-05 21:47:34 +000011035% Since image encoders should not be responsible for the "heavy lifting",
11036% the user should make sure that ImageMagick has already reduced the
11037% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000011038% transparency prior to attempting to write the image with depth, color,
cristy16ea1392012-03-21 20:38:41 +000011039% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000011040%
cristy3ed852e2009-09-05 21:47:34 +000011041% Note that another definition, "png:bit-depth-written" exists, but it
11042% is not intended for external use. It is only used internally by the
11043% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11044%
11045% It is possible to request that the PNG encoder write previously-formatted
11046% ancillary chunks in the output PNG file, using the "-profile" commandline
11047% option as shown below or by setting the profile via a programming
11048% interface:
11049%
11050% -profile PNG-chunk-x:<file>
11051%
11052% where x is a location flag and <file> is a file containing the chunk
11053% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000011054% This encoder will compute the chunk length and CRC, so those must not
11055% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000011056%
11057% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11058% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11059% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000011060% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000011061%
glennrpbb8a7332010-11-13 15:17:35 +000011062% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000011063%
glennrp3241bd02010-12-12 04:36:28 +000011064% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000011065%
glennrpd6afd542010-11-19 01:53:05 +000011066% o 32-bit depth is reduced to 16.
11067% o 16-bit depth is reduced to 8 if all pixels contain samples whose
11068% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000011069% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000011070% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000011071% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000011072% o Grayscale images are reduced to 1, 2, or 4 bit depth if
11073% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000011074% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000011075% o If matte channel is present but only one transparent color is
11076% present, RGB+tRNS is written instead of RGBA
11077% o Opaque matte channel is removed (or added, if color-type 4 or 6
11078% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000011079%
cristy3ed852e2009-09-05 21:47:34 +000011080%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11081*/
11082static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy16ea1392012-03-21 20:38:41 +000011083 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011084{
11085 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000011086 excluding,
11087 logging,
11088 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000011089 status;
11090
11091 MngInfo
11092 *mng_info;
11093
11094 const char
11095 *value;
11096
11097 int
glennrp21f0e622011-01-07 16:20:57 +000011098 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000011099 source;
11100
cristy3ed852e2009-09-05 21:47:34 +000011101 /*
11102 Open image file.
11103 */
11104 assert(image_info != (const ImageInfo *) NULL);
11105 assert(image_info->signature == MagickSignature);
11106 assert(image != (Image *) NULL);
11107 assert(image->signature == MagickSignature);
11108 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000011109 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011110 /*
11111 Allocate a MngInfo structure.
11112 */
11113 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000011114 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000011115
cristy3ed852e2009-09-05 21:47:34 +000011116 if (mng_info == (MngInfo *) NULL)
11117 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011118
cristy3ed852e2009-09-05 21:47:34 +000011119 /*
11120 Initialize members of the MngInfo structure.
11121 */
11122 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11123 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000011124 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011125 have_mng_structure=MagickTrue;
11126
11127 /* See if user has requested a specific PNG subformat */
11128
11129 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11130 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11131 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11132
glennrpb381a262012-02-11 17:49:49 +000011133 value=GetImageOption(image_info,"png:format");
11134
11135 if (value != (char *) NULL)
11136 {
11137 if (LocaleCompare(value,"png8") == 0)
11138 {
11139 mng_info->write_png8 = MagickTrue;
11140 mng_info->write_png24 = MagickFalse;
11141 mng_info->write_png32 = MagickFalse;
11142 }
11143
11144 else if (LocaleCompare(value,"png24") == 0)
11145 {
11146 mng_info->write_png8 = MagickFalse;
11147 mng_info->write_png24 = MagickTrue;
11148 mng_info->write_png32 = MagickFalse;
11149 }
11150
11151 else if (LocaleCompare(value,"png32") == 0)
11152 {
11153 mng_info->write_png8 = MagickFalse;
11154 mng_info->write_png24 = MagickFalse;
11155 mng_info->write_png32 = MagickTrue;
11156 }
11157 }
cristy3ed852e2009-09-05 21:47:34 +000011158 if (mng_info->write_png8)
11159 {
glennrp9c1eb072010-06-06 22:19:15 +000011160 mng_info->write_png_colortype = /* 3 */ 4;
11161 mng_info->write_png_depth = 8;
11162 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000011163 }
11164
11165 if (mng_info->write_png24)
11166 {
glennrp9c1eb072010-06-06 22:19:15 +000011167 mng_info->write_png_colortype = /* 2 */ 3;
11168 mng_info->write_png_depth = 8;
11169 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011170
glennrp9c1eb072010-06-06 22:19:15 +000011171 if (image->matte == MagickTrue)
cristy16ea1392012-03-21 20:38:41 +000011172 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011173
glennrp9c1eb072010-06-06 22:19:15 +000011174 else
cristy16ea1392012-03-21 20:38:41 +000011175 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011176
cristy16ea1392012-03-21 20:38:41 +000011177 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011178 }
11179
11180 if (mng_info->write_png32)
11181 {
glennrp9c1eb072010-06-06 22:19:15 +000011182 mng_info->write_png_colortype = /* 6 */ 7;
11183 mng_info->write_png_depth = 8;
11184 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011185
glennrp9c1eb072010-06-06 22:19:15 +000011186 if (image->matte == MagickTrue)
cristy16ea1392012-03-21 20:38:41 +000011187 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011188
glennrp9c1eb072010-06-06 22:19:15 +000011189 else
cristy16ea1392012-03-21 20:38:41 +000011190 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011191
cristy16ea1392012-03-21 20:38:41 +000011192 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011193 }
11194
11195 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011196
cristy3ed852e2009-09-05 21:47:34 +000011197 if (value != (char *) NULL)
11198 {
11199 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011200 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011201
cristy3ed852e2009-09-05 21:47:34 +000011202 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011203 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011204
cristy3ed852e2009-09-05 21:47:34 +000011205 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011206 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011207
cristy3ed852e2009-09-05 21:47:34 +000011208 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011209 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011210
cristy3ed852e2009-09-05 21:47:34 +000011211 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011212 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011213
glennrpbb8a7332010-11-13 15:17:35 +000011214 else
cristy16ea1392012-03-21 20:38:41 +000011215 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011216 GetMagickModule(),CoderWarning,
11217 "ignoring invalid defined png:bit-depth",
11218 "=%s",value);
11219
cristy3ed852e2009-09-05 21:47:34 +000011220 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011222 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011223 }
glennrp0fe50b42010-11-16 03:52:51 +000011224
cristy3ed852e2009-09-05 21:47:34 +000011225 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011226
cristy3ed852e2009-09-05 21:47:34 +000011227 if (value != (char *) NULL)
11228 {
11229 /* We must store colortype+1 because 0 is a valid colortype */
11230 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011231 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011232
cristy16ea1392012-03-21 20:38:41 +000011233 else if (LocaleCompare(value,"1") == 0)
11234 mng_info->write_png_colortype = 2;
11235
cristy3ed852e2009-09-05 21:47:34 +000011236 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011237 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011238
cristy3ed852e2009-09-05 21:47:34 +000011239 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011240 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011241
cristy3ed852e2009-09-05 21:47:34 +000011242 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011243 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011244
cristy3ed852e2009-09-05 21:47:34 +000011245 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011246 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011247
glennrpbb8a7332010-11-13 15:17:35 +000011248 else
cristy16ea1392012-03-21 20:38:41 +000011249 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011250 GetMagickModule(),CoderWarning,
11251 "ignoring invalid defined png:color-type",
11252 "=%s",value);
11253
cristy3ed852e2009-09-05 21:47:34 +000011254 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011256 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011257 }
11258
glennrp0e8ea192010-12-24 18:00:33 +000011259 /* Check for chunks to be excluded:
11260 *
glennrp0dff56c2011-01-29 19:10:02 +000011261 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011262 * listed in the "unused_chunks" array, above.
11263 *
cristy5d6fc9c2011-12-27 03:10:42 +000011264 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011265 * define (in the image properties or in the image artifacts)
11266 * or via a mng_info member. For convenience, in addition
11267 * to or instead of a comma-separated list of chunks, the
11268 * "exclude-chunk" string can be simply "all" or "none".
11269 *
11270 * The exclude-chunk define takes priority over the mng_info.
11271 *
cristy5d6fc9c2011-12-27 03:10:42 +000011272 * A "png:include-chunk" define takes priority over both the
11273 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011274 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011275 * well as a comma-separated list. Chunks that are unknown to
11276 * ImageMagick are always excluded, regardless of their "copy-safe"
11277 * status according to the PNG specification, and even if they
glennrpaa192b12012-01-17 21:35:21 +000011278 * appear in the "include-chunk" list. Such defines appearing among
11279 * the image options take priority over those found among the image
11280 * artifacts.
glennrp0e8ea192010-12-24 18:00:33 +000011281 *
11282 * Finally, all chunks listed in the "unused_chunks" array are
11283 * automatically excluded, regardless of the other instructions
11284 * or lack thereof.
11285 *
11286 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11287 * will not be written and the gAMA chunk will only be written if it
11288 * is not between .45 and .46, or approximately (1.0/2.2).
11289 *
11290 * If you exclude tRNS and the image has transparency, the colortype
11291 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11292 *
11293 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011294 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011295 */
11296
glennrp26f37912010-12-23 16:22:42 +000011297 mng_info->ping_exclude_bKGD=MagickFalse;
11298 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011299 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011300 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11301 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011302 mng_info->ping_exclude_iCCP=MagickFalse;
11303 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11304 mng_info->ping_exclude_oFFs=MagickFalse;
11305 mng_info->ping_exclude_pHYs=MagickFalse;
11306 mng_info->ping_exclude_sRGB=MagickFalse;
11307 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011308 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011309 mng_info->ping_exclude_vpAg=MagickFalse;
11310 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11311 mng_info->ping_exclude_zTXt=MagickFalse;
11312
glennrp8d3d6e52011-04-19 04:39:51 +000011313 mng_info->ping_preserve_colormap=MagickFalse;
11314
11315 value=GetImageArtifact(image,"png:preserve-colormap");
11316 if (value == NULL)
11317 value=GetImageOption(image_info,"png:preserve-colormap");
11318 if (value != NULL)
11319 mng_info->ping_preserve_colormap=MagickTrue;
11320
glennrp18682582011-06-30 18:11:47 +000011321 /* Thes compression-level, compression-strategy, and compression-filter
11322 * defines take precedence over values from the -quality option.
11323 */
11324 value=GetImageArtifact(image,"png:compression-level");
11325 if (value == NULL)
11326 value=GetImageOption(image_info,"png:compression-level");
11327 if (value != NULL)
11328 {
glennrp18682582011-06-30 18:11:47 +000011329 /* We have to add 1 to everything because 0 is a valid input,
11330 * and we want to use 0 (the default) to mean undefined.
11331 */
11332 if (LocaleCompare(value,"0") == 0)
11333 mng_info->write_png_compression_level = 1;
11334
glennrp0ffb95c2012-01-30 21:16:22 +000011335 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011336 mng_info->write_png_compression_level = 2;
11337
11338 else if (LocaleCompare(value,"2") == 0)
11339 mng_info->write_png_compression_level = 3;
11340
11341 else if (LocaleCompare(value,"3") == 0)
11342 mng_info->write_png_compression_level = 4;
11343
11344 else if (LocaleCompare(value,"4") == 0)
11345 mng_info->write_png_compression_level = 5;
11346
11347 else if (LocaleCompare(value,"5") == 0)
11348 mng_info->write_png_compression_level = 6;
11349
11350 else if (LocaleCompare(value,"6") == 0)
11351 mng_info->write_png_compression_level = 7;
11352
11353 else if (LocaleCompare(value,"7") == 0)
11354 mng_info->write_png_compression_level = 8;
11355
11356 else if (LocaleCompare(value,"8") == 0)
11357 mng_info->write_png_compression_level = 9;
11358
11359 else if (LocaleCompare(value,"9") == 0)
11360 mng_info->write_png_compression_level = 10;
11361
11362 else
cristy16ea1392012-03-21 20:38:41 +000011363 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011364 GetMagickModule(),CoderWarning,
11365 "ignoring invalid defined png:compression-level",
11366 "=%s",value);
11367 }
11368
11369 value=GetImageArtifact(image,"png:compression-strategy");
11370 if (value == NULL)
11371 value=GetImageOption(image_info,"png:compression-strategy");
11372 if (value != NULL)
11373 {
11374
11375 if (LocaleCompare(value,"0") == 0)
11376 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11377
11378 else if (LocaleCompare(value,"1") == 0)
11379 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11380
11381 else if (LocaleCompare(value,"2") == 0)
11382 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11383
11384 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011385#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011386 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011387#else
11388 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11389#endif
glennrp18682582011-06-30 18:11:47 +000011390
11391 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011392#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011393 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011394#else
11395 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11396#endif
glennrp18682582011-06-30 18:11:47 +000011397
11398 else
cristy16ea1392012-03-21 20:38:41 +000011399 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011400 GetMagickModule(),CoderWarning,
11401 "ignoring invalid defined png:compression-strategy",
11402 "=%s",value);
11403 }
11404
11405 value=GetImageArtifact(image,"png:compression-filter");
11406 if (value == NULL)
11407 value=GetImageOption(image_info,"png:compression-filter");
11408 if (value != NULL)
11409 {
11410
11411 /* To do: combinations of filters allowed by libpng
11412 * masks 0x08 through 0xf8
11413 *
11414 * Implement this as a comma-separated list of 0,1,2,3,4,5
11415 * where 5 is a special case meaning PNG_ALL_FILTERS.
11416 */
11417
11418 if (LocaleCompare(value,"0") == 0)
11419 mng_info->write_png_compression_filter = 1;
11420
11421 if (LocaleCompare(value,"1") == 0)
11422 mng_info->write_png_compression_filter = 2;
11423
11424 else if (LocaleCompare(value,"2") == 0)
11425 mng_info->write_png_compression_filter = 3;
11426
11427 else if (LocaleCompare(value,"3") == 0)
11428 mng_info->write_png_compression_filter = 4;
11429
11430 else if (LocaleCompare(value,"4") == 0)
11431 mng_info->write_png_compression_filter = 5;
11432
11433 else if (LocaleCompare(value,"5") == 0)
11434 mng_info->write_png_compression_filter = 6;
11435
glennrp18682582011-06-30 18:11:47 +000011436 else
cristy16ea1392012-03-21 20:38:41 +000011437 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011438 GetMagickModule(),CoderWarning,
11439 "ignoring invalid defined png:compression-filter",
11440 "=%s",value);
11441 }
11442
glennrp03812ae2010-12-24 01:31:34 +000011443 excluding=MagickFalse;
11444
glennrp5c7cf4e2010-12-24 00:30:00 +000011445 for (source=0; source<1; source++)
11446 {
11447 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011448 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011449 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011450
11451 if (value == NULL)
11452 value=GetImageArtifact(image,"png:exclude-chunks");
11453 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011454 else
glennrpacba0042010-12-24 14:27:26 +000011455 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011456 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011457
glennrpacba0042010-12-24 14:27:26 +000011458 if (value == NULL)
11459 value=GetImageOption(image_info,"png:exclude-chunks");
11460 }
11461
glennrp03812ae2010-12-24 01:31:34 +000011462 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011463 {
glennrp03812ae2010-12-24 01:31:34 +000011464
11465 size_t
11466 last;
11467
11468 excluding=MagickTrue;
11469
11470 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011471 {
11472 if (source == 0)
11473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11474 " png:exclude-chunk=%s found in image artifacts.\n", value);
11475 else
11476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11477 " png:exclude-chunk=%s found in image properties.\n", value);
11478 }
glennrp03812ae2010-12-24 01:31:34 +000011479
11480 last=strlen(value);
11481
11482 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011483 {
glennrp03812ae2010-12-24 01:31:34 +000011484
11485 if (LocaleNCompare(value+i,"all",3) == 0)
11486 {
11487 mng_info->ping_exclude_bKGD=MagickTrue;
11488 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011489 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011490 mng_info->ping_exclude_EXIF=MagickTrue;
11491 mng_info->ping_exclude_gAMA=MagickTrue;
11492 mng_info->ping_exclude_iCCP=MagickTrue;
11493 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11494 mng_info->ping_exclude_oFFs=MagickTrue;
11495 mng_info->ping_exclude_pHYs=MagickTrue;
11496 mng_info->ping_exclude_sRGB=MagickTrue;
11497 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011498 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011499 mng_info->ping_exclude_vpAg=MagickTrue;
11500 mng_info->ping_exclude_zCCP=MagickTrue;
11501 mng_info->ping_exclude_zTXt=MagickTrue;
11502 i--;
11503 }
glennrp2cc891a2010-12-24 13:44:32 +000011504
glennrp03812ae2010-12-24 01:31:34 +000011505 if (LocaleNCompare(value+i,"none",4) == 0)
11506 {
11507 mng_info->ping_exclude_bKGD=MagickFalse;
11508 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011509 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011510 mng_info->ping_exclude_EXIF=MagickFalse;
11511 mng_info->ping_exclude_gAMA=MagickFalse;
11512 mng_info->ping_exclude_iCCP=MagickFalse;
11513 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11514 mng_info->ping_exclude_oFFs=MagickFalse;
11515 mng_info->ping_exclude_pHYs=MagickFalse;
11516 mng_info->ping_exclude_sRGB=MagickFalse;
11517 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011518 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011519 mng_info->ping_exclude_vpAg=MagickFalse;
11520 mng_info->ping_exclude_zCCP=MagickFalse;
11521 mng_info->ping_exclude_zTXt=MagickFalse;
11522 }
glennrp2cc891a2010-12-24 13:44:32 +000011523
glennrp03812ae2010-12-24 01:31:34 +000011524 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11525 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011526
glennrp03812ae2010-12-24 01:31:34 +000011527 if (LocaleNCompare(value+i,"chrm",4) == 0)
11528 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011529
glennrpa0ed0092011-04-18 16:36:29 +000011530 if (LocaleNCompare(value+i,"date",4) == 0)
11531 mng_info->ping_exclude_date=MagickTrue;
11532
glennrp03812ae2010-12-24 01:31:34 +000011533 if (LocaleNCompare(value+i,"exif",4) == 0)
11534 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011535
glennrp03812ae2010-12-24 01:31:34 +000011536 if (LocaleNCompare(value+i,"gama",4) == 0)
11537 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011538
glennrp03812ae2010-12-24 01:31:34 +000011539 if (LocaleNCompare(value+i,"iccp",4) == 0)
11540 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011541
glennrp03812ae2010-12-24 01:31:34 +000011542 /*
11543 if (LocaleNCompare(value+i,"itxt",4) == 0)
11544 mng_info->ping_exclude_iTXt=MagickTrue;
11545 */
glennrp2cc891a2010-12-24 13:44:32 +000011546
glennrp03812ae2010-12-24 01:31:34 +000011547 if (LocaleNCompare(value+i,"gama",4) == 0)
11548 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011549
glennrp03812ae2010-12-24 01:31:34 +000011550 if (LocaleNCompare(value+i,"offs",4) == 0)
11551 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011552
glennrp03812ae2010-12-24 01:31:34 +000011553 if (LocaleNCompare(value+i,"phys",4) == 0)
11554 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011555
glennrpa1e3b7b2010-12-24 16:37:33 +000011556 if (LocaleNCompare(value+i,"srgb",4) == 0)
11557 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011558
glennrp03812ae2010-12-24 01:31:34 +000011559 if (LocaleNCompare(value+i,"text",4) == 0)
11560 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011561
glennrpa1e3b7b2010-12-24 16:37:33 +000011562 if (LocaleNCompare(value+i,"trns",4) == 0)
11563 mng_info->ping_exclude_tRNS=MagickTrue;
11564
glennrp03812ae2010-12-24 01:31:34 +000011565 if (LocaleNCompare(value+i,"vpag",4) == 0)
11566 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011567
glennrp03812ae2010-12-24 01:31:34 +000011568 if (LocaleNCompare(value+i,"zccp",4) == 0)
11569 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011570
glennrp03812ae2010-12-24 01:31:34 +000011571 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11572 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011573
glennrp03812ae2010-12-24 01:31:34 +000011574 }
glennrpce91ed52010-12-23 22:37:49 +000011575 }
glennrp26f37912010-12-23 16:22:42 +000011576 }
11577
glennrp5c7cf4e2010-12-24 00:30:00 +000011578 for (source=0; source<1; source++)
11579 {
11580 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011581 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011582 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011583
11584 if (value == NULL)
11585 value=GetImageArtifact(image,"png:include-chunks");
11586 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011587 else
glennrpacba0042010-12-24 14:27:26 +000011588 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011589 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011590
glennrpacba0042010-12-24 14:27:26 +000011591 if (value == NULL)
11592 value=GetImageOption(image_info,"png:include-chunks");
11593 }
11594
glennrp03812ae2010-12-24 01:31:34 +000011595 if (value != NULL)
11596 {
11597 size_t
11598 last;
glennrp26f37912010-12-23 16:22:42 +000011599
glennrp03812ae2010-12-24 01:31:34 +000011600 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011601
glennrp03812ae2010-12-24 01:31:34 +000011602 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011603 {
11604 if (source == 0)
11605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11606 " png:include-chunk=%s found in image artifacts.\n", value);
11607 else
11608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11609 " png:include-chunk=%s found in image properties.\n", value);
11610 }
glennrp03812ae2010-12-24 01:31:34 +000011611
11612 last=strlen(value);
11613
11614 for (i=0; i<(int) last; i+=5)
11615 {
11616 if (LocaleNCompare(value+i,"all",3) == 0)
11617 {
11618 mng_info->ping_exclude_bKGD=MagickFalse;
11619 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011620 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011621 mng_info->ping_exclude_EXIF=MagickFalse;
11622 mng_info->ping_exclude_gAMA=MagickFalse;
11623 mng_info->ping_exclude_iCCP=MagickFalse;
11624 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11625 mng_info->ping_exclude_oFFs=MagickFalse;
11626 mng_info->ping_exclude_pHYs=MagickFalse;
11627 mng_info->ping_exclude_sRGB=MagickFalse;
11628 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011629 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011630 mng_info->ping_exclude_vpAg=MagickFalse;
11631 mng_info->ping_exclude_zCCP=MagickFalse;
11632 mng_info->ping_exclude_zTXt=MagickFalse;
11633 i--;
11634 }
glennrp2cc891a2010-12-24 13:44:32 +000011635
glennrp03812ae2010-12-24 01:31:34 +000011636 if (LocaleNCompare(value+i,"none",4) == 0)
11637 {
11638 mng_info->ping_exclude_bKGD=MagickTrue;
11639 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011640 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011641 mng_info->ping_exclude_EXIF=MagickTrue;
11642 mng_info->ping_exclude_gAMA=MagickTrue;
11643 mng_info->ping_exclude_iCCP=MagickTrue;
11644 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11645 mng_info->ping_exclude_oFFs=MagickTrue;
11646 mng_info->ping_exclude_pHYs=MagickTrue;
11647 mng_info->ping_exclude_sRGB=MagickTrue;
11648 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011649 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011650 mng_info->ping_exclude_vpAg=MagickTrue;
11651 mng_info->ping_exclude_zCCP=MagickTrue;
11652 mng_info->ping_exclude_zTXt=MagickTrue;
11653 }
glennrp2cc891a2010-12-24 13:44:32 +000011654
glennrp03812ae2010-12-24 01:31:34 +000011655 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11656 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011657
glennrp03812ae2010-12-24 01:31:34 +000011658 if (LocaleNCompare(value+i,"chrm",4) == 0)
11659 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011660
glennrpa0ed0092011-04-18 16:36:29 +000011661 if (LocaleNCompare(value+i,"date",4) == 0)
11662 mng_info->ping_exclude_date=MagickFalse;
11663
glennrp03812ae2010-12-24 01:31:34 +000011664 if (LocaleNCompare(value+i,"exif",4) == 0)
11665 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011666
glennrp03812ae2010-12-24 01:31:34 +000011667 if (LocaleNCompare(value+i,"gama",4) == 0)
11668 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011669
glennrp03812ae2010-12-24 01:31:34 +000011670 if (LocaleNCompare(value+i,"iccp",4) == 0)
11671 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011672
glennrp03812ae2010-12-24 01:31:34 +000011673 /*
11674 if (LocaleNCompare(value+i,"itxt",4) == 0)
11675 mng_info->ping_exclude_iTXt=MagickFalse;
11676 */
glennrp2cc891a2010-12-24 13:44:32 +000011677
glennrp03812ae2010-12-24 01:31:34 +000011678 if (LocaleNCompare(value+i,"gama",4) == 0)
11679 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011680
glennrp03812ae2010-12-24 01:31:34 +000011681 if (LocaleNCompare(value+i,"offs",4) == 0)
11682 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011683
glennrp03812ae2010-12-24 01:31:34 +000011684 if (LocaleNCompare(value+i,"phys",4) == 0)
11685 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011686
glennrpa1e3b7b2010-12-24 16:37:33 +000011687 if (LocaleNCompare(value+i,"srgb",4) == 0)
11688 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011689
glennrp03812ae2010-12-24 01:31:34 +000011690 if (LocaleNCompare(value+i,"text",4) == 0)
11691 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011692
glennrpa1e3b7b2010-12-24 16:37:33 +000011693 if (LocaleNCompare(value+i,"trns",4) == 0)
11694 mng_info->ping_exclude_tRNS=MagickFalse;
11695
glennrp03812ae2010-12-24 01:31:34 +000011696 if (LocaleNCompare(value+i,"vpag",4) == 0)
11697 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011698
glennrp03812ae2010-12-24 01:31:34 +000011699 if (LocaleNCompare(value+i,"zccp",4) == 0)
11700 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011701
glennrp03812ae2010-12-24 01:31:34 +000011702 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11703 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011704
glennrp03812ae2010-12-24 01:31:34 +000011705 }
glennrpce91ed52010-12-23 22:37:49 +000011706 }
glennrp26f37912010-12-23 16:22:42 +000011707 }
11708
glennrp03812ae2010-12-24 01:31:34 +000011709 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011710 {
11711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011712 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011713 if (mng_info->ping_exclude_bKGD != MagickFalse)
11714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11715 " bKGD");
11716 if (mng_info->ping_exclude_cHRM != MagickFalse)
11717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11718 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011719 if (mng_info->ping_exclude_date != MagickFalse)
11720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11721 " date");
glennrp26f37912010-12-23 16:22:42 +000011722 if (mng_info->ping_exclude_EXIF != MagickFalse)
11723 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11724 " EXIF");
11725 if (mng_info->ping_exclude_gAMA != MagickFalse)
11726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11727 " gAMA");
11728 if (mng_info->ping_exclude_iCCP != MagickFalse)
11729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11730 " iCCP");
11731/*
11732 if (mng_info->ping_exclude_iTXt != MagickFalse)
11733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11734 " iTXt");
11735*/
11736 if (mng_info->ping_exclude_oFFs != MagickFalse)
11737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11738 " oFFs");
11739 if (mng_info->ping_exclude_pHYs != MagickFalse)
11740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11741 " pHYs");
11742 if (mng_info->ping_exclude_sRGB != MagickFalse)
11743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11744 " sRGB");
11745 if (mng_info->ping_exclude_tEXt != MagickFalse)
11746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11747 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011748 if (mng_info->ping_exclude_tRNS != MagickFalse)
11749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11750 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011751 if (mng_info->ping_exclude_vpAg != MagickFalse)
11752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11753 " vpAg");
11754 if (mng_info->ping_exclude_zCCP != MagickFalse)
11755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11756 " zCCP");
11757 if (mng_info->ping_exclude_zTXt != MagickFalse)
11758 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11759 " zTXt");
11760 }
11761
glennrpb9cfe272010-12-21 15:08:06 +000011762 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011763
cristy16ea1392012-03-21 20:38:41 +000011764 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011765
11766 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011767
cristy3ed852e2009-09-05 21:47:34 +000011768 if (logging != MagickFalse)
11769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011770
cristy3ed852e2009-09-05 21:47:34 +000011771 return(status);
11772}
11773
11774#if defined(JNG_SUPPORTED)
11775
11776/* Write one JNG image */
11777static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +000011778 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011779{
11780 Image
11781 *jpeg_image;
11782
11783 ImageInfo
11784 *jpeg_image_info;
11785
11786 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011787 logging,
cristy3ed852e2009-09-05 21:47:34 +000011788 status;
11789
11790 size_t
11791 length;
11792
11793 unsigned char
11794 *blob,
11795 chunk[80],
11796 *p;
11797
11798 unsigned int
11799 jng_alpha_compression_method,
11800 jng_alpha_sample_depth,
11801 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011802 transparent;
11803
cristybb503372010-05-27 20:51:26 +000011804 size_t
glennrp59575fa2011-12-31 21:31:39 +000011805 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000011806 jng_quality;
11807
11808 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011809 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011810
11811 blob=(unsigned char *) NULL;
11812 jpeg_image=(Image *) NULL;
11813 jpeg_image_info=(ImageInfo *) NULL;
11814
11815 status=MagickTrue;
11816 transparent=image_info->type==GrayscaleMatteType ||
glennrp59575fa2011-12-31 21:31:39 +000011817 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +000011818
glennrp59575fa2011-12-31 21:31:39 +000011819 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11820
11821 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11822
glennrp750105b2012-04-25 16:20:45 +000011823 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
glennrp59575fa2011-12-31 21:31:39 +000011824 image_info->quality;
11825
11826 if (jng_alpha_quality >= 1000)
11827 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000011828
11829 if (transparent)
11830 {
11831 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011832
cristy3ed852e2009-09-05 21:47:34 +000011833 /* Create JPEG blob, image, and image_info */
11834 if (logging != MagickFalse)
11835 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +000011836 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011837
cristy3ed852e2009-09-05 21:47:34 +000011838 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011839
cristy3ed852e2009-09-05 21:47:34 +000011840 if (jpeg_image_info == (ImageInfo *) NULL)
11841 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011842
cristy3ed852e2009-09-05 21:47:34 +000011843 if (logging != MagickFalse)
11844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11845 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011846
cristy16ea1392012-03-21 20:38:41 +000011847 jpeg_image=SeparateImage(image,AlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +000011848 if (jpeg_image == (Image *) NULL)
11849 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11850 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000011851 jpeg_image->matte=MagickFalse;
glennrp8f77fdc2012-03-21 15:16:37 +000011852 jpeg_image->quality=jng_alpha_quality;
cristy16ea1392012-03-21 20:38:41 +000011853 jpeg_image_info->type=GrayscaleType;
11854 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011855 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011856 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011857 "%s",jpeg_image->filename);
11858 }
glennrp59575fa2011-12-31 21:31:39 +000011859 else
11860 {
11861 jng_alpha_compression_method=0;
11862 jng_color_type=10;
11863 jng_alpha_sample_depth=0;
11864 }
cristy3ed852e2009-09-05 21:47:34 +000011865
11866 /* To do: check bit depth of PNG alpha channel */
11867
11868 /* Check if image is grayscale. */
11869 if (image_info->type != TrueColorMatteType && image_info->type !=
cristy7fb26522012-06-21 13:02:48 +000011870 TrueColorType && IsImageGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011871 jng_color_type-=2;
11872
glennrp59575fa2011-12-31 21:31:39 +000011873 if (logging != MagickFalse)
11874 {
11875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11876 " JNG Quality = %d",(int) jng_quality);
11877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11878 " JNG Color Type = %d",jng_color_type);
11879 if (transparent)
11880 {
11881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11882 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11884 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11886 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11887 }
11888 }
11889
cristy3ed852e2009-09-05 21:47:34 +000011890 if (transparent)
11891 {
11892 if (jng_alpha_compression_method==0)
11893 {
11894 const char
11895 *value;
11896
cristy16ea1392012-03-21 20:38:41 +000011897 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011898 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000011899 exception);
cristy3ed852e2009-09-05 21:47:34 +000011900 if (logging != MagickFalse)
11901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11902 " Creating PNG blob.");
11903 length=0;
11904
11905 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11906 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11907 jpeg_image_info->interlace=NoInterlace;
11908
glennrpcc5d45b2012-01-06 04:06:10 +000011909 /* Exclude all ancillary chunks */
11910 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11911
cristy3ed852e2009-09-05 21:47:34 +000011912 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000011913 exception);
cristy3ed852e2009-09-05 21:47:34 +000011914
11915 /* Retrieve sample depth used */
cristy16ea1392012-03-21 20:38:41 +000011916 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011917 if (value != (char *) NULL)
11918 jng_alpha_sample_depth= (unsigned int) value[0];
11919 }
11920 else
11921 {
cristy16ea1392012-03-21 20:38:41 +000011922 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011923
11924 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000011925 exception);
cristy3ed852e2009-09-05 21:47:34 +000011926
11927 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11928 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11929 jpeg_image_info->interlace=NoInterlace;
11930 if (logging != MagickFalse)
11931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11932 " Creating blob.");
11933 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000011934 exception);
cristy3ed852e2009-09-05 21:47:34 +000011935 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011936
cristy3ed852e2009-09-05 21:47:34 +000011937 if (logging != MagickFalse)
11938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011939 " Successfully read jpeg_image into a blob, length=%.20g.",
11940 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011941
11942 }
11943 /* Destroy JPEG image and image_info */
11944 jpeg_image=DestroyImage(jpeg_image);
11945 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11946 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11947 }
11948
11949 /* Write JHDR chunk */
11950 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11951 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011952 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011953 PNGLong(chunk+4,(png_uint_32) image->columns);
11954 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011955 chunk[12]=jng_color_type;
11956 chunk[13]=8; /* sample depth */
11957 chunk[14]=8; /*jng_image_compression_method */
11958 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11959 chunk[16]=jng_alpha_sample_depth;
11960 chunk[17]=jng_alpha_compression_method;
11961 chunk[18]=0; /*jng_alpha_filter_method */
11962 chunk[19]=0; /*jng_alpha_interlace_method */
11963 (void) WriteBlob(image,20,chunk);
11964 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11965 if (logging != MagickFalse)
11966 {
11967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011968 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011969
cristy3ed852e2009-09-05 21:47:34 +000011970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011971 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011972
cristy3ed852e2009-09-05 21:47:34 +000011973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11974 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011975
cristy3ed852e2009-09-05 21:47:34 +000011976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11977 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011978
cristy3ed852e2009-09-05 21:47:34 +000011979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11980 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011981
cristy3ed852e2009-09-05 21:47:34 +000011982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11983 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011984
cristy3ed852e2009-09-05 21:47:34 +000011985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11986 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011987
cristy3ed852e2009-09-05 21:47:34 +000011988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11989 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011990
cristy3ed852e2009-09-05 21:47:34 +000011991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11992 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011993
cristy3ed852e2009-09-05 21:47:34 +000011994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11995 " JNG alpha interlace:%5d",0);
11996 }
11997
glennrp0fe50b42010-11-16 03:52:51 +000011998 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011999 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000012000
12001 /*
12002 Write leading ancillary chunks
12003 */
12004
12005 if (transparent)
12006 {
12007 /*
12008 Write JNG bKGD chunk
12009 */
12010
12011 unsigned char
12012 blue,
12013 green,
12014 red;
12015
cristybb503372010-05-27 20:51:26 +000012016 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012017 num_bytes;
12018
12019 if (jng_color_type == 8 || jng_color_type == 12)
12020 num_bytes=6L;
12021 else
12022 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000012023 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012024 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012025 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000012026 red=ScaleQuantumToChar(image->background_color.red);
12027 green=ScaleQuantumToChar(image->background_color.green);
12028 blue=ScaleQuantumToChar(image->background_color.blue);
12029 *(chunk+4)=0;
12030 *(chunk+5)=red;
12031 *(chunk+6)=0;
12032 *(chunk+7)=green;
12033 *(chunk+8)=0;
12034 *(chunk+9)=blue;
12035 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12036 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12037 }
12038
12039 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12040 {
12041 /*
12042 Write JNG sRGB chunk
12043 */
12044 (void) WriteBlobMSBULong(image,1L);
12045 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012046 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012047
cristy3ed852e2009-09-05 21:47:34 +000012048 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012049 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012050 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012051 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012052
cristy3ed852e2009-09-05 21:47:34 +000012053 else
glennrpe610a072010-08-05 17:08:46 +000012054 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012055 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012056 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012057
cristy3ed852e2009-09-05 21:47:34 +000012058 (void) WriteBlob(image,5,chunk);
12059 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12060 }
12061 else
12062 {
12063 if (image->gamma != 0.0)
12064 {
12065 /*
12066 Write JNG gAMA chunk
12067 */
12068 (void) WriteBlobMSBULong(image,4L);
12069 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012070 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012071 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012072 (void) WriteBlob(image,8,chunk);
12073 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12074 }
glennrp0fe50b42010-11-16 03:52:51 +000012075
cristy3ed852e2009-09-05 21:47:34 +000012076 if ((mng_info->equal_chrms == MagickFalse) &&
12077 (image->chromaticity.red_primary.x != 0.0))
12078 {
12079 PrimaryInfo
12080 primary;
12081
12082 /*
12083 Write JNG cHRM chunk
12084 */
12085 (void) WriteBlobMSBULong(image,32L);
12086 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012087 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012088 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012089 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12090 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012091 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012092 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12093 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012094 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012095 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12096 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012097 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012098 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12099 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012100 (void) WriteBlob(image,36,chunk);
12101 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12102 }
12103 }
glennrp0fe50b42010-11-16 03:52:51 +000012104
cristy16ea1392012-03-21 20:38:41 +000012105 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012106 {
12107 /*
12108 Write JNG pHYs chunk
12109 */
12110 (void) WriteBlobMSBULong(image,9L);
12111 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012112 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000012113 if (image->units == PixelsPerInchResolution)
12114 {
cristy35ef8242010-06-03 16:24:13 +000012115 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012116 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012117
cristy35ef8242010-06-03 16:24:13 +000012118 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012119 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012120
cristy3ed852e2009-09-05 21:47:34 +000012121 chunk[12]=1;
12122 }
glennrp0fe50b42010-11-16 03:52:51 +000012123
cristy3ed852e2009-09-05 21:47:34 +000012124 else
12125 {
12126 if (image->units == PixelsPerCentimeterResolution)
12127 {
cristy35ef8242010-06-03 16:24:13 +000012128 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012129 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012130
cristy35ef8242010-06-03 16:24:13 +000012131 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012132 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012133
cristy3ed852e2009-09-05 21:47:34 +000012134 chunk[12]=1;
12135 }
glennrp0fe50b42010-11-16 03:52:51 +000012136
cristy3ed852e2009-09-05 21:47:34 +000012137 else
12138 {
cristy16ea1392012-03-21 20:38:41 +000012139 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12140 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012141 chunk[12]=0;
12142 }
12143 }
12144 (void) WriteBlob(image,13,chunk);
12145 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12146 }
12147
12148 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12149 {
12150 /*
12151 Write JNG oFFs chunk
12152 */
12153 (void) WriteBlobMSBULong(image,9L);
12154 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000012155 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000012156 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12157 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000012158 chunk[12]=0;
12159 (void) WriteBlob(image,13,chunk);
12160 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12161 }
12162 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12163 {
12164 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12165 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000012166 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000012167 PNGLong(chunk+4,(png_uint_32) image->page.width);
12168 PNGLong(chunk+8,(png_uint_32) image->page.height);
12169 chunk[12]=0; /* unit = pixels */
12170 (void) WriteBlob(image,13,chunk);
12171 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12172 }
12173
12174
12175 if (transparent)
12176 {
12177 if (jng_alpha_compression_method==0)
12178 {
cristybb503372010-05-27 20:51:26 +000012179 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012180 i;
12181
cristybb503372010-05-27 20:51:26 +000012182 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012183 len;
12184
12185 /* Write IDAT chunk header */
12186 if (logging != MagickFalse)
12187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012188 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012189 length);
cristy3ed852e2009-09-05 21:47:34 +000012190
12191 /* Copy IDAT chunks */
12192 len=0;
12193 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012194 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012195 {
12196 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12197 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012198
cristy3ed852e2009-09-05 21:47:34 +000012199 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12200 {
12201 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012202 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012203 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012204 (void) WriteBlob(image,(size_t) len+4,p);
12205 (void) WriteBlobMSBULong(image,
12206 crc32(0,p,(uInt) len+4));
12207 }
glennrp0fe50b42010-11-16 03:52:51 +000012208
cristy3ed852e2009-09-05 21:47:34 +000012209 else
12210 {
12211 if (logging != MagickFalse)
12212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012213 " Skipping %c%c%c%c chunk, length=%.20g.",
12214 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012215 }
12216 p+=(8+len);
12217 }
12218 }
12219 else
12220 {
12221 /* Write JDAA chunk header */
12222 if (logging != MagickFalse)
12223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012224 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012225 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012226 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012227 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012228 /* Write JDAT chunk(s) data */
12229 (void) WriteBlob(image,4,chunk);
12230 (void) WriteBlob(image,length,blob);
12231 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12232 (uInt) length));
12233 }
12234 blob=(unsigned char *) RelinquishMagickMemory(blob);
12235 }
12236
12237 /* Encode image as a JPEG blob */
12238 if (logging != MagickFalse)
12239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12240 " Creating jpeg_image_info.");
12241 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12242 if (jpeg_image_info == (ImageInfo *) NULL)
12243 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12244
12245 if (logging != MagickFalse)
12246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12247 " Creating jpeg_image.");
12248
cristy16ea1392012-03-21 20:38:41 +000012249 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012250 if (jpeg_image == (Image *) NULL)
12251 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12252 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12253
12254 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012255 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012256 jpeg_image->filename);
12257
12258 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012259 exception);
cristy3ed852e2009-09-05 21:47:34 +000012260
12261 if (logging != MagickFalse)
12262 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012263 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12264 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012265
12266 if (jng_color_type == 8 || jng_color_type == 12)
12267 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012268
glennrp59575fa2011-12-31 21:31:39 +000012269 jpeg_image_info->quality=jng_quality;
12270 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012271 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12272 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012273
cristy3ed852e2009-09-05 21:47:34 +000012274 if (logging != MagickFalse)
12275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12276 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012277
cristy16ea1392012-03-21 20:38:41 +000012278 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012279
cristy3ed852e2009-09-05 21:47:34 +000012280 if (logging != MagickFalse)
12281 {
12282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012283 " Successfully read jpeg_image into a blob, length=%.20g.",
12284 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012285
12286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012287 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012288 }
glennrp0fe50b42010-11-16 03:52:51 +000012289
cristy3ed852e2009-09-05 21:47:34 +000012290 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012291 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012292 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012293 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012294 (void) WriteBlob(image,4,chunk);
12295 (void) WriteBlob(image,length,blob);
12296 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12297
12298 jpeg_image=DestroyImage(jpeg_image);
12299 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12300 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12301 blob=(unsigned char *) RelinquishMagickMemory(blob);
12302
12303 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012304 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012305
12306 /* Write IEND chunk */
12307 (void) WriteBlobMSBULong(image,0L);
12308 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012309 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012310 (void) WriteBlob(image,4,chunk);
12311 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12312
12313 if (logging != MagickFalse)
12314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12315 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012316
cristy3ed852e2009-09-05 21:47:34 +000012317 return(status);
12318}
12319
12320
12321/*
12322%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12323% %
12324% %
12325% %
12326% W r i t e J N G I m a g e %
12327% %
12328% %
12329% %
12330%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12331%
12332% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12333%
12334% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12335%
12336% The format of the WriteJNGImage method is:
12337%
cristy16ea1392012-03-21 20:38:41 +000012338% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12339% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012340%
12341% A description of each parameter follows:
12342%
12343% o image_info: the image info.
12344%
12345% o image: The image.
12346%
cristy16ea1392012-03-21 20:38:41 +000012347% o exception: return any errors or warnings in this structure.
12348%
cristy3ed852e2009-09-05 21:47:34 +000012349%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12350*/
cristy16ea1392012-03-21 20:38:41 +000012351static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12352 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012353{
12354 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012355 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012356 logging,
cristy3ed852e2009-09-05 21:47:34 +000012357 status;
12358
12359 MngInfo
12360 *mng_info;
12361
cristy3ed852e2009-09-05 21:47:34 +000012362 /*
12363 Open image file.
12364 */
12365 assert(image_info != (const ImageInfo *) NULL);
12366 assert(image_info->signature == MagickSignature);
12367 assert(image != (Image *) NULL);
12368 assert(image->signature == MagickSignature);
12369 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012370 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012371 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012372 if (status == MagickFalse)
12373 return(status);
12374
12375 /*
12376 Allocate a MngInfo structure.
12377 */
12378 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012379 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012380 if (mng_info == (MngInfo *) NULL)
12381 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12382 /*
12383 Initialize members of the MngInfo structure.
12384 */
12385 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12386 mng_info->image=image;
12387 have_mng_structure=MagickTrue;
12388
12389 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12390
cristy16ea1392012-03-21 20:38:41 +000012391 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012392 (void) CloseBlob(image);
12393
12394 (void) CatchImageException(image);
12395 MngInfoFreeStruct(mng_info,&have_mng_structure);
12396 if (logging != MagickFalse)
12397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12398 return(status);
12399}
12400#endif
12401
cristy16ea1392012-03-21 20:38:41 +000012402static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12403 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012404{
12405 const char
12406 *option;
12407
12408 Image
12409 *next_image;
12410
12411 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012412 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012413 status;
12414
glennrp03812ae2010-12-24 01:31:34 +000012415 volatile MagickBooleanType
12416 logging;
12417
cristy3ed852e2009-09-05 21:47:34 +000012418 MngInfo
12419 *mng_info;
12420
12421 int
cristy3ed852e2009-09-05 21:47:34 +000012422 image_count,
12423 need_iterations,
12424 need_matte;
12425
12426 volatile int
12427#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12428 defined(PNG_MNG_FEATURES_SUPPORTED)
12429 need_local_plte,
12430#endif
12431 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012432 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012433 use_global_plte;
12434
cristybb503372010-05-27 20:51:26 +000012435 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012436 i;
12437
12438 unsigned char
12439 chunk[800];
12440
12441 volatile unsigned int
12442 write_jng,
12443 write_mng;
12444
cristybb503372010-05-27 20:51:26 +000012445 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012446 scene;
12447
cristybb503372010-05-27 20:51:26 +000012448 size_t
cristy3ed852e2009-09-05 21:47:34 +000012449 final_delay=0,
12450 initial_delay;
12451
glennrpd5045b42010-03-24 12:40:35 +000012452#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012453 if (image_info->verbose)
12454 printf("Your PNG library (libpng-%s) is rather old.\n",
12455 PNG_LIBPNG_VER_STRING);
12456#endif
12457
12458 /*
12459 Open image file.
12460 */
12461 assert(image_info != (const ImageInfo *) NULL);
12462 assert(image_info->signature == MagickSignature);
12463 assert(image != (Image *) NULL);
12464 assert(image->signature == MagickSignature);
12465 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012466 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012467 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012468 if (status == MagickFalse)
12469 return(status);
12470
12471 /*
12472 Allocate a MngInfo structure.
12473 */
12474 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012475 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012476 if (mng_info == (MngInfo *) NULL)
12477 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12478 /*
12479 Initialize members of the MngInfo structure.
12480 */
12481 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12482 mng_info->image=image;
12483 have_mng_structure=MagickTrue;
12484 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12485
12486 /*
12487 * See if user has requested a specific PNG subformat to be used
12488 * for all of the PNGs in the MNG being written, e.g.,
12489 *
12490 * convert *.png png8:animation.mng
12491 *
12492 * To do: check -define png:bit_depth and png:color_type as well,
12493 * or perhaps use mng:bit_depth and mng:color_type instead for
12494 * global settings.
12495 */
12496
12497 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12498 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12499 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12500
12501 write_jng=MagickFalse;
12502 if (image_info->compression == JPEGCompression)
12503 write_jng=MagickTrue;
12504
12505 mng_info->adjoin=image_info->adjoin &&
12506 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12507
cristy3ed852e2009-09-05 21:47:34 +000012508 if (logging != MagickFalse)
12509 {
12510 /* Log some info about the input */
12511 Image
12512 *p;
12513
12514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12515 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012516
cristy3ed852e2009-09-05 21:47:34 +000012517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012518 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012519
cristy3ed852e2009-09-05 21:47:34 +000012520 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12521 " Type: %d",image_info->type);
12522
12523 scene=0;
12524 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12525 {
12526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012527 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012528
cristy3ed852e2009-09-05 21:47:34 +000012529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012530 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012531
cristy3ed852e2009-09-05 21:47:34 +000012532 if (p->matte)
12533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12534 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012535
cristy3ed852e2009-09-05 21:47:34 +000012536 else
12537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12538 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012539
cristy3ed852e2009-09-05 21:47:34 +000012540 if (p->storage_class == PseudoClass)
12541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12542 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012543
cristy3ed852e2009-09-05 21:47:34 +000012544 else
12545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12546 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012547
cristy3ed852e2009-09-05 21:47:34 +000012548 if (p->colors)
12549 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012550 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012551
cristy3ed852e2009-09-05 21:47:34 +000012552 else
12553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12554 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012555
cristy3ed852e2009-09-05 21:47:34 +000012556 if (mng_info->adjoin == MagickFalse)
12557 break;
12558 }
12559 }
12560
cristy3ed852e2009-09-05 21:47:34 +000012561 use_global_plte=MagickFalse;
12562 all_images_are_gray=MagickFalse;
12563#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12564 need_local_plte=MagickTrue;
12565#endif
12566 need_defi=MagickFalse;
12567 need_matte=MagickFalse;
12568 mng_info->framing_mode=1;
12569 mng_info->old_framing_mode=1;
12570
12571 if (write_mng)
12572 if (image_info->page != (char *) NULL)
12573 {
12574 /*
12575 Determine image bounding box.
12576 */
12577 SetGeometry(image,&mng_info->page);
12578 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12579 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12580 }
12581 if (write_mng)
12582 {
12583 unsigned int
12584 need_geom;
12585
12586 unsigned short
12587 red,
12588 green,
12589 blue;
12590
12591 mng_info->page=image->page;
12592 need_geom=MagickTrue;
12593 if (mng_info->page.width || mng_info->page.height)
12594 need_geom=MagickFalse;
12595 /*
12596 Check all the scenes.
12597 */
12598 initial_delay=image->delay;
12599 need_iterations=MagickFalse;
12600 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12601 mng_info->equal_physs=MagickTrue,
12602 mng_info->equal_gammas=MagickTrue;
12603 mng_info->equal_srgbs=MagickTrue;
12604 mng_info->equal_backgrounds=MagickTrue;
12605 image_count=0;
12606#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12607 defined(PNG_MNG_FEATURES_SUPPORTED)
12608 all_images_are_gray=MagickTrue;
12609 mng_info->equal_palettes=MagickFalse;
12610 need_local_plte=MagickFalse;
12611#endif
12612 for (next_image=image; next_image != (Image *) NULL; )
12613 {
12614 if (need_geom)
12615 {
12616 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12617 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012618
cristy3ed852e2009-09-05 21:47:34 +000012619 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12620 mng_info->page.height=next_image->rows+next_image->page.y;
12621 }
glennrp0fe50b42010-11-16 03:52:51 +000012622
cristy3ed852e2009-09-05 21:47:34 +000012623 if (next_image->page.x || next_image->page.y)
12624 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012625
cristy3ed852e2009-09-05 21:47:34 +000012626 if (next_image->matte)
12627 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012628
cristy3ed852e2009-09-05 21:47:34 +000012629 if ((int) next_image->dispose >= BackgroundDispose)
12630 if (next_image->matte || next_image->page.x || next_image->page.y ||
12631 ((next_image->columns < mng_info->page.width) &&
12632 (next_image->rows < mng_info->page.height)))
12633 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012634
cristy3ed852e2009-09-05 21:47:34 +000012635 if (next_image->iterations)
12636 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012637
cristy3ed852e2009-09-05 21:47:34 +000012638 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012639
cristy3ed852e2009-09-05 21:47:34 +000012640 if (final_delay != initial_delay || final_delay > 1UL*
12641 next_image->ticks_per_second)
12642 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012643
cristy3ed852e2009-09-05 21:47:34 +000012644#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12645 defined(PNG_MNG_FEATURES_SUPPORTED)
12646 /*
12647 check for global palette possibility.
12648 */
12649 if (image->matte != MagickFalse)
12650 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012651
cristy3ed852e2009-09-05 21:47:34 +000012652 if (need_local_plte == 0)
12653 {
cristy7fb26522012-06-21 13:02:48 +000012654 if (IsImageGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012655 all_images_are_gray=MagickFalse;
12656 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12657 if (use_global_plte == 0)
12658 use_global_plte=mng_info->equal_palettes;
12659 need_local_plte=!mng_info->equal_palettes;
12660 }
12661#endif
12662 if (GetNextImageInList(next_image) != (Image *) NULL)
12663 {
12664 if (next_image->background_color.red !=
12665 next_image->next->background_color.red ||
12666 next_image->background_color.green !=
12667 next_image->next->background_color.green ||
12668 next_image->background_color.blue !=
12669 next_image->next->background_color.blue)
12670 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012671
cristy3ed852e2009-09-05 21:47:34 +000012672 if (next_image->gamma != next_image->next->gamma)
12673 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012674
cristy3ed852e2009-09-05 21:47:34 +000012675 if (next_image->rendering_intent !=
12676 next_image->next->rendering_intent)
12677 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012678
cristy3ed852e2009-09-05 21:47:34 +000012679 if ((next_image->units != next_image->next->units) ||
cristy16ea1392012-03-21 20:38:41 +000012680 (next_image->resolution.x != next_image->next->resolution.x) ||
12681 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012682 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012683
cristy3ed852e2009-09-05 21:47:34 +000012684 if (mng_info->equal_chrms)
12685 {
12686 if (next_image->chromaticity.red_primary.x !=
12687 next_image->next->chromaticity.red_primary.x ||
12688 next_image->chromaticity.red_primary.y !=
12689 next_image->next->chromaticity.red_primary.y ||
12690 next_image->chromaticity.green_primary.x !=
12691 next_image->next->chromaticity.green_primary.x ||
12692 next_image->chromaticity.green_primary.y !=
12693 next_image->next->chromaticity.green_primary.y ||
12694 next_image->chromaticity.blue_primary.x !=
12695 next_image->next->chromaticity.blue_primary.x ||
12696 next_image->chromaticity.blue_primary.y !=
12697 next_image->next->chromaticity.blue_primary.y ||
12698 next_image->chromaticity.white_point.x !=
12699 next_image->next->chromaticity.white_point.x ||
12700 next_image->chromaticity.white_point.y !=
12701 next_image->next->chromaticity.white_point.y)
12702 mng_info->equal_chrms=MagickFalse;
12703 }
12704 }
12705 image_count++;
12706 next_image=GetNextImageInList(next_image);
12707 }
12708 if (image_count < 2)
12709 {
12710 mng_info->equal_backgrounds=MagickFalse;
12711 mng_info->equal_chrms=MagickFalse;
12712 mng_info->equal_gammas=MagickFalse;
12713 mng_info->equal_srgbs=MagickFalse;
12714 mng_info->equal_physs=MagickFalse;
12715 use_global_plte=MagickFalse;
12716#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12717 need_local_plte=MagickTrue;
12718#endif
12719 need_iterations=MagickFalse;
12720 }
glennrp0fe50b42010-11-16 03:52:51 +000012721
cristy3ed852e2009-09-05 21:47:34 +000012722 if (mng_info->need_fram == MagickFalse)
12723 {
12724 /*
12725 Only certain framing rates 100/n are exactly representable without
12726 the FRAM chunk but we'll allow some slop in VLC files
12727 */
12728 if (final_delay == 0)
12729 {
12730 if (need_iterations != MagickFalse)
12731 {
12732 /*
12733 It's probably a GIF with loop; don't run it *too* fast.
12734 */
glennrp02617122010-07-28 13:07:35 +000012735 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012736 {
12737 final_delay=10;
cristy16ea1392012-03-21 20:38:41 +000012738 (void) ThrowMagickException(exception,GetMagickModule(),
12739 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012740 "input has zero delay between all frames; assuming",
12741 " 10 cs `%s'","");
12742 }
cristy3ed852e2009-09-05 21:47:34 +000012743 }
12744 else
12745 mng_info->ticks_per_second=0;
12746 }
12747 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012748 mng_info->ticks_per_second=(png_uint_32)
12749 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012750 if (final_delay > 50)
12751 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012752
cristy3ed852e2009-09-05 21:47:34 +000012753 if (final_delay > 75)
12754 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012755
cristy3ed852e2009-09-05 21:47:34 +000012756 if (final_delay > 125)
12757 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012758
cristy3ed852e2009-09-05 21:47:34 +000012759 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12760 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12761 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12762 1UL*image->ticks_per_second))
12763 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12764 }
glennrp0fe50b42010-11-16 03:52:51 +000012765
cristy3ed852e2009-09-05 21:47:34 +000012766 if (mng_info->need_fram != MagickFalse)
12767 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12768 /*
12769 If pseudocolor, we should also check to see if all the
12770 palettes are identical and write a global PLTE if they are.
12771 ../glennrp Feb 99.
12772 */
12773 /*
12774 Write the MNG version 1.0 signature and MHDR chunk.
12775 */
12776 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12777 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12778 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012779 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012780 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12781 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012782 PNGLong(chunk+12,mng_info->ticks_per_second);
12783 PNGLong(chunk+16,0L); /* layer count=unknown */
12784 PNGLong(chunk+20,0L); /* frame count=unknown */
12785 PNGLong(chunk+24,0L); /* play time=unknown */
12786 if (write_jng)
12787 {
12788 if (need_matte)
12789 {
12790 if (need_defi || mng_info->need_fram || use_global_plte)
12791 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012792
cristy3ed852e2009-09-05 21:47:34 +000012793 else
12794 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12795 }
glennrp0fe50b42010-11-16 03:52:51 +000012796
cristy3ed852e2009-09-05 21:47:34 +000012797 else
12798 {
12799 if (need_defi || mng_info->need_fram || use_global_plte)
12800 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012801
cristy3ed852e2009-09-05 21:47:34 +000012802 else
12803 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12804 }
12805 }
glennrp0fe50b42010-11-16 03:52:51 +000012806
cristy3ed852e2009-09-05 21:47:34 +000012807 else
12808 {
12809 if (need_matte)
12810 {
12811 if (need_defi || mng_info->need_fram || use_global_plte)
12812 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012813
cristy3ed852e2009-09-05 21:47:34 +000012814 else
12815 PNGLong(chunk+28,9L); /* simplicity=VLC */
12816 }
glennrp0fe50b42010-11-16 03:52:51 +000012817
cristy3ed852e2009-09-05 21:47:34 +000012818 else
12819 {
12820 if (need_defi || mng_info->need_fram || use_global_plte)
12821 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012822
cristy3ed852e2009-09-05 21:47:34 +000012823 else
12824 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12825 }
12826 }
12827 (void) WriteBlob(image,32,chunk);
12828 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12829 option=GetImageOption(image_info,"mng:need-cacheoff");
12830 if (option != (const char *) NULL)
12831 {
12832 size_t
12833 length;
12834
12835 /*
12836 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12837 */
12838 PNGType(chunk,mng_nEED);
12839 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012840 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012841 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012842 length+=4;
12843 (void) WriteBlob(image,length,chunk);
12844 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12845 }
12846 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12847 (GetNextImageInList(image) != (Image *) NULL) &&
12848 (image->iterations != 1))
12849 {
12850 /*
12851 Write MNG TERM chunk
12852 */
12853 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12854 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012855 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012856 chunk[4]=3; /* repeat animation */
12857 chunk[5]=0; /* show last frame when done */
12858 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12859 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012860
cristy3ed852e2009-09-05 21:47:34 +000012861 if (image->iterations == 0)
12862 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012863
cristy3ed852e2009-09-05 21:47:34 +000012864 else
12865 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012866
cristy3ed852e2009-09-05 21:47:34 +000012867 if (logging != MagickFalse)
12868 {
12869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012870 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12871 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012872
cristy3ed852e2009-09-05 21:47:34 +000012873 if (image->iterations == 0)
12874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012875 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012876
cristy3ed852e2009-09-05 21:47:34 +000012877 else
12878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012879 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012880 }
12881 (void) WriteBlob(image,14,chunk);
12882 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12883 }
12884 /*
12885 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12886 */
12887 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12888 mng_info->equal_srgbs)
12889 {
12890 /*
12891 Write MNG sRGB chunk
12892 */
12893 (void) WriteBlobMSBULong(image,1L);
12894 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012895 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012896
cristy3ed852e2009-09-05 21:47:34 +000012897 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012898 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012899 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012900 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012901
cristy3ed852e2009-09-05 21:47:34 +000012902 else
glennrpe610a072010-08-05 17:08:46 +000012903 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012904 Magick_RenderingIntent_to_PNG_RenderingIntent(
12905 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012906
cristy3ed852e2009-09-05 21:47:34 +000012907 (void) WriteBlob(image,5,chunk);
12908 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12909 mng_info->have_write_global_srgb=MagickTrue;
12910 }
glennrp0fe50b42010-11-16 03:52:51 +000012911
cristy3ed852e2009-09-05 21:47:34 +000012912 else
12913 {
12914 if (image->gamma && mng_info->equal_gammas)
12915 {
12916 /*
12917 Write MNG gAMA chunk
12918 */
12919 (void) WriteBlobMSBULong(image,4L);
12920 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012921 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012922 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012923 (void) WriteBlob(image,8,chunk);
12924 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12925 mng_info->have_write_global_gama=MagickTrue;
12926 }
12927 if (mng_info->equal_chrms)
12928 {
12929 PrimaryInfo
12930 primary;
12931
12932 /*
12933 Write MNG cHRM chunk
12934 */
12935 (void) WriteBlobMSBULong(image,32L);
12936 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012937 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012938 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012939 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12940 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012941 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012942 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12943 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012944 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012945 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12946 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012947 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012948 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12949 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012950 (void) WriteBlob(image,36,chunk);
12951 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12952 mng_info->have_write_global_chrm=MagickTrue;
12953 }
12954 }
cristy16ea1392012-03-21 20:38:41 +000012955 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012956 {
12957 /*
12958 Write MNG pHYs chunk
12959 */
12960 (void) WriteBlobMSBULong(image,9L);
12961 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012962 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012963
cristy3ed852e2009-09-05 21:47:34 +000012964 if (image->units == PixelsPerInchResolution)
12965 {
cristy35ef8242010-06-03 16:24:13 +000012966 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012967 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012968
cristy35ef8242010-06-03 16:24:13 +000012969 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012970 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012971
cristy3ed852e2009-09-05 21:47:34 +000012972 chunk[12]=1;
12973 }
glennrp0fe50b42010-11-16 03:52:51 +000012974
cristy3ed852e2009-09-05 21:47:34 +000012975 else
12976 {
12977 if (image->units == PixelsPerCentimeterResolution)
12978 {
cristy35ef8242010-06-03 16:24:13 +000012979 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012980 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012981
cristy35ef8242010-06-03 16:24:13 +000012982 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012983 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012984
cristy3ed852e2009-09-05 21:47:34 +000012985 chunk[12]=1;
12986 }
glennrp0fe50b42010-11-16 03:52:51 +000012987
cristy3ed852e2009-09-05 21:47:34 +000012988 else
12989 {
cristy16ea1392012-03-21 20:38:41 +000012990 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12991 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012992 chunk[12]=0;
12993 }
12994 }
12995 (void) WriteBlob(image,13,chunk);
12996 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12997 }
12998 /*
12999 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13000 or does not cover the entire frame.
13001 */
13002 if (write_mng && (image->matte || image->page.x > 0 ||
13003 image->page.y > 0 || (image->page.width &&
13004 (image->page.width+image->page.x < mng_info->page.width))
13005 || (image->page.height && (image->page.height+image->page.y
13006 < mng_info->page.height))))
13007 {
13008 (void) WriteBlobMSBULong(image,6L);
13009 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000013010 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000013011 red=ScaleQuantumToShort(image->background_color.red);
13012 green=ScaleQuantumToShort(image->background_color.green);
13013 blue=ScaleQuantumToShort(image->background_color.blue);
13014 PNGShort(chunk+4,red);
13015 PNGShort(chunk+6,green);
13016 PNGShort(chunk+8,blue);
13017 (void) WriteBlob(image,10,chunk);
13018 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13019 if (mng_info->equal_backgrounds)
13020 {
13021 (void) WriteBlobMSBULong(image,6L);
13022 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000013023 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000013024 (void) WriteBlob(image,10,chunk);
13025 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13026 }
13027 }
13028
13029#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13030 if ((need_local_plte == MagickFalse) &&
13031 (image->storage_class == PseudoClass) &&
13032 (all_images_are_gray == MagickFalse))
13033 {
cristybb503372010-05-27 20:51:26 +000013034 size_t
cristy3ed852e2009-09-05 21:47:34 +000013035 data_length;
13036
13037 /*
13038 Write MNG PLTE chunk
13039 */
13040 data_length=3*image->colors;
13041 (void) WriteBlobMSBULong(image,data_length);
13042 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013043 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013044
cristybb503372010-05-27 20:51:26 +000013045 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013046 {
cristy16ea1392012-03-21 20:38:41 +000013047 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13048 image->colormap[i].red) & 0xff);
13049 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13050 image->colormap[i].green) & 0xff);
13051 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13052 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000013053 }
glennrp0fe50b42010-11-16 03:52:51 +000013054
cristy3ed852e2009-09-05 21:47:34 +000013055 (void) WriteBlob(image,data_length+4,chunk);
13056 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13057 mng_info->have_write_global_plte=MagickTrue;
13058 }
13059#endif
13060 }
13061 scene=0;
13062 mng_info->delay=0;
13063#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13064 defined(PNG_MNG_FEATURES_SUPPORTED)
13065 mng_info->equal_palettes=MagickFalse;
13066#endif
13067 do
13068 {
13069 if (mng_info->adjoin)
13070 {
13071#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13072 defined(PNG_MNG_FEATURES_SUPPORTED)
13073 /*
13074 If we aren't using a global palette for the entire MNG, check to
13075 see if we can use one for two or more consecutive images.
13076 */
13077 if (need_local_plte && use_global_plte && !all_images_are_gray)
13078 {
13079 if (mng_info->IsPalette)
13080 {
13081 /*
13082 When equal_palettes is true, this image has the same palette
13083 as the previous PseudoClass image
13084 */
13085 mng_info->have_write_global_plte=mng_info->equal_palettes;
13086 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13087 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13088 {
13089 /*
13090 Write MNG PLTE chunk
13091 */
cristybb503372010-05-27 20:51:26 +000013092 size_t
cristy3ed852e2009-09-05 21:47:34 +000013093 data_length;
13094
13095 data_length=3*image->colors;
13096 (void) WriteBlobMSBULong(image,data_length);
13097 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000013098 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000013099
cristybb503372010-05-27 20:51:26 +000013100 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000013101 {
13102 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13103 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13104 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13105 }
glennrp0fe50b42010-11-16 03:52:51 +000013106
cristy3ed852e2009-09-05 21:47:34 +000013107 (void) WriteBlob(image,data_length+4,chunk);
13108 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13109 (uInt) (data_length+4)));
13110 mng_info->have_write_global_plte=MagickTrue;
13111 }
13112 }
13113 else
13114 mng_info->have_write_global_plte=MagickFalse;
13115 }
13116#endif
13117 if (need_defi)
13118 {
cristybb503372010-05-27 20:51:26 +000013119 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000013120 previous_x,
13121 previous_y;
13122
13123 if (scene)
13124 {
13125 previous_x=mng_info->page.x;
13126 previous_y=mng_info->page.y;
13127 }
13128 else
13129 {
13130 previous_x=0;
13131 previous_y=0;
13132 }
13133 mng_info->page=image->page;
13134 if ((mng_info->page.x != previous_x) ||
13135 (mng_info->page.y != previous_y))
13136 {
13137 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13138 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000013139 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000013140 chunk[4]=0; /* object 0 MSB */
13141 chunk[5]=0; /* object 0 LSB */
13142 chunk[6]=0; /* visible */
13143 chunk[7]=0; /* abstract */
13144 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13145 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13146 (void) WriteBlob(image,16,chunk);
13147 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13148 }
13149 }
13150 }
13151
13152 mng_info->write_mng=write_mng;
13153
13154 if ((int) image->dispose >= 3)
13155 mng_info->framing_mode=3;
13156
13157 if (mng_info->need_fram && mng_info->adjoin &&
13158 ((image->delay != mng_info->delay) ||
13159 (mng_info->framing_mode != mng_info->old_framing_mode)))
13160 {
13161 if (image->delay == mng_info->delay)
13162 {
13163 /*
13164 Write a MNG FRAM chunk with the new framing mode.
13165 */
13166 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13167 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013168 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000013169 chunk[4]=(unsigned char) mng_info->framing_mode;
13170 (void) WriteBlob(image,5,chunk);
13171 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13172 }
13173 else
13174 {
13175 /*
13176 Write a MNG FRAM chunk with the delay.
13177 */
13178 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13179 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013180 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013181 chunk[4]=(unsigned char) mng_info->framing_mode;
13182 chunk[5]=0; /* frame name separator (no name) */
13183 chunk[6]=2; /* flag for changing default delay */
13184 chunk[7]=0; /* flag for changing frame timeout */
13185 chunk[8]=0; /* flag for changing frame clipping */
13186 chunk[9]=0; /* flag for changing frame sync_id */
13187 PNGLong(chunk+10,(png_uint_32)
13188 ((mng_info->ticks_per_second*
13189 image->delay)/MagickMax(image->ticks_per_second,1)));
13190 (void) WriteBlob(image,14,chunk);
13191 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013192 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013193 }
13194 mng_info->old_framing_mode=mng_info->framing_mode;
13195 }
13196
13197#if defined(JNG_SUPPORTED)
13198 if (image_info->compression == JPEGCompression)
13199 {
13200 ImageInfo
13201 *write_info;
13202
13203 if (logging != MagickFalse)
13204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13205 " Writing JNG object.");
13206 /* To do: specify the desired alpha compression method. */
13207 write_info=CloneImageInfo(image_info);
13208 write_info->compression=UndefinedCompression;
cristy16ea1392012-03-21 20:38:41 +000013209 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013210 write_info=DestroyImageInfo(write_info);
13211 }
13212 else
13213#endif
13214 {
13215 if (logging != MagickFalse)
13216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13217 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013218
glennrpb9cfe272010-12-21 15:08:06 +000013219 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013220 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013221
13222 /* We don't want any ancillary chunks written */
13223 mng_info->ping_exclude_bKGD=MagickTrue;
13224 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013225 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013226 mng_info->ping_exclude_EXIF=MagickTrue;
13227 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013228 mng_info->ping_exclude_iCCP=MagickTrue;
13229 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13230 mng_info->ping_exclude_oFFs=MagickTrue;
13231 mng_info->ping_exclude_pHYs=MagickTrue;
13232 mng_info->ping_exclude_sRGB=MagickTrue;
13233 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013234 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013235 mng_info->ping_exclude_vpAg=MagickTrue;
13236 mng_info->ping_exclude_zCCP=MagickTrue;
13237 mng_info->ping_exclude_zTXt=MagickTrue;
13238
cristy16ea1392012-03-21 20:38:41 +000013239 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013240 }
13241
13242 if (status == MagickFalse)
13243 {
13244 MngInfoFreeStruct(mng_info,&have_mng_structure);
13245 (void) CloseBlob(image);
13246 return(MagickFalse);
13247 }
13248 (void) CatchImageException(image);
13249 if (GetNextImageInList(image) == (Image *) NULL)
13250 break;
13251 image=SyncNextImageInList(image);
13252 status=SetImageProgress(image,SaveImagesTag,scene++,
13253 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013254
cristy3ed852e2009-09-05 21:47:34 +000013255 if (status == MagickFalse)
13256 break;
glennrp0fe50b42010-11-16 03:52:51 +000013257
cristy3ed852e2009-09-05 21:47:34 +000013258 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013259
cristy3ed852e2009-09-05 21:47:34 +000013260 if (write_mng)
13261 {
13262 while (GetPreviousImageInList(image) != (Image *) NULL)
13263 image=GetPreviousImageInList(image);
13264 /*
13265 Write the MEND chunk.
13266 */
13267 (void) WriteBlobMSBULong(image,0x00000000L);
13268 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013269 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013270 (void) WriteBlob(image,4,chunk);
13271 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13272 }
13273 /*
13274 Relinquish resources.
13275 */
13276 (void) CloseBlob(image);
13277 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013278
cristy3ed852e2009-09-05 21:47:34 +000013279 if (logging != MagickFalse)
13280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013281
cristy3ed852e2009-09-05 21:47:34 +000013282 return(MagickTrue);
13283}
glennrpd5045b42010-03-24 12:40:35 +000013284#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013285
cristy3ed852e2009-09-05 21:47:34 +000013286static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13287{
glennrp3bd393f2011-12-21 18:54:53 +000013288 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013289 printf("Your PNG library is too old: You have libpng-%s\n",
13290 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013291
cristy3ed852e2009-09-05 21:47:34 +000013292 ThrowBinaryException(CoderError,"PNG library is too old",
13293 image_info->filename);
13294}
glennrp39992b42010-11-14 00:03:43 +000013295
cristy3ed852e2009-09-05 21:47:34 +000013296static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13297{
13298 return(WritePNGImage(image_info,image));
13299}
glennrpd5045b42010-03-24 12:40:35 +000013300#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013301#endif