blob: f50177fd63e35fe5693a125de2979487212df16d [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
glennrpe610a072010-08-05 17:08:46 +00001055static int
glennrpcf002022011-01-30 02:38:15 +00001056Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001057{
glennrpe610a072010-08-05 17:08:46 +00001058 switch (intent)
1059 {
1060 case PerceptualIntent:
1061 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001062
glennrpe610a072010-08-05 17:08:46 +00001063 case RelativeIntent:
1064 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001065
glennrpe610a072010-08-05 17:08:46 +00001066 case SaturationIntent:
1067 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001068
glennrpe610a072010-08-05 17:08:46 +00001069 case AbsoluteIntent:
1070 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001071
glennrpe610a072010-08-05 17:08:46 +00001072 default:
1073 return -1;
1074 }
1075}
1076
1077static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001078Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001079{
glennrpcf002022011-01-30 02:38:15 +00001080 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001081 {
1082 case 0:
1083 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001084
glennrpe610a072010-08-05 17:08:46 +00001085 case 1:
1086 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001087
glennrpe610a072010-08-05 17:08:46 +00001088 case 2:
1089 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001090
glennrpe610a072010-08-05 17:08:46 +00001091 case 3:
1092 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001093
glennrpe610a072010-08-05 17:08:46 +00001094 default:
1095 return UndefinedIntent;
1096 }
1097}
1098
cristybb503372010-05-27 20:51:26 +00001099static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001100{
1101 if (x > y)
1102 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001103
cristy3ed852e2009-09-05 21:47:34 +00001104 return(y);
1105}
glennrp0c3e06b2010-11-19 13:45:02 +00001106
cristybb503372010-05-27 20:51:26 +00001107static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001108{
1109 if (x < y)
1110 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001111
cristy3ed852e2009-09-05 21:47:34 +00001112 return(y);
1113}
glennrp0c3e06b2010-11-19 13:45:02 +00001114
cristy3ed852e2009-09-05 21:47:34 +00001115
1116/*
1117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1118% %
1119% %
1120% %
1121% I m a g e I s G r a y %
1122% %
1123% %
1124% %
1125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1126% %
cristy16ea1392012-03-21 20:38:41 +00001127% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001128% %
1129%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1130*/
cristy16ea1392012-03-21 20:38:41 +00001131static MagickBooleanType ImageIsGray(Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001132{
cristy16ea1392012-03-21 20:38:41 +00001133 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001134 *p;
1135
cristybb503372010-05-27 20:51:26 +00001136 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001137 i,
1138 x,
1139 y;
1140
1141 assert(image != (Image *) NULL);
1142 assert(image->signature == MagickSignature);
1143 if (image->debug != MagickFalse)
1144 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1145
1146 if (image->storage_class == PseudoClass)
1147 {
cristybb503372010-05-27 20:51:26 +00001148 for (i=0; i < (ssize_t) image->colors; i++)
cristy16ea1392012-03-21 20:38:41 +00001149 if (IsPixelInfoGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001150 return(MagickFalse);
1151 return(MagickTrue);
1152 }
cristybb503372010-05-27 20:51:26 +00001153 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001154 {
cristy16ea1392012-03-21 20:38:41 +00001155 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1156 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001157 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001158 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001159 {
cristy16ea1392012-03-21 20:38:41 +00001160 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001161 return(MagickFalse);
cristy16ea1392012-03-21 20:38:41 +00001162 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001163 }
1164 }
1165 return(MagickTrue);
1166}
glennrpd5045b42010-03-24 12:40:35 +00001167#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001168#endif /* MAGICKCORE_PNG_DELEGATE */
1169
1170/*
1171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1172% %
1173% %
1174% %
1175% I s M N G %
1176% %
1177% %
1178% %
1179%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1180%
1181% IsMNG() returns MagickTrue if the image format type, identified by the
1182% magick string, is MNG.
1183%
1184% The format of the IsMNG method is:
1185%
1186% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1187%
1188% A description of each parameter follows:
1189%
1190% o magick: compare image format pattern against these bytes.
1191%
1192% o length: Specifies the length of the magick string.
1193%
1194%
1195*/
1196static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1197{
1198 if (length < 8)
1199 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001200
cristy3ed852e2009-09-05 21:47:34 +00001201 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1202 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001203
cristy3ed852e2009-09-05 21:47:34 +00001204 return(MagickFalse);
1205}
1206
1207/*
1208%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1209% %
1210% %
1211% %
1212% I s J N G %
1213% %
1214% %
1215% %
1216%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1217%
1218% IsJNG() returns MagickTrue if the image format type, identified by the
1219% magick string, is JNG.
1220%
1221% The format of the IsJNG method is:
1222%
1223% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1224%
1225% A description of each parameter follows:
1226%
1227% o magick: compare image format pattern against these bytes.
1228%
1229% o length: Specifies the length of the magick string.
1230%
1231%
1232*/
1233static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1234{
1235 if (length < 8)
1236 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001237
cristy3ed852e2009-09-05 21:47:34 +00001238 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1239 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001240
cristy3ed852e2009-09-05 21:47:34 +00001241 return(MagickFalse);
1242}
1243
1244/*
1245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1246% %
1247% %
1248% %
1249% I s P N G %
1250% %
1251% %
1252% %
1253%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254%
1255% IsPNG() returns MagickTrue if the image format type, identified by the
1256% magick string, is PNG.
1257%
1258% The format of the IsPNG method is:
1259%
1260% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1261%
1262% A description of each parameter follows:
1263%
1264% o magick: compare image format pattern against these bytes.
1265%
1266% o length: Specifies the length of the magick string.
1267%
1268*/
1269static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1270{
1271 if (length < 8)
1272 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001273
cristy3ed852e2009-09-05 21:47:34 +00001274 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1275 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001276
cristy3ed852e2009-09-05 21:47:34 +00001277 return(MagickFalse);
1278}
1279
1280#if defined(MAGICKCORE_PNG_DELEGATE)
1281#if defined(__cplusplus) || defined(c_plusplus)
1282extern "C" {
1283#endif
1284
glennrpd5045b42010-03-24 12:40:35 +00001285#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001286static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001287{
1288 unsigned char
1289 buffer[4];
1290
1291 assert(image != (Image *) NULL);
1292 assert(image->signature == MagickSignature);
1293 buffer[0]=(unsigned char) (value >> 24);
1294 buffer[1]=(unsigned char) (value >> 16);
1295 buffer[2]=(unsigned char) (value >> 8);
1296 buffer[3]=(unsigned char) value;
1297 return((size_t) WriteBlob(image,4,buffer));
1298}
1299
1300static void PNGLong(png_bytep p,png_uint_32 value)
1301{
1302 *p++=(png_byte) ((value >> 24) & 0xff);
1303 *p++=(png_byte) ((value >> 16) & 0xff);
1304 *p++=(png_byte) ((value >> 8) & 0xff);
1305 *p++=(png_byte) (value & 0xff);
1306}
1307
glennrpa521b2f2010-10-29 04:11:03 +00001308#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001309static void PNGsLong(png_bytep p,png_int_32 value)
1310{
1311 *p++=(png_byte) ((value >> 24) & 0xff);
1312 *p++=(png_byte) ((value >> 16) & 0xff);
1313 *p++=(png_byte) ((value >> 8) & 0xff);
1314 *p++=(png_byte) (value & 0xff);
1315}
glennrpa521b2f2010-10-29 04:11:03 +00001316#endif
cristy3ed852e2009-09-05 21:47:34 +00001317
1318static void PNGShort(png_bytep p,png_uint_16 value)
1319{
1320 *p++=(png_byte) ((value >> 8) & 0xff);
1321 *p++=(png_byte) (value & 0xff);
1322}
1323
1324static void PNGType(png_bytep p,png_bytep type)
1325{
1326 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1327}
1328
glennrp03812ae2010-12-24 01:31:34 +00001329static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1330 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001331{
1332 if (logging != MagickFalse)
1333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001334 " Writing %c%c%c%c chunk, length: %.20g",
1335 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001336}
glennrpd5045b42010-03-24 12:40:35 +00001337#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001338
1339#if defined(__cplusplus) || defined(c_plusplus)
1340}
1341#endif
1342
glennrpd5045b42010-03-24 12:40:35 +00001343#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001344/*
1345%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1346% %
1347% %
1348% %
1349% R e a d P N G I m a g e %
1350% %
1351% %
1352% %
1353%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1354%
1355% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1356% Multiple-image Network Graphics (MNG) image file and returns it. It
1357% allocates the memory necessary for the new Image structure and returns a
1358% pointer to the new image or set of images.
1359%
1360% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1361%
1362% The format of the ReadPNGImage method is:
1363%
1364% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1365%
1366% A description of each parameter follows:
1367%
1368% o image_info: the image info.
1369%
1370% o exception: return any errors or warnings in this structure.
1371%
1372% To do, more or less in chronological order (as of version 5.5.2,
1373% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1374%
1375% Get 16-bit cheap transparency working.
1376%
1377% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1378%
1379% Preserve all unknown and not-yet-handled known chunks found in input
1380% PNG file and copy them into output PNG files according to the PNG
1381% copying rules.
1382%
1383% (At this point, PNG encoding should be in full MNG compliance)
1384%
1385% Provide options for choice of background to use when the MNG BACK
1386% chunk is not present or is not mandatory (i.e., leave transparent,
1387% user specified, MNG BACK, PNG bKGD)
1388%
1389% Implement LOOP/ENDL [done, but could do discretionary loops more
1390% efficiently by linking in the duplicate frames.].
1391%
1392% Decode and act on the MHDR simplicity profile (offer option to reject
1393% files or attempt to process them anyway when the profile isn't LC or VLC).
1394%
1395% Upgrade to full MNG without Delta-PNG.
1396%
1397% o BACK [done a while ago except for background image ID]
1398% o MOVE [done 15 May 1999]
1399% o CLIP [done 15 May 1999]
1400% o DISC [done 19 May 1999]
1401% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1402% o SEEK [partially done 19 May 1999 (discard function only)]
1403% o SHOW
1404% o PAST
1405% o BASI
1406% o MNG-level tEXt/iTXt/zTXt
1407% o pHYg
1408% o pHYs
1409% o sBIT
1410% o bKGD
1411% o iTXt (wait for libpng implementation).
1412%
1413% Use the scene signature to discover when an identical scene is
1414% being reused, and just point to the original image->exception instead
1415% of storing another set of pixels. This not specific to MNG
1416% but could be applied generally.
1417%
1418% Upgrade to full MNG with Delta-PNG.
1419%
1420% JNG tEXt/iTXt/zTXt
1421%
1422% We will not attempt to read files containing the CgBI chunk.
1423% They are really Xcode files meant for display on the iPhone.
1424% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001425% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001426% since irretrievable loss of color data has occurred due to the
1427% use of premultiplied alpha.
1428*/
1429
1430#if defined(__cplusplus) || defined(c_plusplus)
1431extern "C" {
1432#endif
1433
1434/*
1435 This the function that does the actual reading of data. It is
1436 the same as the one supplied in libpng, except that it receives the
1437 datastream from the ReadBlob() function instead of standard input.
1438*/
1439static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1440{
1441 Image
1442 *image;
1443
1444 image=(Image *) png_get_io_ptr(png_ptr);
1445 if (length)
1446 {
1447 png_size_t
1448 check;
1449
1450 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1451 if (check != length)
1452 {
1453 char
1454 msg[MaxTextExtent];
1455
cristy3b6fd2e2011-05-20 12:53:50 +00001456 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001457 "Expected %.20g bytes; found %.20g bytes",(double) length,
1458 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001459 png_warning(png_ptr,msg);
1460 png_error(png_ptr,"Read Exception");
1461 }
1462 }
1463}
1464
1465#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1466 !defined(PNG_MNG_FEATURES_SUPPORTED)
1467/* We use mng_get_data() instead of png_get_data() if we have a libpng
1468 * older than libpng-1.0.3a, which was the first to allow the empty
1469 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1470 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1471 * encountered after an empty PLTE, so we have to look ahead for bKGD
1472 * chunks and remove them from the datastream that is passed to libpng,
1473 * and store their contents for later use.
1474 */
1475static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1476{
1477 MngInfo
1478 *mng_info;
1479
1480 Image
1481 *image;
1482
1483 png_size_t
1484 check;
1485
cristybb503372010-05-27 20:51:26 +00001486 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001487 i;
1488
1489 i=0;
1490 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1491 image=(Image *) mng_info->image;
1492 while (mng_info->bytes_in_read_buffer && length)
1493 {
1494 data[i]=mng_info->read_buffer[i];
1495 mng_info->bytes_in_read_buffer--;
1496 length--;
1497 i++;
1498 }
1499 if (length)
1500 {
1501 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001502
cristy3ed852e2009-09-05 21:47:34 +00001503 if (check != length)
1504 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001505
cristy3ed852e2009-09-05 21:47:34 +00001506 if (length == 4)
1507 {
1508 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1509 (data[3] == 0))
1510 {
1511 check=(png_size_t) ReadBlob(image,(size_t) length,
1512 (char *) mng_info->read_buffer);
1513 mng_info->read_buffer[4]=0;
1514 mng_info->bytes_in_read_buffer=4;
1515 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1516 mng_info->found_empty_plte=MagickTrue;
1517 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1518 {
1519 mng_info->found_empty_plte=MagickFalse;
1520 mng_info->have_saved_bkgd_index=MagickFalse;
1521 }
1522 }
glennrp0fe50b42010-11-16 03:52:51 +00001523
cristy3ed852e2009-09-05 21:47:34 +00001524 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1525 (data[3] == 1))
1526 {
1527 check=(png_size_t) ReadBlob(image,(size_t) length,
1528 (char *) mng_info->read_buffer);
1529 mng_info->read_buffer[4]=0;
1530 mng_info->bytes_in_read_buffer=4;
1531 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1532 if (mng_info->found_empty_plte)
1533 {
1534 /*
1535 Skip the bKGD data byte and CRC.
1536 */
1537 check=(png_size_t)
1538 ReadBlob(image,5,(char *) mng_info->read_buffer);
1539 check=(png_size_t) ReadBlob(image,(size_t) length,
1540 (char *) mng_info->read_buffer);
1541 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1542 mng_info->have_saved_bkgd_index=MagickTrue;
1543 mng_info->bytes_in_read_buffer=0;
1544 }
1545 }
1546 }
1547 }
1548}
1549#endif
1550
1551static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1552{
1553 Image
1554 *image;
1555
1556 image=(Image *) png_get_io_ptr(png_ptr);
1557 if (length)
1558 {
1559 png_size_t
1560 check;
1561
cristybb503372010-05-27 20:51:26 +00001562 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001563
cristy3ed852e2009-09-05 21:47:34 +00001564 if (check != length)
1565 png_error(png_ptr,"WriteBlob Failed");
1566 }
1567}
1568
1569static void png_flush_data(png_structp png_ptr)
1570{
1571 (void) png_ptr;
1572}
1573
1574#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1575static int PalettesAreEqual(Image *a,Image *b)
1576{
cristybb503372010-05-27 20:51:26 +00001577 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001578 i;
1579
1580 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1581 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001582
cristy3ed852e2009-09-05 21:47:34 +00001583 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1584 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001585
cristy3ed852e2009-09-05 21:47:34 +00001586 if (a->colors != b->colors)
1587 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001588
cristybb503372010-05-27 20:51:26 +00001589 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001590 {
1591 if ((a->colormap[i].red != b->colormap[i].red) ||
1592 (a->colormap[i].green != b->colormap[i].green) ||
1593 (a->colormap[i].blue != b->colormap[i].blue))
1594 return((int) MagickFalse);
1595 }
glennrp0fe50b42010-11-16 03:52:51 +00001596
cristy3ed852e2009-09-05 21:47:34 +00001597 return((int) MagickTrue);
1598}
1599#endif
1600
1601static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1602{
1603 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1604 mng_info->exists[i] && !mng_info->frozen[i])
1605 {
1606#ifdef MNG_OBJECT_BUFFERS
1607 if (mng_info->ob[i] != (MngBuffer *) NULL)
1608 {
1609 if (mng_info->ob[i]->reference_count > 0)
1610 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001611
cristy3ed852e2009-09-05 21:47:34 +00001612 if (mng_info->ob[i]->reference_count == 0)
1613 {
1614 if (mng_info->ob[i]->image != (Image *) NULL)
1615 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001616
cristy3ed852e2009-09-05 21:47:34 +00001617 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1618 }
1619 }
1620 mng_info->ob[i]=(MngBuffer *) NULL;
1621#endif
1622 mng_info->exists[i]=MagickFalse;
1623 mng_info->invisible[i]=MagickFalse;
1624 mng_info->viewable[i]=MagickFalse;
1625 mng_info->frozen[i]=MagickFalse;
1626 mng_info->x_off[i]=0;
1627 mng_info->y_off[i]=0;
1628 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001629 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001630 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001631 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001632 }
1633}
1634
glennrp21f0e622011-01-07 16:20:57 +00001635static void MngInfoFreeStruct(MngInfo *mng_info,
1636 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001637{
glennrp21f0e622011-01-07 16:20:57 +00001638 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001639 {
cristybb503372010-05-27 20:51:26 +00001640 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001641 i;
1642
1643 for (i=1; i < MNG_MAX_OBJECTS; i++)
1644 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001645
cristy3ed852e2009-09-05 21:47:34 +00001646 if (mng_info->global_plte != (png_colorp) NULL)
1647 mng_info->global_plte=(png_colorp)
1648 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001649
cristy3ed852e2009-09-05 21:47:34 +00001650 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1651 *have_mng_structure=MagickFalse;
1652 }
1653}
1654
1655static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1656{
1657 MngBox
1658 box;
1659
1660 box=box1;
1661 if (box.left < box2.left)
1662 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001663
cristy3ed852e2009-09-05 21:47:34 +00001664 if (box.top < box2.top)
1665 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001666
cristy3ed852e2009-09-05 21:47:34 +00001667 if (box.right > box2.right)
1668 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001669
cristy3ed852e2009-09-05 21:47:34 +00001670 if (box.bottom > box2.bottom)
1671 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001672
cristy3ed852e2009-09-05 21:47:34 +00001673 return box;
1674}
1675
1676static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1677{
1678 MngBox
1679 box;
1680
1681 /*
1682 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1683 */
cristybb503372010-05-27 20:51:26 +00001684 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1685 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1686 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1687 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001688 if (delta_type != 0)
1689 {
1690 box.left+=previous_box.left;
1691 box.right+=previous_box.right;
1692 box.top+=previous_box.top;
1693 box.bottom+=previous_box.bottom;
1694 }
glennrp0fe50b42010-11-16 03:52:51 +00001695
cristy3ed852e2009-09-05 21:47:34 +00001696 return(box);
1697}
1698
1699static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1700 unsigned char *p)
1701{
1702 MngPair
1703 pair;
1704 /*
cristybb503372010-05-27 20:51:26 +00001705 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001706 */
cristy8182b072010-05-30 20:10:53 +00001707 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1708 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001709
cristy3ed852e2009-09-05 21:47:34 +00001710 if (delta_type != 0)
1711 {
1712 pair.a+=previous_pair.a;
1713 pair.b+=previous_pair.b;
1714 }
glennrp0fe50b42010-11-16 03:52:51 +00001715
cristy3ed852e2009-09-05 21:47:34 +00001716 return(pair);
1717}
1718
cristy8182b072010-05-30 20:10:53 +00001719static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001720{
cristy8182b072010-05-30 20:10:53 +00001721 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001722}
1723
cristy16ea1392012-03-21 20:38:41 +00001724typedef struct _PNGErrorInfo
cristyc82a27b2011-10-21 01:07:16 +00001725{
cristyc82a27b2011-10-21 01:07:16 +00001726 Image
1727 *image;
1728
cristy16ea1392012-03-21 20:38:41 +00001729 ExceptionInfo
1730 *exception;
1731} PNGErrorInfo;
cristyc82a27b2011-10-21 01:07:16 +00001732
cristy16ea1392012-03-21 20:38:41 +00001733static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1734{
1735 ExceptionInfo
1736 *exception;
cristyc82a27b2011-10-21 01:07:16 +00001737
cristy16ea1392012-03-21 20:38:41 +00001738 Image
1739 *image;
1740
1741 PNGErrorInfo
1742 *error_info;
1743
1744 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1745 image=error_info->image;
1746 exception=error_info->exception;
1747
1748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1749 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
1750
1751 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1752 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001753
glennrpe4017e32011-01-08 17:16:09 +00001754#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001755 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1756 * are building with libpng-1.4.x and can be ignored.
1757 */
cristy3ed852e2009-09-05 21:47:34 +00001758 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001759#else
1760 png_longjmp(ping,1);
1761#endif
cristy3ed852e2009-09-05 21:47:34 +00001762}
1763
glennrpcf002022011-01-30 02:38:15 +00001764static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001765{
cristy16ea1392012-03-21 20:38:41 +00001766 ExceptionInfo
1767 *exception;
1768
cristy3ed852e2009-09-05 21:47:34 +00001769 Image
1770 *image;
1771
cristy16ea1392012-03-21 20:38:41 +00001772 PNGErrorInfo
1773 *error_info;
1774
cristy3ed852e2009-09-05 21:47:34 +00001775 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1776 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001777
cristy16ea1392012-03-21 20:38:41 +00001778 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1779 image=error_info->image;
1780 exception=error_info->exception;
1781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1782 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001783
cristy16ea1392012-03-21 20:38:41 +00001784 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
cristy3ed852e2009-09-05 21:47:34 +00001785 message,"`%s'",image->filename);
1786}
1787
1788#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001789static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001790{
cristydf0d90e2011-12-12 01:03:55 +00001791 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001792 return((png_voidp) AcquireMagickMemory((size_t) size));
cristy3ed852e2009-09-05 21:47:34 +00001793}
1794
1795/*
1796 Free a pointer. It is removed from the list at the same time.
1797*/
glennrpcf002022011-01-30 02:38:15 +00001798static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001799{
glennrp3bd393f2011-12-21 18:54:53 +00001800 (void) png_ptr;
cristy3ed852e2009-09-05 21:47:34 +00001801 ptr=RelinquishMagickMemory(ptr);
1802 return((png_free_ptr) NULL);
1803}
1804#endif
1805
1806#if defined(__cplusplus) || defined(c_plusplus)
1807}
1808#endif
1809
1810static int
glennrpedaa0382012-04-12 14:16:21 +00001811Magick_png_read_raw_profile(png_struct *ping,Image *image,
1812 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001813{
cristybb503372010-05-27 20:51:26 +00001814 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001815 i;
1816
1817 register unsigned char
1818 *dp;
1819
1820 register png_charp
1821 sp;
1822
1823 png_uint_32
1824 length,
1825 nibbles;
1826
1827 StringInfo
1828 *profile;
1829
glennrp0c3e06b2010-11-19 13:45:02 +00001830 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001831 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1832 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1833 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1834 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1835 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1836 13,14,15};
1837
1838 sp=text[ii].text+1;
1839 /* look for newline */
1840 while (*sp != '\n')
1841 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001842
cristy3ed852e2009-09-05 21:47:34 +00001843 /* look for length */
1844 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1845 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001846
cristyf2f27272009-12-17 14:48:46 +00001847 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001848
glennrp97f90e22011-02-22 05:47:58 +00001849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1850 " length: %lu",(unsigned long) length);
1851
cristy3ed852e2009-09-05 21:47:34 +00001852 while (*sp != ' ' && *sp != '\n')
1853 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001854
cristy3ed852e2009-09-05 21:47:34 +00001855 /* allocate space */
1856 if (length == 0)
1857 {
glennrpedaa0382012-04-12 14:16:21 +00001858 png_warning(ping,"invalid profile length");
cristy3ed852e2009-09-05 21:47:34 +00001859 return(MagickFalse);
1860 }
glennrp0fe50b42010-11-16 03:52:51 +00001861
cristy8723e4b2011-09-01 13:11:19 +00001862 profile=BlobToStringInfo((const void *) NULL,length);
glennrp0fe50b42010-11-16 03:52:51 +00001863
cristy3ed852e2009-09-05 21:47:34 +00001864 if (profile == (StringInfo *) NULL)
1865 {
glennrpedaa0382012-04-12 14:16:21 +00001866 png_warning(ping, "unable to copy profile");
cristy3ed852e2009-09-05 21:47:34 +00001867 return(MagickFalse);
1868 }
glennrp0fe50b42010-11-16 03:52:51 +00001869
cristy3ed852e2009-09-05 21:47:34 +00001870 /* copy profile, skipping white space and column 1 "=" signs */
1871 dp=GetStringInfoDatum(profile);
1872 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001873
cristybb503372010-05-27 20:51:26 +00001874 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001875 {
1876 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1877 {
1878 if (*sp == '\0')
1879 {
glennrpedaa0382012-04-12 14:16:21 +00001880 png_warning(ping, "ran out of profile data");
cristy3ed852e2009-09-05 21:47:34 +00001881 profile=DestroyStringInfo(profile);
1882 return(MagickFalse);
1883 }
1884 sp++;
1885 }
glennrp0fe50b42010-11-16 03:52:51 +00001886
cristy3ed852e2009-09-05 21:47:34 +00001887 if (i%2 == 0)
1888 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001889
cristy3ed852e2009-09-05 21:47:34 +00001890 else
1891 (*dp++)+=unhex[(int) *sp++];
1892 }
1893 /*
1894 We have already read "Raw profile type.
1895 */
cristy16ea1392012-03-21 20:38:41 +00001896 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
cristy3ed852e2009-09-05 21:47:34 +00001897 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001898
cristy3ed852e2009-09-05 21:47:34 +00001899 if (image_info->verbose)
1900 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001901
cristy3ed852e2009-09-05 21:47:34 +00001902 return MagickTrue;
1903}
1904
1905#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1906static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1907{
1908 Image
1909 *image;
1910
1911
1912 /* The unknown chunk structure contains the chunk data:
1913 png_byte name[5];
1914 png_byte *data;
1915 png_size_t size;
1916
1917 Note that libpng has already taken care of the CRC handling.
1918 */
1919
1920
1921 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1922 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1923 return(0); /* Did not recognize */
1924
1925 /* recognized vpAg */
1926
1927 if (chunk->size != 9)
1928 return(-1); /* Error return */
1929
1930 if (chunk->data[8] != 0)
1931 return(0); /* ImageMagick requires pixel units */
1932
1933 image=(Image *) png_get_user_chunk_ptr(ping);
1934
cristybb503372010-05-27 20:51:26 +00001935 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001936 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001937
cristybb503372010-05-27 20:51:26 +00001938 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001939 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1940
1941 /* Return one of the following: */
1942 /* return(-n); chunk had an error */
1943 /* return(0); did not recognize */
1944 /* return(n); success */
1945
1946 return(1);
1947
1948}
1949#endif
1950
1951/*
1952%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1953% %
1954% %
1955% %
1956% R e a d O n e P N G I m a g e %
1957% %
1958% %
1959% %
1960%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1961%
1962% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1963% (minus the 8-byte signature) and returns it. It allocates the memory
1964% necessary for the new Image structure and returns a pointer to the new
1965% image.
1966%
1967% The format of the ReadOnePNGImage method is:
1968%
1969% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1970% ExceptionInfo *exception)
1971%
1972% A description of each parameter follows:
1973%
1974% o mng_info: Specifies a pointer to a MngInfo structure.
1975%
1976% o image_info: the image info.
1977%
1978% o exception: return any errors or warnings in this structure.
1979%
1980*/
1981static Image *ReadOnePNGImage(MngInfo *mng_info,
1982 const ImageInfo *image_info, ExceptionInfo *exception)
1983{
1984 /* Read one PNG image */
1985
glennrpcc95c3f2011-04-18 16:46:48 +00001986 /* To do: Read the tIME chunk into the date:modify property */
1987 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1988
cristy3ed852e2009-09-05 21:47:34 +00001989 Image
1990 *image;
1991
1992 int
glennrp4eb39312011-03-30 21:34:55 +00001993 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001994 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001995 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001996 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001997 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001998 pass,
1999 ping_bit_depth,
2000 ping_color_type,
2001 ping_interlace_method,
2002 ping_compression_method,
2003 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00002004 ping_num_trans,
2005 unit_type;
2006
2007 double
2008 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00002009
2010 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002011 logging,
cristy3ed852e2009-09-05 21:47:34 +00002012 status;
2013
cristy16ea1392012-03-21 20:38:41 +00002014 PixelInfo
2015 transparent_color;
2016
2017 PNGErrorInfo
2018 error_info;
2019
glennrpfaa852b2010-03-30 12:17:00 +00002020 png_bytep
2021 ping_trans_alpha;
2022
2023 png_color_16p
2024 ping_background,
2025 ping_trans_color;
2026
cristy3ed852e2009-09-05 21:47:34 +00002027 png_info
2028 *end_info,
2029 *ping_info;
2030
2031 png_struct
2032 *ping;
2033
2034 png_textp
2035 text;
2036
glennrpfaa852b2010-03-30 12:17:00 +00002037 png_uint_32
2038 ping_height,
2039 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002040 x_resolution,
2041 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002042
cristy16ea1392012-03-21 20:38:41 +00002043 QuantumInfo
2044 *quantum_info;
2045
cristy3ed852e2009-09-05 21:47:34 +00002046 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002047 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002048
cristybb503372010-05-27 20:51:26 +00002049 ssize_t
cristy756ae432011-11-19 02:18:25 +00002050 ping_rowbytes,
cristy3ed852e2009-09-05 21:47:34 +00002051 y;
2052
2053 register unsigned char
2054 *p;
2055
cristybb503372010-05-27 20:51:26 +00002056 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002057 i,
2058 x;
2059
cristy16ea1392012-03-21 20:38:41 +00002060 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002061 *q;
2062
2063 size_t
glennrp39992b42010-11-14 00:03:43 +00002064 length,
cristy3ed852e2009-09-05 21:47:34 +00002065 row_offset;
2066
cristyeb3b22a2011-03-31 20:16:11 +00002067 ssize_t
2068 j;
2069
cristy3ed852e2009-09-05 21:47:34 +00002070#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2071 png_byte unused_chunks[]=
2072 {
2073 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2074 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2075 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2076 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2077 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2078 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2079 };
2080#endif
2081
2082 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002083 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002084
glennrp25c1e2b2010-03-25 01:39:56 +00002085#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002086 if (image_info->verbose)
2087 printf("Your PNG library (libpng-%s) is rather old.\n",
2088 PNG_LIBPNG_VER_STRING);
2089#endif
2090
glennrp61b4c952009-11-10 20:40:41 +00002091#if (PNG_LIBPNG_VER >= 10400)
2092# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2093 if (image_info->verbose)
2094 {
2095 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2096 PNG_LIBPNG_VER_STRING);
2097 printf("Please update it.\n");
2098 }
2099# endif
2100#endif
2101
cristy16ea1392012-03-21 20:38:41 +00002102
2103 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002104 image=mng_info->image;
2105
glennrpa6a06632011-01-19 15:15:34 +00002106 if (logging != MagickFalse)
2107 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2108 " image->matte=%d",(int) image->matte);
2109
glennrp0e319732011-01-25 21:53:13 +00002110 /* Set to an out-of-range color unless tRNS chunk is present */
2111 transparent_color.red=65537;
2112 transparent_color.green=65537;
2113 transparent_color.blue=65537;
cristy16ea1392012-03-21 20:38:41 +00002114 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002115
glennrpcb395ac2011-03-30 19:50:23 +00002116 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002117 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002118 num_raw_profiles = 0;
2119
cristy3ed852e2009-09-05 21:47:34 +00002120 /*
2121 Allocate the PNG structures
2122 */
2123#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00002124 error_info.image=image;
2125 error_info.exception=exception;
2126 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002127 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2128 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002129#else
cristy16ea1392012-03-21 20:38:41 +00002130 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00002131 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002132#endif
2133 if (ping == (png_struct *) NULL)
2134 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002135
cristy3ed852e2009-09-05 21:47:34 +00002136 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002137
cristy3ed852e2009-09-05 21:47:34 +00002138 if (ping_info == (png_info *) NULL)
2139 {
2140 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2141 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2142 }
glennrp0fe50b42010-11-16 03:52:51 +00002143
cristy3ed852e2009-09-05 21:47:34 +00002144 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002145
cristy3ed852e2009-09-05 21:47:34 +00002146 if (end_info == (png_info *) NULL)
2147 {
2148 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2149 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2150 }
glennrp0fe50b42010-11-16 03:52:51 +00002151
glennrpcf002022011-01-30 02:38:15 +00002152 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002153
glennrpfaa852b2010-03-30 12:17:00 +00002154 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002155 {
2156 /*
2157 PNG image is corrupt.
2158 */
2159 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002160
2161#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002162 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002163#endif
glennrpedaa0382012-04-12 14:16:21 +00002164
2165 if (ping_pixels != (unsigned char *) NULL)
2166 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
2167
cristy3ed852e2009-09-05 21:47:34 +00002168 if (logging != MagickFalse)
2169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2170 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002171
cristy3ed852e2009-09-05 21:47:34 +00002172 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002173 {
cristy16ea1392012-03-21 20:38:41 +00002174 InheritException(exception,exception);
cristy7b755eb2009-12-05 14:51:29 +00002175 image->columns=0;
2176 }
glennrp0fe50b42010-11-16 03:52:51 +00002177
cristy3ed852e2009-09-05 21:47:34 +00002178 return(GetFirstImageInList(image));
2179 }
glennrpedaa0382012-04-12 14:16:21 +00002180
2181 /* { For navigation to end of SETJMP-protected block. Within this
2182 * block, use png_error() instead of Throwing an Exception, to ensure
2183 * that libpng is able to clean up, and that the semaphore is unlocked.
2184 */
2185
2186#ifdef PNG_SETJMP_NOT_THREAD_SAFE
2187 LockSemaphoreInfo(ping_semaphore);
2188#endif
2189
cristy3ed852e2009-09-05 21:47:34 +00002190 /*
2191 Prepare PNG for reading.
2192 */
glennrpfaa852b2010-03-30 12:17:00 +00002193
cristy3ed852e2009-09-05 21:47:34 +00002194 mng_info->image_found++;
2195 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002196
cristy3ed852e2009-09-05 21:47:34 +00002197 if (LocaleCompare(image_info->magick,"MNG") == 0)
2198 {
2199#if defined(PNG_MNG_FEATURES_SUPPORTED)
2200 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2201 png_set_read_fn(ping,image,png_get_data);
2202#else
2203#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2204 png_permit_empty_plte(ping,MagickTrue);
2205 png_set_read_fn(ping,image,png_get_data);
2206#else
2207 mng_info->image=image;
2208 mng_info->bytes_in_read_buffer=0;
2209 mng_info->found_empty_plte=MagickFalse;
2210 mng_info->have_saved_bkgd_index=MagickFalse;
2211 png_set_read_fn(ping,mng_info,mng_get_data);
2212#endif
2213#endif
2214 }
glennrp0fe50b42010-11-16 03:52:51 +00002215
cristy3ed852e2009-09-05 21:47:34 +00002216 else
2217 png_set_read_fn(ping,image,png_get_data);
2218
2219#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2220 /* Ignore unused chunks and all unknown chunks except for vpAg */
2221 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2222 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2223 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2224 (int)sizeof(unused_chunks)/5);
2225 /* Callback for other unknown chunks */
2226 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2227#endif
2228
glennrp991e92a2010-01-28 03:09:00 +00002229#if (PNG_LIBPNG_VER < 10400)
2230# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2231 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002232 /* Disable thread-unsafe features of pnggccrd */
2233 if (png_access_version_number() >= 10200)
2234 {
2235 png_uint_32 mmx_disable_mask=0;
2236 png_uint_32 asm_flags;
2237
2238 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2239 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2240 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2241 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2242 asm_flags=png_get_asm_flags(ping);
2243 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2244 }
glennrp991e92a2010-01-28 03:09:00 +00002245# endif
cristy3ed852e2009-09-05 21:47:34 +00002246#endif
2247
2248 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002249
2250 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2251 &ping_bit_depth,&ping_color_type,
2252 &ping_interlace_method,&ping_compression_method,
2253 &ping_filter_method);
2254
2255 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2256 &ping_trans_color);
2257
2258 (void) png_get_bKGD(ping, ping_info, &ping_background);
2259
2260 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002261 {
glennrpfaa852b2010-03-30 12:17:00 +00002262 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2263 {
2264 png_set_packing(ping);
2265 ping_bit_depth = 8;
2266 }
cristy3ed852e2009-09-05 21:47:34 +00002267 }
glennrpfaa852b2010-03-30 12:17:00 +00002268
2269 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002270 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002271 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002272 if (logging != MagickFalse)
2273 {
2274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002275 " PNG width: %.20g, height: %.20g",
2276 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002277
cristy3ed852e2009-09-05 21:47:34 +00002278 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2279 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002280 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002281
cristy3ed852e2009-09-05 21:47:34 +00002282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2283 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002284 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002285
cristy3ed852e2009-09-05 21:47:34 +00002286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2287 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002288 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002289 }
2290
glennrpfaa852b2010-03-30 12:17:00 +00002291#ifdef PNG_READ_iCCP_SUPPORTED
2292 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002293 {
2294 int
2295 compression;
2296
glennrpe4017e32011-01-08 17:16:09 +00002297#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002298 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002299 info;
2300#else
2301 png_bytep
2302 info;
2303#endif
2304
2305 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002306 name;
2307
2308 png_uint_32
2309 profile_length;
2310
2311 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2312 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002313
cristy3ed852e2009-09-05 21:47:34 +00002314 if (profile_length != 0)
2315 {
2316 StringInfo
2317 *profile;
2318
2319 if (logging != MagickFalse)
2320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2321 " Reading PNG iCCP chunk.");
cristye8f8f382011-09-01 13:32:37 +00002322 profile=BlobToStringInfo(info,profile_length);
2323 if (profile == (StringInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00002324 {
2325 png_warning(ping, "ICC profile is NULL");
2326 profile=DestroyStringInfo(profile);
2327 }
2328 else
2329 {
2330 (void) SetImageProfile(image,"icc",profile,exception);
2331 profile=DestroyStringInfo(profile);
2332 }
cristy3ed852e2009-09-05 21:47:34 +00002333 }
2334 }
2335#endif
2336#if defined(PNG_READ_sRGB_SUPPORTED)
2337 {
cristy3ed852e2009-09-05 21:47:34 +00002338 if (mng_info->have_global_srgb)
cristy2ea7a8e2012-02-08 01:04:50 +00002339 {
cristy2ea7a8e2012-02-08 01:04:50 +00002340 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2341 (mng_info->global_srgb_intent);
2342 }
glennrp0fe50b42010-11-16 03:52:51 +00002343
cristy3ed852e2009-09-05 21:47:34 +00002344 if (png_get_sRGB(ping,ping_info,&intent))
2345 {
glennrpcf002022011-01-30 02:38:15 +00002346 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2347 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002348
cristy3ed852e2009-09-05 21:47:34 +00002349 if (logging != MagickFalse)
2350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002351 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002352 }
2353 }
2354#endif
2355 {
glennrpfaa852b2010-03-30 12:17:00 +00002356 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2357 if (mng_info->have_global_gama)
2358 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002359
cristy3ed852e2009-09-05 21:47:34 +00002360 if (png_get_gAMA(ping,ping_info,&file_gamma))
2361 {
2362 image->gamma=(float) file_gamma;
2363 if (logging != MagickFalse)
2364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2365 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2366 }
2367 }
glennrpfaa852b2010-03-30 12:17:00 +00002368 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2369 {
2370 if (mng_info->have_global_chrm != MagickFalse)
2371 {
2372 (void) png_set_cHRM(ping,ping_info,
2373 mng_info->global_chrm.white_point.x,
2374 mng_info->global_chrm.white_point.y,
2375 mng_info->global_chrm.red_primary.x,
2376 mng_info->global_chrm.red_primary.y,
2377 mng_info->global_chrm.green_primary.x,
2378 mng_info->global_chrm.green_primary.y,
2379 mng_info->global_chrm.blue_primary.x,
2380 mng_info->global_chrm.blue_primary.y);
2381 }
2382 }
glennrp0fe50b42010-11-16 03:52:51 +00002383
glennrpfaa852b2010-03-30 12:17:00 +00002384 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002385 {
2386 (void) png_get_cHRM(ping,ping_info,
2387 &image->chromaticity.white_point.x,
2388 &image->chromaticity.white_point.y,
2389 &image->chromaticity.red_primary.x,
2390 &image->chromaticity.red_primary.y,
2391 &image->chromaticity.green_primary.x,
2392 &image->chromaticity.green_primary.y,
2393 &image->chromaticity.blue_primary.x,
2394 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002395
cristy3ed852e2009-09-05 21:47:34 +00002396 if (logging != MagickFalse)
2397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2398 " Reading PNG cHRM chunk.");
2399 }
glennrp0fe50b42010-11-16 03:52:51 +00002400
glennrpe610a072010-08-05 17:08:46 +00002401 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002402 {
glennrpe610a072010-08-05 17:08:46 +00002403 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002404 Magick_RenderingIntent_to_PNG_RenderingIntent
2405 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002406 png_set_gAMA(ping,ping_info,0.45455f);
2407 png_set_cHRM(ping,ping_info,
2408 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2409 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002410 }
cristy3ed852e2009-09-05 21:47:34 +00002411#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002412 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002413 {
cristy905ef802011-02-23 00:29:18 +00002414 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2415 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002416
cristy3ed852e2009-09-05 21:47:34 +00002417 if (logging != MagickFalse)
2418 if (image->page.x || image->page.y)
2419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002420 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2421 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002422 }
2423#endif
2424#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002425 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2426 {
2427 if (mng_info->have_global_phys)
2428 {
2429 png_set_pHYs(ping,ping_info,
2430 mng_info->global_x_pixels_per_unit,
2431 mng_info->global_y_pixels_per_unit,
2432 mng_info->global_phys_unit_type);
2433 }
2434 }
2435
2436 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002437 {
cristy3ed852e2009-09-05 21:47:34 +00002438 /*
2439 Set image resolution.
2440 */
2441 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002442 &unit_type);
cristy16ea1392012-03-21 20:38:41 +00002443 image->resolution.x=(double) x_resolution;
2444 image->resolution.y=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002445
cristy3ed852e2009-09-05 21:47:34 +00002446 if (unit_type == PNG_RESOLUTION_METER)
2447 {
2448 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00002449 image->resolution.x=(double) x_resolution/100.0;
2450 image->resolution.y=(double) y_resolution/100.0;
cristy3ed852e2009-09-05 21:47:34 +00002451 }
glennrp0fe50b42010-11-16 03:52:51 +00002452
cristy3ed852e2009-09-05 21:47:34 +00002453 if (logging != MagickFalse)
2454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002455 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2456 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002457 }
cristy3ed852e2009-09-05 21:47:34 +00002458#endif
glennrp823b55c2011-03-14 18:46:46 +00002459
glennrpfaa852b2010-03-30 12:17:00 +00002460 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002461 {
2462 int
2463 number_colors;
2464
2465 png_colorp
2466 palette;
2467
2468 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002469
cristy3ed852e2009-09-05 21:47:34 +00002470 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002471 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002472 {
2473 if (mng_info->global_plte_length)
2474 {
2475 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2476 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002477
glennrpfaa852b2010-03-30 12:17:00 +00002478 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
glennrpedaa0382012-04-12 14:16:21 +00002479 {
cristy3ed852e2009-09-05 21:47:34 +00002480 if (mng_info->global_trns_length)
2481 {
glennrpedaa0382012-04-12 14:16:21 +00002482 png_warning(ping,
2483 "global tRNS has more entries than global PLTE");
cristy3ed852e2009-09-05 21:47:34 +00002484 }
glennrpedaa0382012-04-12 14:16:21 +00002485 else
2486 {
2487 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2488 (int) mng_info->global_trns_length,NULL);
2489 }
2490 }
glennrpbfd9e612011-04-22 14:02:20 +00002491#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002492 if (
2493#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2494 mng_info->have_saved_bkgd_index ||
2495#endif
glennrpfaa852b2010-03-30 12:17:00 +00002496 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002497 {
2498 png_color_16
2499 background;
2500
2501#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2502 if (mng_info->have_saved_bkgd_index)
2503 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002504#endif
glennrpfaa852b2010-03-30 12:17:00 +00002505 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2506 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002507
cristy3ed852e2009-09-05 21:47:34 +00002508 background.red=(png_uint_16)
2509 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002510
cristy3ed852e2009-09-05 21:47:34 +00002511 background.green=(png_uint_16)
2512 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002513
cristy3ed852e2009-09-05 21:47:34 +00002514 background.blue=(png_uint_16)
2515 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002516
glennrpc6c391a2011-04-27 02:23:56 +00002517 background.gray=(png_uint_16)
2518 mng_info->global_plte[background.index].green;
2519
cristy3ed852e2009-09-05 21:47:34 +00002520 png_set_bKGD(ping,ping_info,&background);
2521 }
2522#endif
2523 }
2524 else
glennrpedaa0382012-04-12 14:16:21 +00002525 png_error(ping,"No global PLTE in file");
cristy3ed852e2009-09-05 21:47:34 +00002526 }
2527 }
2528
glennrpbfd9e612011-04-22 14:02:20 +00002529#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002530 if (mng_info->have_global_bkgd &&
2531 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002532 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002533
glennrpfaa852b2010-03-30 12:17:00 +00002534 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002535 {
glennrpbfd9e612011-04-22 14:02:20 +00002536 unsigned int
2537 bkgd_scale;
2538
cristy3ed852e2009-09-05 21:47:34 +00002539 /*
2540 Set image background color.
2541 */
2542 if (logging != MagickFalse)
2543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2544 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002545
glennrpbfd9e612011-04-22 14:02:20 +00002546 /* Scale background components to 16-bit, then scale
2547 * to quantum depth
2548 */
2549 if (logging != MagickFalse)
2550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2551 " raw ping_background=(%d,%d,%d).",ping_background->red,
2552 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002553
glennrpbfd9e612011-04-22 14:02:20 +00002554 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002555
glennrpbfd9e612011-04-22 14:02:20 +00002556 if (ping_bit_depth == 1)
2557 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002558
glennrpbfd9e612011-04-22 14:02:20 +00002559 else if (ping_bit_depth == 2)
2560 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002561
glennrpbfd9e612011-04-22 14:02:20 +00002562 else if (ping_bit_depth == 4)
2563 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002564
glennrpbfd9e612011-04-22 14:02:20 +00002565 if (ping_bit_depth <= 8)
2566 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002567
glennrpbfd9e612011-04-22 14:02:20 +00002568 ping_background->red *= bkgd_scale;
2569 ping_background->green *= bkgd_scale;
2570 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002571
glennrpbfd9e612011-04-22 14:02:20 +00002572 if (logging != MagickFalse)
2573 {
glennrp2cbb4482010-06-02 04:37:24 +00002574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2575 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002576
glennrp2cbb4482010-06-02 04:37:24 +00002577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2578 " ping_background=(%d,%d,%d).",ping_background->red,
2579 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002580 }
glennrp2cbb4482010-06-02 04:37:24 +00002581
glennrpbfd9e612011-04-22 14:02:20 +00002582 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002583 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002584
glennrpbfd9e612011-04-22 14:02:20 +00002585 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002586 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002587
glennrpbfd9e612011-04-22 14:02:20 +00002588 image->background_color.blue=
2589 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002590
cristy16ea1392012-03-21 20:38:41 +00002591 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002592
glennrpbfd9e612011-04-22 14:02:20 +00002593 if (logging != MagickFalse)
2594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2595 " image->background_color=(%.20g,%.20g,%.20g).",
2596 (double) image->background_color.red,
2597 (double) image->background_color.green,
2598 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002599 }
glennrpbfd9e612011-04-22 14:02:20 +00002600#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002601
glennrpfaa852b2010-03-30 12:17:00 +00002602 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002603 {
2604 /*
glennrpa6a06632011-01-19 15:15:34 +00002605 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002606 */
2607 int
2608 max_sample;
2609
cristy35ef8242010-06-03 16:24:13 +00002610 size_t
2611 one=1;
2612
cristy3ed852e2009-09-05 21:47:34 +00002613 if (logging != MagickFalse)
2614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2615 " Reading PNG tRNS chunk.");
2616
cristyf9cca6a2010-06-04 23:49:28 +00002617 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002618
glennrpfaa852b2010-03-30 12:17:00 +00002619 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2620 (int)ping_trans_color->gray > max_sample) ||
2621 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2622 ((int)ping_trans_color->red > max_sample ||
2623 (int)ping_trans_color->green > max_sample ||
2624 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002625 {
2626 if (logging != MagickFalse)
2627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2628 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002629 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002630 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002631 image->matte=MagickFalse;
2632 }
2633 else
2634 {
glennrpa6a06632011-01-19 15:15:34 +00002635 int
2636 scale_to_short;
2637
2638 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2639
2640 /* Scale transparent_color to short */
2641 transparent_color.red= scale_to_short*ping_trans_color->red;
2642 transparent_color.green= scale_to_short*ping_trans_color->green;
2643 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy16ea1392012-03-21 20:38:41 +00002644 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002645
glennrpfaa852b2010-03-30 12:17:00 +00002646 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002647 {
glennrp0f111982010-07-07 20:18:33 +00002648 if (logging != MagickFalse)
2649 {
2650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2651 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002652
glennrp0f111982010-07-07 20:18:33 +00002653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00002654 " scaled graylevel is %.20g.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002655 }
cristy16ea1392012-03-21 20:38:41 +00002656 transparent_color.red=transparent_color.alpha;
2657 transparent_color.green=transparent_color.alpha;
2658 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002659 }
2660 }
2661 }
2662#if defined(PNG_READ_sBIT_SUPPORTED)
2663 if (mng_info->have_global_sbit)
2664 {
glennrpfaa852b2010-03-30 12:17:00 +00002665 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002666 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2667 }
2668#endif
2669 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002670
cristy3ed852e2009-09-05 21:47:34 +00002671 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002672
2673 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2674
cristy3ed852e2009-09-05 21:47:34 +00002675 /*
2676 Initialize image structure.
2677 */
2678 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002679 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002680 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002681 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002682 if (mng_info->mng_type == 0)
2683 {
glennrpfaa852b2010-03-30 12:17:00 +00002684 mng_info->mng_width=ping_width;
2685 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002686 mng_info->frame=mng_info->image_box;
2687 mng_info->clip=mng_info->image_box;
2688 }
glennrp0fe50b42010-11-16 03:52:51 +00002689
cristy3ed852e2009-09-05 21:47:34 +00002690 else
2691 {
2692 image->page.y=mng_info->y_off[mng_info->object_id];
2693 }
glennrp0fe50b42010-11-16 03:52:51 +00002694
cristy3ed852e2009-09-05 21:47:34 +00002695 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002696 image->columns=ping_width;
2697 image->rows=ping_height;
cristy16ea1392012-03-21 20:38:41 +00002698 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2699 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2700 image->colorspace=GRAYColorspace;
glennrpfaa852b2010-03-30 12:17:00 +00002701 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002702 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002703 {
cristybefe4d22010-06-07 01:18:58 +00002704 size_t
2705 one;
2706
cristy3ed852e2009-09-05 21:47:34 +00002707 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002708 one=1;
2709 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002710#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2711 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002712 image->colors=256;
2713#else
2714 if (image->colors > 65536L)
2715 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002716#endif
glennrpfaa852b2010-03-30 12:17:00 +00002717 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002718 {
2719 int
2720 number_colors;
2721
2722 png_colorp
2723 palette;
2724
2725 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002726 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002727
cristy3ed852e2009-09-05 21:47:34 +00002728 if (logging != MagickFalse)
2729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2730 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2731 }
2732 }
2733
2734 if (image->storage_class == PseudoClass)
2735 {
2736 /*
2737 Initialize image colormap.
2738 */
cristy16ea1392012-03-21 20:38:41 +00002739 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
glennrpedaa0382012-04-12 14:16:21 +00002740 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00002741
glennrpfaa852b2010-03-30 12:17:00 +00002742 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002743 {
2744 int
2745 number_colors;
2746
2747 png_colorp
2748 palette;
2749
2750 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002751
glennrp6af6cf12011-04-22 13:05:16 +00002752 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002753 {
2754 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2755 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2756 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2757 }
glennrp6af6cf12011-04-22 13:05:16 +00002758
glennrp67b9c1a2011-04-22 18:47:36 +00002759 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002760 {
2761 image->colormap[i].red=0;
2762 image->colormap[i].green=0;
2763 image->colormap[i].blue=0;
2764 }
cristy3ed852e2009-09-05 21:47:34 +00002765 }
glennrp0fe50b42010-11-16 03:52:51 +00002766
cristy3ed852e2009-09-05 21:47:34 +00002767 else
2768 {
cristybb503372010-05-27 20:51:26 +00002769 size_t
cristy3ed852e2009-09-05 21:47:34 +00002770 scale;
2771
glennrpfaa852b2010-03-30 12:17:00 +00002772 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002773
cristy3ed852e2009-09-05 21:47:34 +00002774 if (scale < 1)
2775 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002776
cristybb503372010-05-27 20:51:26 +00002777 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002778 {
2779 image->colormap[i].red=(Quantum) (i*scale);
2780 image->colormap[i].green=(Quantum) (i*scale);
2781 image->colormap[i].blue=(Quantum) (i*scale);
2782 }
2783 }
2784 }
glennrp147bc912011-03-30 18:47:21 +00002785
glennrpcb395ac2011-03-30 19:50:23 +00002786 /* Set some properties for reporting by "identify" */
2787 {
glennrp147bc912011-03-30 18:47:21 +00002788 char
2789 msg[MaxTextExtent];
2790
2791 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2792 ping_interlace_method in value */
2793
cristy3b6fd2e2011-05-20 12:53:50 +00002794 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002795 "%d, %d",(int) ping_width, (int) ping_height);
cristy16ea1392012-03-21 20:38:41 +00002796 (void) SetImageProperty(image,"png:IHDR.width,height ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002797
cristy3b6fd2e2011-05-20 12:53:50 +00002798 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
cristy16ea1392012-03-21 20:38:41 +00002799 (void) SetImageProperty(image,"png:IHDR.bit_depth ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002800
cristy3b6fd2e2011-05-20 12:53:50 +00002801 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
cristy16ea1392012-03-21 20:38:41 +00002802 (void) SetImageProperty(image,"png:IHDR.color_type ",msg,exception);
glennrp147bc912011-03-30 18:47:21 +00002803
cristy3b6fd2e2011-05-20 12:53:50 +00002804 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002805 (int) ping_interlace_method);
cristy16ea1392012-03-21 20:38:41 +00002806 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
glennrpcb395ac2011-03-30 19:50:23 +00002807 }
glennrp147bc912011-03-30 18:47:21 +00002808
cristy3ed852e2009-09-05 21:47:34 +00002809 /*
2810 Read image scanlines.
2811 */
2812 if (image->delay != 0)
2813 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002814
glennrp0ca69b12010-07-26 01:57:52 +00002815 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002816 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2817 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002818 {
glennrp1b888c42012-03-02 15:18:14 +00002819 /* This happens later in non-ping decodes */
2820 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2821 image->storage_class=DirectClass;
2822
cristy3ed852e2009-09-05 21:47:34 +00002823 if (logging != MagickFalse)
2824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002825 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002826 mng_info->scenes_found-1);
2827 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpedaa0382012-04-12 14:16:21 +00002828
2829#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00002830 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002831#endif
glennrpedaa0382012-04-12 14:16:21 +00002832
cristy3ed852e2009-09-05 21:47:34 +00002833 if (logging != MagickFalse)
2834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2835 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002836
cristy3ed852e2009-09-05 21:47:34 +00002837 return(image);
2838 }
glennrp0fe50b42010-11-16 03:52:51 +00002839
cristy3ed852e2009-09-05 21:47:34 +00002840 if (logging != MagickFalse)
2841 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2842 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002843
cristy3ed852e2009-09-05 21:47:34 +00002844 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002845 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2846 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002847
cristy3ed852e2009-09-05 21:47:34 +00002848 else
glennrpcf002022011-01-30 02:38:15 +00002849 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2850 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002851
glennrpcf002022011-01-30 02:38:15 +00002852 if (ping_pixels == (unsigned char *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00002853 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00002854
cristy3ed852e2009-09-05 21:47:34 +00002855 if (logging != MagickFalse)
2856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2857 " Converting PNG pixels to pixel packets");
2858 /*
2859 Convert PNG pixels to pixel packets.
2860 */
cristy16ea1392012-03-21 20:38:41 +00002861 quantum_info=AcquireQuantumInfo(image_info,image);
2862
2863 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00002864 png_error(ping,"Failed to allocate quantum_info");
glennrp0fe50b42010-11-16 03:52:51 +00002865
glennrpc8cbc5d2011-01-01 00:12:34 +00002866 {
2867
2868 MagickBooleanType
2869 found_transparent_pixel;
2870
2871 found_transparent_pixel=MagickFalse;
2872
cristy3ed852e2009-09-05 21:47:34 +00002873 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002874 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002875 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002876 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002877 /*
2878 Convert image to DirectClass pixel packets.
2879 */
glennrp67b9c1a2011-04-22 18:47:36 +00002880#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2881 int
2882 depth;
2883
2884 depth=(ssize_t) ping_bit_depth;
2885#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002886 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2887 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2888 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2889 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002890
glennrpc8cbc5d2011-01-01 00:12:34 +00002891 for (y=0; y < (ssize_t) image->rows; y++)
2892 {
2893 if (num_passes > 1)
2894 row_offset=ping_rowbytes*y;
2895
2896 else
2897 row_offset=0;
2898
glennrpcf002022011-01-30 02:38:15 +00002899 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002900 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2901
cristy16ea1392012-03-21 20:38:41 +00002902 if (q == (Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002903 break;
2904
cristy16ea1392012-03-21 20:38:41 +00002905 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2906 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2907 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002908
cristy16ea1392012-03-21 20:38:41 +00002909 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2910 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2911 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002912
cristy16ea1392012-03-21 20:38:41 +00002913 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2914 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2915 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002916
cristy16ea1392012-03-21 20:38:41 +00002917 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2918 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2919 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002920
cristy16ea1392012-03-21 20:38:41 +00002921 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2922 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
2923 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002924
glennrpc8cbc5d2011-01-01 00:12:34 +00002925 if (found_transparent_pixel == MagickFalse)
2926 {
2927 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002928 if (y== 0 && logging != MagickFalse)
2929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2930 " Looking for cheap transparent pixel");
2931
glennrpc8cbc5d2011-01-01 00:12:34 +00002932 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2933 {
glennrp5aa37f62011-01-02 03:07:57 +00002934 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2935 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy16ea1392012-03-21 20:38:41 +00002936 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002937 {
glennrpa6a06632011-01-19 15:15:34 +00002938 if (logging != MagickFalse)
2939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2940 " ...got one.");
2941
glennrpc8cbc5d2011-01-01 00:12:34 +00002942 found_transparent_pixel = MagickTrue;
2943 break;
2944 }
glennrp4f25bd02011-01-01 18:51:28 +00002945 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2946 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
cristy16ea1392012-03-21 20:38:41 +00002947 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
2948 transparent_color.red &&
2949 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
2950 transparent_color.green &&
2951 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
2952 transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002953 {
glennrpa6a06632011-01-19 15:15:34 +00002954 if (logging != MagickFalse)
2955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2956 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002957 found_transparent_pixel = MagickTrue;
2958 break;
2959 }
cristy16ea1392012-03-21 20:38:41 +00002960 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002961 }
2962 }
2963
2964 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2965 {
2966 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2967 image->rows);
2968
2969 if (status == MagickFalse)
2970 break;
2971 }
2972 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2973 break;
2974 }
2975
2976 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2977 {
2978 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002979 if (status == MagickFalse)
2980 break;
2981 }
cristy3ed852e2009-09-05 21:47:34 +00002982 }
cristy3ed852e2009-09-05 21:47:34 +00002983 }
glennrp0fe50b42010-11-16 03:52:51 +00002984
cristy3ed852e2009-09-05 21:47:34 +00002985 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002986
cristy3ed852e2009-09-05 21:47:34 +00002987 for (pass=0; pass < num_passes; pass++)
2988 {
2989 Quantum
2990 *quantum_scanline;
2991
2992 register Quantum
2993 *r;
2994
2995 /*
2996 Convert grayscale image to PseudoClass pixel packets.
2997 */
glennrpc17d96f2011-06-27 01:20:11 +00002998 if (logging != MagickFalse)
2999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3000 " Converting grayscale pixels to pixel packets");
cristy16ea1392012-03-21 20:38:41 +00003001
glennrpfaa852b2010-03-30 12:17:00 +00003002 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00003003 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00003004
cristy3ed852e2009-09-05 21:47:34 +00003005 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3006 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00003007
cristy3ed852e2009-09-05 21:47:34 +00003008 if (quantum_scanline == (Quantum *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003009 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003010
cristybb503372010-05-27 20:51:26 +00003011 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00003012 {
3013 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00003014 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00003015
cristy3ed852e2009-09-05 21:47:34 +00003016 else
3017 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00003018
glennrpcf002022011-01-30 02:38:15 +00003019 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003020 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003021
cristy16ea1392012-03-21 20:38:41 +00003022 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003023 break;
glennrp0fe50b42010-11-16 03:52:51 +00003024
glennrpcf002022011-01-30 02:38:15 +00003025 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003026 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003027
glennrpfaa852b2010-03-30 12:17:00 +00003028 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003029 {
3030 case 1:
3031 {
cristybb503372010-05-27 20:51:26 +00003032 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003033 bit;
3034
cristybb503372010-05-27 20:51:26 +00003035 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003036 {
3037 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003038 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003039 p++;
3040 }
glennrp0fe50b42010-11-16 03:52:51 +00003041
cristy3ed852e2009-09-05 21:47:34 +00003042 if ((image->columns % 8) != 0)
3043 {
cristybb503372010-05-27 20:51:26 +00003044 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003045 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003046 }
glennrp0fe50b42010-11-16 03:52:51 +00003047
cristy3ed852e2009-09-05 21:47:34 +00003048 break;
3049 }
glennrp47b9dd52010-11-24 18:12:06 +00003050
cristy3ed852e2009-09-05 21:47:34 +00003051 case 2:
3052 {
cristybb503372010-05-27 20:51:26 +00003053 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003054 {
glennrpa18d5bc2011-04-23 14:51:34 +00003055 *r++=(*p >> 6) & 0x03;
3056 *r++=(*p >> 4) & 0x03;
3057 *r++=(*p >> 2) & 0x03;
3058 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003059 }
glennrp0fe50b42010-11-16 03:52:51 +00003060
cristy3ed852e2009-09-05 21:47:34 +00003061 if ((image->columns % 4) != 0)
3062 {
cristybb503372010-05-27 20:51:26 +00003063 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003064 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003065 }
glennrp0fe50b42010-11-16 03:52:51 +00003066
cristy3ed852e2009-09-05 21:47:34 +00003067 break;
3068 }
glennrp47b9dd52010-11-24 18:12:06 +00003069
cristy3ed852e2009-09-05 21:47:34 +00003070 case 4:
3071 {
cristybb503372010-05-27 20:51:26 +00003072 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003073 {
glennrpa18d5bc2011-04-23 14:51:34 +00003074 *r++=(*p >> 4) & 0x0f;
3075 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003076 }
glennrp0fe50b42010-11-16 03:52:51 +00003077
cristy3ed852e2009-09-05 21:47:34 +00003078 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003079 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003080
cristy3ed852e2009-09-05 21:47:34 +00003081 break;
3082 }
glennrp47b9dd52010-11-24 18:12:06 +00003083
cristy3ed852e2009-09-05 21:47:34 +00003084 case 8:
3085 {
glennrpfaa852b2010-03-30 12:17:00 +00003086 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003087 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003088 {
glennrpa18d5bc2011-04-23 14:51:34 +00003089 *r++=*p++;
cristy16ea1392012-03-21 20:38:41 +00003090 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3091 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003092 found_transparent_pixel = MagickTrue;
cristy16ea1392012-03-21 20:38:41 +00003093 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003094 }
glennrp0fe50b42010-11-16 03:52:51 +00003095
cristy3ed852e2009-09-05 21:47:34 +00003096 else
cristybb503372010-05-27 20:51:26 +00003097 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003098 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003099
cristy3ed852e2009-09-05 21:47:34 +00003100 break;
3101 }
glennrp47b9dd52010-11-24 18:12:06 +00003102
cristy3ed852e2009-09-05 21:47:34 +00003103 case 16:
3104 {
cristybb503372010-05-27 20:51:26 +00003105 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003106 {
glennrpc17d96f2011-06-27 01:20:11 +00003107#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003108 size_t
3109 quantum;
3110
3111 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003112 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003113
3114 else
glennrpc17d96f2011-06-27 01:20:11 +00003115 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003116
glennrp58f77c72011-04-23 14:09:09 +00003117 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003118 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003119 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003120
3121 if (ping_color_type == 4)
3122 {
glennrpc17d96f2011-06-27 01:20:11 +00003123 if (image->colors > 256)
3124 quantum=((*p++) << 8);
3125 else
3126 quantum=0;
3127
glennrp58f77c72011-04-23 14:09:09 +00003128 quantum|=(*p++);
cristy16ea1392012-03-21 20:38:41 +00003129 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3130 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003131 found_transparent_pixel = MagickTrue;
cristy16ea1392012-03-21 20:38:41 +00003132 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003133 }
glennrp58f77c72011-04-23 14:09:09 +00003134
3135#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3136 *r++=(*p++);
3137 p++; /* strip low byte */
3138
3139 if (ping_color_type == 4)
3140 {
cristy16ea1392012-03-21 20:38:41 +00003141 SetPixelAlpha(image,*p++,q);
3142 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003143 found_transparent_pixel = MagickTrue;
3144 p++;
cristy16ea1392012-03-21 20:38:41 +00003145 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003146 }
cristy3ed852e2009-09-05 21:47:34 +00003147#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003148 }
glennrp47b9dd52010-11-24 18:12:06 +00003149
cristy3ed852e2009-09-05 21:47:34 +00003150 break;
3151 }
glennrp47b9dd52010-11-24 18:12:06 +00003152
cristy3ed852e2009-09-05 21:47:34 +00003153 default:
3154 break;
3155 }
glennrp3faa9a32011-04-23 14:00:25 +00003156
cristy3ed852e2009-09-05 21:47:34 +00003157 /*
3158 Transfer image scanline.
3159 */
3160 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003161
cristy16ea1392012-03-21 20:38:41 +00003162 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3163
3164 if (q == (Quantum *) NULL)
3165 break;
cristybb503372010-05-27 20:51:26 +00003166 for (x=0; x < (ssize_t) image->columns; x++)
cristy16ea1392012-03-21 20:38:41 +00003167 {
3168 SetPixelIndex(image,*r++,q);
3169 q+=GetPixelChannels(image);
3170 }
glennrp0fe50b42010-11-16 03:52:51 +00003171
cristy3ed852e2009-09-05 21:47:34 +00003172 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3173 break;
glennrp0fe50b42010-11-16 03:52:51 +00003174
cristy7a287bf2010-02-14 02:18:09 +00003175 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3176 {
cristycee97112010-05-28 00:44:52 +00003177 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003178 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003179
cristy7a287bf2010-02-14 02:18:09 +00003180 if (status == MagickFalse)
3181 break;
3182 }
cristy3ed852e2009-09-05 21:47:34 +00003183 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003184
cristy7a287bf2010-02-14 02:18:09 +00003185 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003186 {
3187 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003188
cristy3ed852e2009-09-05 21:47:34 +00003189 if (status == MagickFalse)
3190 break;
3191 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003192
cristy3ed852e2009-09-05 21:47:34 +00003193 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3194 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003195
3196 image->matte=found_transparent_pixel;
3197
3198 if (logging != MagickFalse)
3199 {
3200 if (found_transparent_pixel != MagickFalse)
3201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3202 " Found transparent pixel");
3203 else
glennrp5aa37f62011-01-02 03:07:57 +00003204 {
3205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3206 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003207
glennrp5aa37f62011-01-02 03:07:57 +00003208 ping_color_type&=0x03;
3209 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003210 }
3211 }
3212
cristy16ea1392012-03-21 20:38:41 +00003213 if (quantum_info != (QuantumInfo *) NULL)
3214 quantum_info=DestroyQuantumInfo(quantum_info);
3215
cristy5c6f7892010-05-05 22:53:29 +00003216 if (image->storage_class == PseudoClass)
3217 {
cristyaeb2cbc2010-05-07 13:28:58 +00003218 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003219 matte;
3220
3221 matte=image->matte;
3222 image->matte=MagickFalse;
cristy16ea1392012-03-21 20:38:41 +00003223 (void) SyncImage(image,exception);
cristyaeb2cbc2010-05-07 13:28:58 +00003224 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003225 }
glennrp47b9dd52010-11-24 18:12:06 +00003226
glennrp4eb39312011-03-30 21:34:55 +00003227 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003228
3229 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003230 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003231 {
3232 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003233 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003234 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00003235 (void) SetImageBackgroundColor(image,exception);
glennrpedaa0382012-04-12 14:16:21 +00003236#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00003237 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003238#endif
3239 if (logging != MagickFalse)
3240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3241 " exit ReadOnePNGImage() early.");
3242 return(image);
3243 }
glennrp47b9dd52010-11-24 18:12:06 +00003244
glennrpfaa852b2010-03-30 12:17:00 +00003245 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003246 {
3247 ClassType
3248 storage_class;
3249
3250 /*
3251 Image has a transparent background.
3252 */
3253 storage_class=image->storage_class;
3254 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003255
glennrp3c218112010-11-27 15:31:26 +00003256/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003257
glennrp0fe50b42010-11-16 03:52:51 +00003258 if (storage_class == PseudoClass)
3259 {
3260 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003261 {
glennrp0fe50b42010-11-16 03:52:51 +00003262 for (x=0; x < ping_num_trans; x++)
3263 {
cristy16ea1392012-03-21 20:38:41 +00003264 image->colormap[x].matte=MagickTrue;
3265 image->colormap[x].alpha =
3266 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003267 }
glennrpc11cf6a2010-03-20 16:46:19 +00003268 }
glennrp47b9dd52010-11-24 18:12:06 +00003269
glennrp0fe50b42010-11-16 03:52:51 +00003270 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3271 {
3272 for (x=0; x < (int) image->colors; x++)
3273 {
3274 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy16ea1392012-03-21 20:38:41 +00003275 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003276 {
cristy16ea1392012-03-21 20:38:41 +00003277 image->colormap[x].matte=MagickTrue;
3278 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003279 }
3280 }
3281 }
cristy16ea1392012-03-21 20:38:41 +00003282 (void) SyncImage(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003283 }
glennrp47b9dd52010-11-24 18:12:06 +00003284
glennrpa6a06632011-01-19 15:15:34 +00003285#if 1 /* Should have already been done above, but glennrp problem P10
3286 * needs this.
3287 */
glennrp0fe50b42010-11-16 03:52:51 +00003288 else
3289 {
3290 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003291 {
glennrp0fe50b42010-11-16 03:52:51 +00003292 image->storage_class=storage_class;
3293 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3294
cristy16ea1392012-03-21 20:38:41 +00003295 if (q == (Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003296 break;
3297
glennrp0fe50b42010-11-16 03:52:51 +00003298
glennrpa6a06632011-01-19 15:15:34 +00003299 /* Caution: on a Q8 build, this does not distinguish between
3300 * 16-bit colors that differ only in the low byte
3301 */
glennrp0fe50b42010-11-16 03:52:51 +00003302 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3303 {
cristy16ea1392012-03-21 20:38:41 +00003304 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3305 transparent_color.red &&
3306 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3307 transparent_color.green &&
3308 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3309 transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003310 {
cristy16ea1392012-03-21 20:38:41 +00003311 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003312 }
glennrp0fe50b42010-11-16 03:52:51 +00003313
glennrp67b9c1a2011-04-22 18:47:36 +00003314#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003315 else
glennrp4f25bd02011-01-01 18:51:28 +00003316 {
cristy16ea1392012-03-21 20:38:41 +00003317 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003318 }
glennrpa6a06632011-01-19 15:15:34 +00003319#endif
glennrp0fe50b42010-11-16 03:52:51 +00003320
cristy16ea1392012-03-21 20:38:41 +00003321 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003322 }
3323
3324 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3325 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003326 }
glennrp0fe50b42010-11-16 03:52:51 +00003327 }
glennrpa6a06632011-01-19 15:15:34 +00003328#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003329
cristy3ed852e2009-09-05 21:47:34 +00003330 image->storage_class=DirectClass;
3331 }
glennrp3c218112010-11-27 15:31:26 +00003332
cristyeb3b22a2011-03-31 20:16:11 +00003333 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003334 {
3335 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003336 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3337 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003338 else
glennrpa0ed0092011-04-18 16:36:29 +00003339 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3340 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003341
glennrp4eb39312011-03-30 21:34:55 +00003342 if (status != MagickFalse)
3343 for (i=0; i < (ssize_t) num_text; i++)
3344 {
3345 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003346
glennrp4eb39312011-03-30 21:34:55 +00003347 if (logging != MagickFalse)
3348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3349 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003350
glennrp4eb39312011-03-30 21:34:55 +00003351 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003352 {
glennrpedaa0382012-04-12 14:16:21 +00003353 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3354 (int) i,exception);
glennrp4eb39312011-03-30 21:34:55 +00003355 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003356 }
glennrp0fe50b42010-11-16 03:52:51 +00003357
glennrp4eb39312011-03-30 21:34:55 +00003358 else
3359 {
3360 char
3361 *value;
3362
3363 length=text[i].text_length;
3364 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3365 sizeof(*value));
3366 if (value == (char *) NULL)
3367 {
glennrpedaa0382012-04-12 14:16:21 +00003368 png_error(ping,"Memory allocation failed");
glennrp4eb39312011-03-30 21:34:55 +00003369 break;
3370 }
3371 *value='\0';
3372 (void) ConcatenateMagickString(value,text[i].text,length+2);
3373
3374 /* Don't save "density" or "units" property if we have a pHYs
3375 * chunk
3376 */
3377 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3378 (LocaleCompare(text[i].key,"density") != 0 &&
3379 LocaleCompare(text[i].key,"units") != 0))
cristy16ea1392012-03-21 20:38:41 +00003380 (void) SetImageProperty(image,text[i].key,value,exception);
glennrp4eb39312011-03-30 21:34:55 +00003381
3382 if (logging != MagickFalse)
3383 {
3384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3385 " length: %lu",(unsigned long) length);
3386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3387 " Keyword: %s",text[i].key);
3388 }
3389
3390 value=DestroyString(value);
3391 }
3392 }
3393 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003394 }
glennrp3c218112010-11-27 15:31:26 +00003395
cristy3ed852e2009-09-05 21:47:34 +00003396#ifdef MNG_OBJECT_BUFFERS
3397 /*
3398 Store the object if necessary.
3399 */
3400 if (object_id && !mng_info->frozen[object_id])
3401 {
3402 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3403 {
3404 /*
3405 create a new object buffer.
3406 */
3407 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003408 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003409
cristy3ed852e2009-09-05 21:47:34 +00003410 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3411 {
3412 mng_info->ob[object_id]->image=(Image *) NULL;
3413 mng_info->ob[object_id]->reference_count=1;
3414 }
3415 }
glennrp47b9dd52010-11-24 18:12:06 +00003416
cristy3ed852e2009-09-05 21:47:34 +00003417 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3418 mng_info->ob[object_id]->frozen)
3419 {
3420 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
glennrpedaa0382012-04-12 14:16:21 +00003421 png_error(ping,"Memory allocation failed");
glennrp0fe50b42010-11-16 03:52:51 +00003422
cristy3ed852e2009-09-05 21:47:34 +00003423 if (mng_info->ob[object_id]->frozen)
glennrpedaa0382012-04-12 14:16:21 +00003424 png_error(ping,"Cannot overwrite frozen MNG object buffer");
cristy3ed852e2009-09-05 21:47:34 +00003425 }
glennrp0fe50b42010-11-16 03:52:51 +00003426
cristy3ed852e2009-09-05 21:47:34 +00003427 else
3428 {
cristy3ed852e2009-09-05 21:47:34 +00003429
3430 if (mng_info->ob[object_id]->image != (Image *) NULL)
3431 mng_info->ob[object_id]->image=DestroyImage
3432 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003433
cristy3ed852e2009-09-05 21:47:34 +00003434 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
cristy16ea1392012-03-21 20:38:41 +00003435 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003436
cristy3ed852e2009-09-05 21:47:34 +00003437 if (mng_info->ob[object_id]->image != (Image *) NULL)
3438 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003439
cristy3ed852e2009-09-05 21:47:34 +00003440 else
glennrpedaa0382012-04-12 14:16:21 +00003441 png_error(ping, "Cloning image for object buffer failed");
glennrp0fe50b42010-11-16 03:52:51 +00003442
glennrpfaa852b2010-03-30 12:17:00 +00003443 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003444 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003445
glennrpfaa852b2010-03-30 12:17:00 +00003446 mng_info->ob[object_id]->width=ping_width;
3447 mng_info->ob[object_id]->height=ping_height;
3448 mng_info->ob[object_id]->color_type=ping_color_type;
3449 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3450 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3451 mng_info->ob[object_id]->compression_method=
3452 ping_compression_method;
3453 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003454
glennrpfaa852b2010-03-30 12:17:00 +00003455 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003456 {
3457 int
3458 number_colors;
3459
3460 png_colorp
3461 plte;
3462
3463 /*
3464 Copy the PLTE to the object buffer.
3465 */
3466 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3467 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003468
cristy3ed852e2009-09-05 21:47:34 +00003469 for (i=0; i < number_colors; i++)
3470 {
3471 mng_info->ob[object_id]->plte[i]=plte[i];
3472 }
3473 }
glennrp47b9dd52010-11-24 18:12:06 +00003474
cristy3ed852e2009-09-05 21:47:34 +00003475 else
3476 mng_info->ob[object_id]->plte_length=0;
3477 }
3478 }
3479#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003480
3481 /* Set image->matte to MagickTrue if the input colortype supports
3482 * alpha or if a valid tRNS chunk is present, no matter whether there
3483 * is actual transparency present.
3484 */
3485 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3486 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3487 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3488 MagickTrue : MagickFalse;
3489
glennrpcb395ac2011-03-30 19:50:23 +00003490 /* Set more properties for identify to retrieve */
3491 {
3492 char
3493 msg[MaxTextExtent];
3494
glennrp4eb39312011-03-30 21:34:55 +00003495 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003496 {
3497 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003498 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003499 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
cristy16ea1392012-03-21 20:38:41 +00003500 (void) SetImageProperty(image,"png:text ",msg,
3501 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003502 }
3503
3504 if (num_raw_profiles != 0)
3505 {
cristy3b6fd2e2011-05-20 12:53:50 +00003506 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003507 "%d were found", num_raw_profiles);
cristy16ea1392012-03-21 20:38:41 +00003508 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3509 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003510 }
3511
glennrpcb395ac2011-03-30 19:50:23 +00003512 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003513 {
cristy3b6fd2e2011-05-20 12:53:50 +00003514 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003515 "chunk was found (see Chromaticity, above)");
cristy16ea1392012-03-21 20:38:41 +00003516 (void) SetImageProperty(image,"png:cHRM ",msg,
3517 exception);
glennrp59612252011-03-30 21:45:21 +00003518 }
glennrpcb395ac2011-03-30 19:50:23 +00003519
3520 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003521 {
cristy3b6fd2e2011-05-20 12:53:50 +00003522 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003523 "chunk was found (see Background color, above)");
cristy16ea1392012-03-21 20:38:41 +00003524 (void) SetImageProperty(image,"png:bKGD ",msg,
3525 exception);
glennrp59612252011-03-30 21:45:21 +00003526 }
3527
cristy3b6fd2e2011-05-20 12:53:50 +00003528 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003529 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003530
3531 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy16ea1392012-03-21 20:38:41 +00003532 (void) SetImageProperty(image,"png:iCCP ",msg,
3533 exception);
glennrpcb395ac2011-03-30 19:50:23 +00003534
glennrpcb395ac2011-03-30 19:50:23 +00003535 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy16ea1392012-03-21 20:38:41 +00003536 (void) SetImageProperty(image,"png:tRNS ",msg,
3537 exception);
glennrp4eb39312011-03-30 21:34:55 +00003538
3539#if defined(PNG_sRGB_SUPPORTED)
3540 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3541 {
cristy3b6fd2e2011-05-20 12:53:50 +00003542 (void) FormatLocaleString(msg,MaxTextExtent,
cristy16ea1392012-03-21 20:38:41 +00003543 "intent=%d (See Rendering intent)", (int) intent);
3544 (void) SetImageProperty(image,"png:sRGB ",msg,
3545 exception);
glennrp4eb39312011-03-30 21:34:55 +00003546 }
3547#endif
3548
3549 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3550 {
cristy3b6fd2e2011-05-20 12:53:50 +00003551 (void) FormatLocaleString(msg,MaxTextExtent,
cristy16ea1392012-03-21 20:38:41 +00003552 "gamma=%.8g (See Gamma, above)",
3553 file_gamma);
3554 (void) SetImageProperty(image,"png:gAMA ",msg,
3555 exception);
glennrp4eb39312011-03-30 21:34:55 +00003556 }
3557
3558#if defined(PNG_pHYs_SUPPORTED)
3559 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3560 {
cristy3b6fd2e2011-05-20 12:53:50 +00003561 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003562 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003563 (double) x_resolution,(double) y_resolution, unit_type);
cristy16ea1392012-03-21 20:38:41 +00003564 (void) SetImageProperty(image,"png:pHYs ",msg,
3565 exception);
glennrp4eb39312011-03-30 21:34:55 +00003566 }
3567#endif
3568
3569#if defined(PNG_oFFs_SUPPORTED)
3570 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3571 {
cristy3b6fd2e2011-05-20 12:53:50 +00003572 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003573 (double) image->page.x,(double) image->page.y);
cristy16ea1392012-03-21 20:38:41 +00003574 (void) SetImageProperty(image,"png:oFFs ",msg,
3575 exception);
glennrp4eb39312011-03-30 21:34:55 +00003576 }
3577#endif
3578
glennrp07523c72011-03-31 18:12:10 +00003579 if ((image->page.width != 0 && image->page.width != image->columns) ||
3580 (image->page.height != 0 && image->page.height != image->rows))
3581 {
cristy3b6fd2e2011-05-20 12:53:50 +00003582 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003583 "width=%.20g, height=%.20g",
3584 (double) image->page.width,(double) image->page.height);
cristy16ea1392012-03-21 20:38:41 +00003585 (void) SetImageProperty(image,"png:vpAg ",msg,
3586 exception);
glennrp07523c72011-03-31 18:12:10 +00003587 }
glennrpcb395ac2011-03-30 19:50:23 +00003588 }
3589
cristy3ed852e2009-09-05 21:47:34 +00003590 /*
3591 Relinquish resources.
3592 */
3593 png_destroy_read_struct(&ping,&ping_info,&end_info);
3594
glennrpcf002022011-01-30 02:38:15 +00003595 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003596
3597 if (logging != MagickFalse)
3598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3599 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003600
glennrpedaa0382012-04-12 14:16:21 +00003601#ifdef PNG_SETJMP_NOT_THREAD_SAFE
3602 UnlockSemaphoreInfo(ping_semaphore);
3603#endif
3604
3605 /* } for navigation to beginning of SETJMP-protected block, revert to
3606 * Throwing an Exception when an error occurs.
3607 */
3608
cristy3ed852e2009-09-05 21:47:34 +00003609 return(image);
3610
3611/* end of reading one PNG image */
3612}
3613
3614static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3615{
3616 Image
3617 *image,
3618 *previous;
3619
3620 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003621 have_mng_structure,
3622 logging,
cristy3ed852e2009-09-05 21:47:34 +00003623 status;
3624
3625 MngInfo
3626 *mng_info;
3627
3628 char
3629 magic_number[MaxTextExtent];
3630
cristy3ed852e2009-09-05 21:47:34 +00003631 ssize_t
3632 count;
3633
3634 /*
3635 Open image file.
3636 */
3637 assert(image_info != (const ImageInfo *) NULL);
3638 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003639
cristy3ed852e2009-09-05 21:47:34 +00003640 if (image_info->debug != MagickFalse)
3641 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3642 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003643
cristy3ed852e2009-09-05 21:47:34 +00003644 assert(exception != (ExceptionInfo *) NULL);
3645 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003646 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy16ea1392012-03-21 20:38:41 +00003647 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00003648 mng_info=(MngInfo *) NULL;
3649 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003650
cristy3ed852e2009-09-05 21:47:34 +00003651 if (status == MagickFalse)
3652 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003653
cristy3ed852e2009-09-05 21:47:34 +00003654 /*
3655 Verify PNG signature.
3656 */
3657 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003658
glennrpdde35db2011-02-21 12:06:32 +00003659 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003660 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003661
cristy3ed852e2009-09-05 21:47:34 +00003662 /*
3663 Allocate a MngInfo structure.
3664 */
3665 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003666 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003667
cristy3ed852e2009-09-05 21:47:34 +00003668 if (mng_info == (MngInfo *) NULL)
3669 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003670
cristy3ed852e2009-09-05 21:47:34 +00003671 /*
3672 Initialize members of the MngInfo structure.
3673 */
3674 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3675 mng_info->image=image;
3676 have_mng_structure=MagickTrue;
3677
3678 previous=image;
3679 image=ReadOnePNGImage(mng_info,image_info,exception);
3680 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003681
cristy3ed852e2009-09-05 21:47:34 +00003682 if (image == (Image *) NULL)
3683 {
3684 if (previous != (Image *) NULL)
3685 {
3686 if (previous->signature != MagickSignature)
3687 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003688
cristy3ed852e2009-09-05 21:47:34 +00003689 (void) CloseBlob(previous);
3690 (void) DestroyImageList(previous);
3691 }
glennrp0fe50b42010-11-16 03:52:51 +00003692
cristy3ed852e2009-09-05 21:47:34 +00003693 if (logging != MagickFalse)
3694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3695 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003696
cristy3ed852e2009-09-05 21:47:34 +00003697 return((Image *) NULL);
3698 }
glennrp47b9dd52010-11-24 18:12:06 +00003699
cristy3ed852e2009-09-05 21:47:34 +00003700 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003701
cristy3ed852e2009-09-05 21:47:34 +00003702 if ((image->columns == 0) || (image->rows == 0))
3703 {
3704 if (logging != MagickFalse)
3705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3706 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003707
cristy3ed852e2009-09-05 21:47:34 +00003708 ThrowReaderException(CorruptImageError,"CorruptImage");
3709 }
glennrp47b9dd52010-11-24 18:12:06 +00003710
cristy3ed852e2009-09-05 21:47:34 +00003711 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3712 {
cristy16ea1392012-03-21 20:38:41 +00003713 (void) SetImageType(image,TrueColorType,exception);
cristy3ed852e2009-09-05 21:47:34 +00003714 image->matte=MagickFalse;
3715 }
glennrp0fe50b42010-11-16 03:52:51 +00003716
cristy3ed852e2009-09-05 21:47:34 +00003717 if (LocaleCompare(image_info->magick,"PNG32") == 0)
cristy16ea1392012-03-21 20:38:41 +00003718 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003719
cristy3ed852e2009-09-05 21:47:34 +00003720 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3722 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3723 (double) image->page.width,(double) image->page.height,
3724 (double) image->page.x,(double) image->page.y);
3725
3726 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003728
cristy3ed852e2009-09-05 21:47:34 +00003729 return(image);
3730}
3731
3732
3733
3734#if defined(JNG_SUPPORTED)
3735/*
3736%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3737% %
3738% %
3739% %
3740% R e a d O n e J N G I m a g e %
3741% %
3742% %
3743% %
3744%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3745%
3746% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3747% (minus the 8-byte signature) and returns it. It allocates the memory
3748% necessary for the new Image structure and returns a pointer to the new
3749% image.
3750%
3751% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3752%
3753% The format of the ReadOneJNGImage method is:
3754%
3755% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3756% ExceptionInfo *exception)
3757%
3758% A description of each parameter follows:
3759%
3760% o mng_info: Specifies a pointer to a MngInfo structure.
3761%
3762% o image_info: the image info.
3763%
3764% o exception: return any errors or warnings in this structure.
3765%
3766*/
3767static Image *ReadOneJNGImage(MngInfo *mng_info,
3768 const ImageInfo *image_info, ExceptionInfo *exception)
3769{
3770 Image
3771 *alpha_image,
3772 *color_image,
3773 *image,
3774 *jng_image;
3775
3776 ImageInfo
3777 *alpha_image_info,
3778 *color_image_info;
3779
cristy4383ec82011-01-05 15:42:32 +00003780 MagickBooleanType
3781 logging;
3782
cristybb503372010-05-27 20:51:26 +00003783 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003784 y;
3785
3786 MagickBooleanType
3787 status;
3788
3789 png_uint_32
3790 jng_height,
3791 jng_width;
3792
3793 png_byte
3794 jng_color_type,
3795 jng_image_sample_depth,
3796 jng_image_compression_method,
3797 jng_image_interlace_method,
3798 jng_alpha_sample_depth,
3799 jng_alpha_compression_method,
3800 jng_alpha_filter_method,
3801 jng_alpha_interlace_method;
3802
cristy16ea1392012-03-21 20:38:41 +00003803 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003804 *s;
3805
cristybb503372010-05-27 20:51:26 +00003806 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003807 i,
3808 x;
3809
cristy16ea1392012-03-21 20:38:41 +00003810 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003811 *q;
3812
3813 register unsigned char
3814 *p;
3815
3816 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003817 read_JSEP,
3818 reading_idat,
3819 skip_to_iend;
3820
cristybb503372010-05-27 20:51:26 +00003821 size_t
cristy3ed852e2009-09-05 21:47:34 +00003822 length;
3823
3824 jng_alpha_compression_method=0;
3825 jng_alpha_sample_depth=8;
3826 jng_color_type=0;
3827 jng_height=0;
3828 jng_width=0;
3829 alpha_image=(Image *) NULL;
3830 color_image=(Image *) NULL;
3831 alpha_image_info=(ImageInfo *) NULL;
3832 color_image_info=(ImageInfo *) NULL;
3833
3834 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003835 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003836
3837 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003838
cristy16ea1392012-03-21 20:38:41 +00003839 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003840 {
3841 /*
3842 Allocate next image structure.
3843 */
3844 if (logging != MagickFalse)
3845 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3846 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003847
cristy16ea1392012-03-21 20:38:41 +00003848 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003849
cristy3ed852e2009-09-05 21:47:34 +00003850 if (GetNextImageInList(image) == (Image *) NULL)
3851 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003852
cristy3ed852e2009-09-05 21:47:34 +00003853 image=SyncNextImageInList(image);
3854 }
3855 mng_info->image=image;
3856
3857 /*
3858 Signature bytes have already been read.
3859 */
3860
3861 read_JSEP=MagickFalse;
3862 reading_idat=MagickFalse;
3863 skip_to_iend=MagickFalse;
3864 for (;;)
3865 {
3866 char
3867 type[MaxTextExtent];
3868
3869 unsigned char
3870 *chunk;
3871
3872 unsigned int
3873 count;
3874
3875 /*
3876 Read a new JNG chunk.
3877 */
3878 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3879 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003880
cristy3ed852e2009-09-05 21:47:34 +00003881 if (status == MagickFalse)
3882 break;
glennrp0fe50b42010-11-16 03:52:51 +00003883
cristy3ed852e2009-09-05 21:47:34 +00003884 type[0]='\0';
3885 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3886 length=ReadBlobMSBLong(image);
3887 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3888
3889 if (logging != MagickFalse)
3890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003891 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3892 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003893
3894 if (length > PNG_UINT_31_MAX || count == 0)
3895 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003896
cristy3ed852e2009-09-05 21:47:34 +00003897 p=NULL;
3898 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003899
cristy3ed852e2009-09-05 21:47:34 +00003900 if (length)
3901 {
3902 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003903
cristy3ed852e2009-09-05 21:47:34 +00003904 if (chunk == (unsigned char *) NULL)
3905 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003906
cristybb503372010-05-27 20:51:26 +00003907 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003908 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003909
cristy3ed852e2009-09-05 21:47:34 +00003910 p=chunk;
3911 }
glennrp47b9dd52010-11-24 18:12:06 +00003912
cristy3ed852e2009-09-05 21:47:34 +00003913 (void) ReadBlobMSBLong(image); /* read crc word */
3914
3915 if (skip_to_iend)
3916 {
3917 if (length)
3918 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003919
cristy3ed852e2009-09-05 21:47:34 +00003920 continue;
3921 }
3922
3923 if (memcmp(type,mng_JHDR,4) == 0)
3924 {
3925 if (length == 16)
3926 {
cristybb503372010-05-27 20:51:26 +00003927 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003928 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003929 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003930 (p[6] << 8) | p[7]);
3931 jng_color_type=p[8];
3932 jng_image_sample_depth=p[9];
3933 jng_image_compression_method=p[10];
3934 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003935
cristy3ed852e2009-09-05 21:47:34 +00003936 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3937 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003938
cristy3ed852e2009-09-05 21:47:34 +00003939 jng_alpha_sample_depth=p[12];
3940 jng_alpha_compression_method=p[13];
3941 jng_alpha_filter_method=p[14];
3942 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003943
cristy3ed852e2009-09-05 21:47:34 +00003944 if (logging != MagickFalse)
3945 {
3946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003947 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003948
cristy3ed852e2009-09-05 21:47:34 +00003949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003950 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003951
cristy3ed852e2009-09-05 21:47:34 +00003952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3953 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003954
cristy3ed852e2009-09-05 21:47:34 +00003955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3956 " jng_image_sample_depth: %3d",
3957 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003958
cristy3ed852e2009-09-05 21:47:34 +00003959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3960 " jng_image_compression_method:%3d",
3961 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003962
cristy3ed852e2009-09-05 21:47:34 +00003963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3964 " jng_image_interlace_method: %3d",
3965 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003966
cristy3ed852e2009-09-05 21:47:34 +00003967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3968 " jng_alpha_sample_depth: %3d",
3969 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003970
cristy3ed852e2009-09-05 21:47:34 +00003971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3972 " jng_alpha_compression_method:%3d",
3973 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003974
cristy3ed852e2009-09-05 21:47:34 +00003975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3976 " jng_alpha_filter_method: %3d",
3977 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003978
cristy3ed852e2009-09-05 21:47:34 +00003979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3980 " jng_alpha_interlace_method: %3d",
3981 jng_alpha_interlace_method);
3982 }
3983 }
glennrp47b9dd52010-11-24 18:12:06 +00003984
cristy3ed852e2009-09-05 21:47:34 +00003985 if (length)
3986 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003987
cristy3ed852e2009-09-05 21:47:34 +00003988 continue;
3989 }
3990
3991
3992 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3993 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3994 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3995 {
3996 /*
3997 o create color_image
3998 o open color_blob, attached to color_image
3999 o if (color type has alpha)
4000 open alpha_blob, attached to alpha_image
4001 */
4002
cristy73bd4a52010-10-05 11:24:23 +00004003 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00004004
cristy3ed852e2009-09-05 21:47:34 +00004005 if (color_image_info == (ImageInfo *) NULL)
4006 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004007
cristy3ed852e2009-09-05 21:47:34 +00004008 GetImageInfo(color_image_info);
cristy16ea1392012-03-21 20:38:41 +00004009 color_image=AcquireImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004010
cristy3ed852e2009-09-05 21:47:34 +00004011 if (color_image == (Image *) NULL)
4012 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4013
4014 if (logging != MagickFalse)
4015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4016 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004017
cristy3ed852e2009-09-05 21:47:34 +00004018 (void) AcquireUniqueFilename(color_image->filename);
4019 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4020 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004021
cristy3ed852e2009-09-05 21:47:34 +00004022 if (status == MagickFalse)
4023 return((Image *) NULL);
4024
4025 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4026 {
4027 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00004028 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004029
cristy3ed852e2009-09-05 21:47:34 +00004030 if (alpha_image_info == (ImageInfo *) NULL)
4031 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004032
cristy3ed852e2009-09-05 21:47:34 +00004033 GetImageInfo(alpha_image_info);
cristy16ea1392012-03-21 20:38:41 +00004034 alpha_image=AcquireImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004035
cristy3ed852e2009-09-05 21:47:34 +00004036 if (alpha_image == (Image *) NULL)
4037 {
4038 alpha_image=DestroyImage(alpha_image);
4039 ThrowReaderException(ResourceLimitError,
4040 "MemoryAllocationFailed");
4041 }
glennrp0fe50b42010-11-16 03:52:51 +00004042
cristy3ed852e2009-09-05 21:47:34 +00004043 if (logging != MagickFalse)
4044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4045 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004046
cristy3ed852e2009-09-05 21:47:34 +00004047 (void) AcquireUniqueFilename(alpha_image->filename);
4048 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4049 exception);
glennrp0fe50b42010-11-16 03:52:51 +00004050
cristy3ed852e2009-09-05 21:47:34 +00004051 if (status == MagickFalse)
4052 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004053
cristy3ed852e2009-09-05 21:47:34 +00004054 if (jng_alpha_compression_method == 0)
4055 {
4056 unsigned char
4057 data[18];
4058
4059 if (logging != MagickFalse)
4060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4061 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004062
cristy3ed852e2009-09-05 21:47:34 +00004063 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4064 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004065
cristy3ed852e2009-09-05 21:47:34 +00004066 (void) WriteBlobMSBULong(alpha_image,13L);
4067 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004068 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004069 PNGLong(data+4,jng_width);
4070 PNGLong(data+8,jng_height);
4071 data[12]=jng_alpha_sample_depth;
4072 data[13]=0; /* color_type gray */
4073 data[14]=0; /* compression method 0 */
4074 data[15]=0; /* filter_method 0 */
4075 data[16]=0; /* interlace_method 0 */
4076 (void) WriteBlob(alpha_image,17,data);
4077 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4078 }
4079 }
4080 reading_idat=MagickTrue;
4081 }
4082
4083 if (memcmp(type,mng_JDAT,4) == 0)
4084 {
glennrp47b9dd52010-11-24 18:12:06 +00004085 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004086
4087 if (logging != MagickFalse)
4088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4089 " Copying JDAT chunk data to color_blob.");
4090
4091 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004092
cristy3ed852e2009-09-05 21:47:34 +00004093 if (length)
4094 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004095
cristy3ed852e2009-09-05 21:47:34 +00004096 continue;
4097 }
4098
4099 if (memcmp(type,mng_IDAT,4) == 0)
4100 {
4101 png_byte
4102 data[5];
4103
glennrp47b9dd52010-11-24 18:12:06 +00004104 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004105
4106 if (image_info->ping == MagickFalse)
4107 {
4108 if (logging != MagickFalse)
4109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4110 " Copying IDAT chunk data to alpha_blob.");
4111
cristybb503372010-05-27 20:51:26 +00004112 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004113 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004114 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004115 (void) WriteBlob(alpha_image,4,data);
4116 (void) WriteBlob(alpha_image,length,chunk);
4117 (void) WriteBlobMSBULong(alpha_image,
4118 crc32(crc32(0,data,4),chunk,(uInt) length));
4119 }
glennrp0fe50b42010-11-16 03:52:51 +00004120
cristy3ed852e2009-09-05 21:47:34 +00004121 if (length)
4122 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004123
cristy3ed852e2009-09-05 21:47:34 +00004124 continue;
4125 }
4126
4127 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4128 {
glennrp47b9dd52010-11-24 18:12:06 +00004129 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004130
4131 if (image_info->ping == MagickFalse)
4132 {
4133 if (logging != MagickFalse)
4134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4135 " Copying JDAA chunk data to alpha_blob.");
4136
4137 (void) WriteBlob(alpha_image,length,chunk);
4138 }
glennrp0fe50b42010-11-16 03:52:51 +00004139
cristy3ed852e2009-09-05 21:47:34 +00004140 if (length)
4141 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004142
cristy3ed852e2009-09-05 21:47:34 +00004143 continue;
4144 }
4145
4146 if (memcmp(type,mng_JSEP,4) == 0)
4147 {
4148 read_JSEP=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004149
cristy3ed852e2009-09-05 21:47:34 +00004150 if (length)
4151 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004152
cristy3ed852e2009-09-05 21:47:34 +00004153 continue;
4154 }
4155
4156 if (memcmp(type,mng_bKGD,4) == 0)
4157 {
4158 if (length == 2)
4159 {
4160 image->background_color.red=ScaleCharToQuantum(p[1]);
4161 image->background_color.green=image->background_color.red;
4162 image->background_color.blue=image->background_color.red;
4163 }
glennrp0fe50b42010-11-16 03:52:51 +00004164
cristy3ed852e2009-09-05 21:47:34 +00004165 if (length == 6)
4166 {
4167 image->background_color.red=ScaleCharToQuantum(p[1]);
4168 image->background_color.green=ScaleCharToQuantum(p[3]);
4169 image->background_color.blue=ScaleCharToQuantum(p[5]);
4170 }
glennrp0fe50b42010-11-16 03:52:51 +00004171
cristy3ed852e2009-09-05 21:47:34 +00004172 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4173 continue;
4174 }
4175
4176 if (memcmp(type,mng_gAMA,4) == 0)
4177 {
4178 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004179 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004180
cristy3ed852e2009-09-05 21:47:34 +00004181 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4182 continue;
4183 }
4184
4185 if (memcmp(type,mng_cHRM,4) == 0)
4186 {
4187 if (length == 32)
4188 {
cristy8182b072010-05-30 20:10:53 +00004189 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4190 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4191 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4192 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4193 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4194 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4195 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4196 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004197 }
glennrp47b9dd52010-11-24 18:12:06 +00004198
cristy3ed852e2009-09-05 21:47:34 +00004199 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4200 continue;
4201 }
4202
4203 if (memcmp(type,mng_sRGB,4) == 0)
4204 {
4205 if (length == 1)
4206 {
glennrpe610a072010-08-05 17:08:46 +00004207 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004208 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004209 image->gamma=0.45455f;
4210 image->chromaticity.red_primary.x=0.6400f;
4211 image->chromaticity.red_primary.y=0.3300f;
4212 image->chromaticity.green_primary.x=0.3000f;
4213 image->chromaticity.green_primary.y=0.6000f;
4214 image->chromaticity.blue_primary.x=0.1500f;
4215 image->chromaticity.blue_primary.y=0.0600f;
4216 image->chromaticity.white_point.x=0.3127f;
4217 image->chromaticity.white_point.y=0.3290f;
4218 }
glennrp47b9dd52010-11-24 18:12:06 +00004219
cristy3ed852e2009-09-05 21:47:34 +00004220 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4221 continue;
4222 }
4223
4224 if (memcmp(type,mng_oFFs,4) == 0)
4225 {
4226 if (length > 8)
4227 {
glennrp5eae7602011-02-22 15:21:32 +00004228 image->page.x=(ssize_t) mng_get_long(p);
4229 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004230
cristy3ed852e2009-09-05 21:47:34 +00004231 if ((int) p[8] != 0)
4232 {
4233 image->page.x/=10000;
4234 image->page.y/=10000;
4235 }
4236 }
glennrp47b9dd52010-11-24 18:12:06 +00004237
cristy3ed852e2009-09-05 21:47:34 +00004238 if (length)
4239 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004240
cristy3ed852e2009-09-05 21:47:34 +00004241 continue;
4242 }
4243
4244 if (memcmp(type,mng_pHYs,4) == 0)
4245 {
4246 if (length > 8)
4247 {
cristy16ea1392012-03-21 20:38:41 +00004248 image->resolution.x=(double) mng_get_long(p);
4249 image->resolution.y=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004250 if ((int) p[8] == PNG_RESOLUTION_METER)
4251 {
4252 image->units=PixelsPerCentimeterResolution;
cristy16ea1392012-03-21 20:38:41 +00004253 image->resolution.x=image->resolution.x/100.0f;
4254 image->resolution.y=image->resolution.y/100.0f;
cristy3ed852e2009-09-05 21:47:34 +00004255 }
4256 }
glennrp0fe50b42010-11-16 03:52:51 +00004257
cristy3ed852e2009-09-05 21:47:34 +00004258 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4259 continue;
4260 }
4261
4262#if 0
4263 if (memcmp(type,mng_iCCP,4) == 0)
4264 {
glennrpfd05d622011-02-25 04:10:33 +00004265 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004266 if (length)
4267 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004268
cristy3ed852e2009-09-05 21:47:34 +00004269 continue;
4270 }
4271#endif
4272
4273 if (length)
4274 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4275
4276 if (memcmp(type,mng_IEND,4))
4277 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004278
cristy3ed852e2009-09-05 21:47:34 +00004279 break;
4280 }
4281
4282
4283 /* IEND found */
4284
4285 /*
4286 Finish up reading image data:
4287
4288 o read main image from color_blob.
4289
4290 o close color_blob.
4291
4292 o if (color_type has alpha)
4293 if alpha_encoding is PNG
4294 read secondary image from alpha_blob via ReadPNG
4295 if alpha_encoding is JPEG
4296 read secondary image from alpha_blob via ReadJPEG
4297
4298 o close alpha_blob.
4299
4300 o copy intensity of secondary image into
cristy16ea1392012-03-21 20:38:41 +00004301 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004302
4303 o destroy the secondary image.
4304 */
4305
4306 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004307
cristy3ed852e2009-09-05 21:47:34 +00004308 if (logging != MagickFalse)
4309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4310 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004311
cristy3b6fd2e2011-05-20 12:53:50 +00004312 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004313 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004314
cristy3ed852e2009-09-05 21:47:34 +00004315 color_image_info->ping=MagickFalse; /* To do: avoid this */
4316 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004317
cristy3ed852e2009-09-05 21:47:34 +00004318 if (jng_image == (Image *) NULL)
4319 return((Image *) NULL);
4320
4321 (void) RelinquishUniqueFileResource(color_image->filename);
4322 color_image=DestroyImage(color_image);
4323 color_image_info=DestroyImageInfo(color_image_info);
4324
4325 if (jng_image == (Image *) NULL)
4326 return((Image *) NULL);
4327
4328 if (logging != MagickFalse)
4329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4330 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004331
cristy3ed852e2009-09-05 21:47:34 +00004332 image->rows=jng_height;
4333 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004334
cristybb503372010-05-27 20:51:26 +00004335 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004336 {
cristy16ea1392012-03-21 20:38:41 +00004337 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +00004338 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00004339 for (x=(ssize_t) image->columns; x != 0; x--)
4340 {
4341 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4342 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4343 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4344 q+=GetPixelChannels(image);
4345 s+=GetPixelChannels(jng_image);
4346 }
glennrp47b9dd52010-11-24 18:12:06 +00004347
cristy3ed852e2009-09-05 21:47:34 +00004348 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4349 break;
4350 }
glennrp0fe50b42010-11-16 03:52:51 +00004351
cristy3ed852e2009-09-05 21:47:34 +00004352 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004353
cristy3ed852e2009-09-05 21:47:34 +00004354 if (image_info->ping == MagickFalse)
4355 {
4356 if (jng_color_type >= 12)
4357 {
4358 if (jng_alpha_compression_method == 0)
4359 {
4360 png_byte
4361 data[5];
4362 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4363 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004364 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004365 (void) WriteBlob(alpha_image,4,data);
4366 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4367 }
glennrp0fe50b42010-11-16 03:52:51 +00004368
cristy3ed852e2009-09-05 21:47:34 +00004369 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004370
cristy3ed852e2009-09-05 21:47:34 +00004371 if (logging != MagickFalse)
4372 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00004373 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004374
cristy3b6fd2e2011-05-20 12:53:50 +00004375 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004376 "%s",alpha_image->filename);
4377
4378 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004379
cristy3ed852e2009-09-05 21:47:34 +00004380 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004381 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004382 {
4383 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy16ea1392012-03-21 20:38:41 +00004384 exception);
cristy3ed852e2009-09-05 21:47:34 +00004385 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004386
cristy3ed852e2009-09-05 21:47:34 +00004387 if (image->matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00004388 for (x=(ssize_t) image->columns; x != 0; x--)
4389 {
4390 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4391 q+=GetPixelChannels(image);
4392 s+=GetPixelChannels(jng_image);
4393 }
glennrp0fe50b42010-11-16 03:52:51 +00004394
cristy3ed852e2009-09-05 21:47:34 +00004395 else
cristy16ea1392012-03-21 20:38:41 +00004396 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004397 {
cristy16ea1392012-03-21 20:38:41 +00004398 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4399 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004400 image->matte=MagickTrue;
cristy16ea1392012-03-21 20:38:41 +00004401 q+=GetPixelChannels(image);
4402 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004403 }
glennrp0fe50b42010-11-16 03:52:51 +00004404
cristy3ed852e2009-09-05 21:47:34 +00004405 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4406 break;
4407 }
4408 (void) RelinquishUniqueFileResource(alpha_image->filename);
4409 alpha_image=DestroyImage(alpha_image);
4410 alpha_image_info=DestroyImageInfo(alpha_image_info);
4411 if (jng_image != (Image *) NULL)
4412 jng_image=DestroyImage(jng_image);
4413 }
4414 }
4415
glennrp47b9dd52010-11-24 18:12:06 +00004416 /* Read the JNG image. */
4417
cristy3ed852e2009-09-05 21:47:34 +00004418 if (mng_info->mng_type == 0)
4419 {
4420 mng_info->mng_width=jng_width;
4421 mng_info->mng_height=jng_height;
4422 }
glennrp0fe50b42010-11-16 03:52:51 +00004423
cristy3ed852e2009-09-05 21:47:34 +00004424 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004425 {
4426 image->page.width=jng_width;
4427 image->page.height=jng_height;
4428 }
4429
cristy3ed852e2009-09-05 21:47:34 +00004430 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004431 {
4432 image->page.x=mng_info->x_off[mng_info->object_id];
4433 image->page.y=mng_info->y_off[mng_info->object_id];
4434 }
4435
cristy3ed852e2009-09-05 21:47:34 +00004436 else
glennrp0fe50b42010-11-16 03:52:51 +00004437 {
4438 image->page.y=mng_info->y_off[mng_info->object_id];
4439 }
4440
cristy3ed852e2009-09-05 21:47:34 +00004441 mng_info->image_found++;
4442 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4443 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004444
cristy3ed852e2009-09-05 21:47:34 +00004445 if (logging != MagickFalse)
4446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4447 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004448
cristy3ed852e2009-09-05 21:47:34 +00004449 return(image);
4450}
4451
4452/*
4453%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4454% %
4455% %
4456% %
4457% R e a d J N G I m a g e %
4458% %
4459% %
4460% %
4461%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4462%
4463% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4464% (including the 8-byte signature) and returns it. It allocates the memory
4465% necessary for the new Image structure and returns a pointer to the new
4466% image.
4467%
4468% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4469%
4470% The format of the ReadJNGImage method is:
4471%
4472% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4473% *exception)
4474%
4475% A description of each parameter follows:
4476%
4477% o image_info: the image info.
4478%
4479% o exception: return any errors or warnings in this structure.
4480%
4481*/
4482
4483static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4484{
4485 Image
4486 *image,
4487 *previous;
4488
4489 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004490 have_mng_structure,
4491 logging,
cristy3ed852e2009-09-05 21:47:34 +00004492 status;
4493
4494 MngInfo
4495 *mng_info;
4496
4497 char
4498 magic_number[MaxTextExtent];
4499
cristy3ed852e2009-09-05 21:47:34 +00004500 size_t
4501 count;
4502
4503 /*
4504 Open image file.
4505 */
4506 assert(image_info != (const ImageInfo *) NULL);
4507 assert(image_info->signature == MagickSignature);
4508 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4509 assert(exception != (ExceptionInfo *) NULL);
4510 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004511 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004512 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004513 mng_info=(MngInfo *) NULL;
4514 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004515
cristy3ed852e2009-09-05 21:47:34 +00004516 if (status == MagickFalse)
4517 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004518
cristy3ed852e2009-09-05 21:47:34 +00004519 if (LocaleCompare(image_info->magick,"JNG") != 0)
4520 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004521
glennrp47b9dd52010-11-24 18:12:06 +00004522 /* Verify JNG signature. */
4523
cristy3ed852e2009-09-05 21:47:34 +00004524 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004525
glennrp3b8763e2011-02-21 12:08:18 +00004526 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004527 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004528
glennrp47b9dd52010-11-24 18:12:06 +00004529 /* Allocate a MngInfo structure. */
4530
cristy3ed852e2009-09-05 21:47:34 +00004531 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004532 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004533
cristy3ed852e2009-09-05 21:47:34 +00004534 if (mng_info == (MngInfo *) NULL)
4535 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004536
glennrp47b9dd52010-11-24 18:12:06 +00004537 /* Initialize members of the MngInfo structure. */
4538
cristy3ed852e2009-09-05 21:47:34 +00004539 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4540 have_mng_structure=MagickTrue;
4541
4542 mng_info->image=image;
4543 previous=image;
4544 image=ReadOneJNGImage(mng_info,image_info,exception);
4545 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004546
cristy3ed852e2009-09-05 21:47:34 +00004547 if (image == (Image *) NULL)
4548 {
4549 if (IsImageObject(previous) != MagickFalse)
4550 {
4551 (void) CloseBlob(previous);
4552 (void) DestroyImageList(previous);
4553 }
glennrp0fe50b42010-11-16 03:52:51 +00004554
cristy3ed852e2009-09-05 21:47:34 +00004555 if (logging != MagickFalse)
4556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4557 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004558
cristy3ed852e2009-09-05 21:47:34 +00004559 return((Image *) NULL);
4560 }
4561 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004562
cristy3ed852e2009-09-05 21:47:34 +00004563 if (image->columns == 0 || image->rows == 0)
4564 {
4565 if (logging != MagickFalse)
4566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4567 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004568
cristy3ed852e2009-09-05 21:47:34 +00004569 ThrowReaderException(CorruptImageError,"CorruptImage");
4570 }
glennrp0fe50b42010-11-16 03:52:51 +00004571
cristy3ed852e2009-09-05 21:47:34 +00004572 if (logging != MagickFalse)
4573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004574
cristy3ed852e2009-09-05 21:47:34 +00004575 return(image);
4576}
4577#endif
4578
4579static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4580{
4581 char
4582 page_geometry[MaxTextExtent];
4583
4584 Image
4585 *image,
4586 *previous;
4587
cristy4383ec82011-01-05 15:42:32 +00004588 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004589 logging,
4590 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004591
cristy3ed852e2009-09-05 21:47:34 +00004592 volatile int
4593 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004594 object_id,
4595 term_chunk_found,
4596 skip_to_iend;
4597
cristybb503372010-05-27 20:51:26 +00004598 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004599 image_count=0;
4600
4601 MagickBooleanType
4602 status;
4603
4604 MagickOffsetType
4605 offset;
4606
4607 MngInfo
4608 *mng_info;
4609
4610 MngBox
4611 default_fb,
4612 fb,
4613 previous_fb;
4614
4615#if defined(MNG_INSERT_LAYERS)
cristy16ea1392012-03-21 20:38:41 +00004616 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +00004617 mng_background_color;
4618#endif
4619
4620 register unsigned char
4621 *p;
4622
cristybb503372010-05-27 20:51:26 +00004623 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004624 i;
4625
4626 size_t
4627 count;
4628
cristybb503372010-05-27 20:51:26 +00004629 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004630 loop_level;
4631
4632 volatile short
4633 skipping_loop;
4634
4635#if defined(MNG_INSERT_LAYERS)
4636 unsigned int
4637 mandatory_back=0;
4638#endif
4639
4640 volatile unsigned int
4641#ifdef MNG_OBJECT_BUFFERS
4642 mng_background_object=0,
4643#endif
4644 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4645
cristybb503372010-05-27 20:51:26 +00004646 size_t
cristy3ed852e2009-09-05 21:47:34 +00004647 default_frame_timeout,
4648 frame_timeout,
4649#if defined(MNG_INSERT_LAYERS)
4650 image_height,
4651 image_width,
4652#endif
4653 length;
4654
glennrp38ea0832010-06-02 18:50:28 +00004655 /* These delays are all measured in image ticks_per_second,
4656 * not in MNG ticks_per_second
4657 */
cristybb503372010-05-27 20:51:26 +00004658 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004659 default_frame_delay,
4660 final_delay,
4661 final_image_delay,
4662 frame_delay,
4663#if defined(MNG_INSERT_LAYERS)
4664 insert_layers,
4665#endif
4666 mng_iterations=1,
4667 simplicity=0,
4668 subframe_height=0,
4669 subframe_width=0;
4670
4671 previous_fb.top=0;
4672 previous_fb.bottom=0;
4673 previous_fb.left=0;
4674 previous_fb.right=0;
4675 default_fb.top=0;
4676 default_fb.bottom=0;
4677 default_fb.left=0;
4678 default_fb.right=0;
4679
glennrp47b9dd52010-11-24 18:12:06 +00004680 /* Open image file. */
4681
cristy3ed852e2009-09-05 21:47:34 +00004682 assert(image_info != (const ImageInfo *) NULL);
4683 assert(image_info->signature == MagickSignature);
4684 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4685 assert(exception != (ExceptionInfo *) NULL);
4686 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004687 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy16ea1392012-03-21 20:38:41 +00004688 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00004689 mng_info=(MngInfo *) NULL;
4690 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004691
cristy3ed852e2009-09-05 21:47:34 +00004692 if (status == MagickFalse)
4693 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004694
cristy3ed852e2009-09-05 21:47:34 +00004695 first_mng_object=MagickFalse;
4696 skipping_loop=(-1);
4697 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004698
4699 /* Allocate a MngInfo structure. */
4700
cristy73bd4a52010-10-05 11:24:23 +00004701 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004702
cristy3ed852e2009-09-05 21:47:34 +00004703 if (mng_info == (MngInfo *) NULL)
4704 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004705
glennrp47b9dd52010-11-24 18:12:06 +00004706 /* Initialize members of the MngInfo structure. */
4707
cristy3ed852e2009-09-05 21:47:34 +00004708 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4709 mng_info->image=image;
4710 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004711
4712 if (LocaleCompare(image_info->magick,"MNG") == 0)
4713 {
4714 char
4715 magic_number[MaxTextExtent];
4716
glennrp47b9dd52010-11-24 18:12:06 +00004717 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004718 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4719 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4720 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004721
4722 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004723 for (i=0; i < MNG_MAX_OBJECTS; i++)
4724 {
cristybb503372010-05-27 20:51:26 +00004725 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4726 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004727 }
4728 mng_info->exists[0]=MagickTrue;
4729 }
glennrp47b9dd52010-11-24 18:12:06 +00004730
cristy3ed852e2009-09-05 21:47:34 +00004731 first_mng_object=MagickTrue;
4732 mng_type=0;
4733#if defined(MNG_INSERT_LAYERS)
4734 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4735#endif
4736 default_frame_delay=0;
4737 default_frame_timeout=0;
4738 frame_delay=0;
4739 final_delay=1;
4740 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4741 object_id=0;
4742 skip_to_iend=MagickFalse;
4743 term_chunk_found=MagickFalse;
4744 mng_info->framing_mode=1;
4745#if defined(MNG_INSERT_LAYERS)
4746 mandatory_back=MagickFalse;
4747#endif
4748#if defined(MNG_INSERT_LAYERS)
4749 mng_background_color=image->background_color;
4750#endif
4751 default_fb=mng_info->frame;
4752 previous_fb=mng_info->frame;
4753 do
4754 {
4755 char
4756 type[MaxTextExtent];
4757
4758 if (LocaleCompare(image_info->magick,"MNG") == 0)
4759 {
4760 unsigned char
4761 *chunk;
4762
4763 /*
4764 Read a new chunk.
4765 */
4766 type[0]='\0';
4767 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4768 length=ReadBlobMSBLong(image);
4769 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4770
4771 if (logging != MagickFalse)
4772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004773 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4774 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004775
4776 if (length > PNG_UINT_31_MAX)
4777 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004778
cristy3ed852e2009-09-05 21:47:34 +00004779 if (count == 0)
4780 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004781
cristy3ed852e2009-09-05 21:47:34 +00004782 p=NULL;
4783 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004784
cristy3ed852e2009-09-05 21:47:34 +00004785 if (length)
4786 {
4787 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004788
cristy3ed852e2009-09-05 21:47:34 +00004789 if (chunk == (unsigned char *) NULL)
4790 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004791
cristybb503372010-05-27 20:51:26 +00004792 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004793 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004794
cristy3ed852e2009-09-05 21:47:34 +00004795 p=chunk;
4796 }
glennrp0fe50b42010-11-16 03:52:51 +00004797
cristy3ed852e2009-09-05 21:47:34 +00004798 (void) ReadBlobMSBLong(image); /* read crc word */
4799
4800#if !defined(JNG_SUPPORTED)
4801 if (memcmp(type,mng_JHDR,4) == 0)
4802 {
4803 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004804
cristy3ed852e2009-09-05 21:47:34 +00004805 if (mng_info->jhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00004806 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004807 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004808
cristy3ed852e2009-09-05 21:47:34 +00004809 mng_info->jhdr_warning++;
4810 }
4811#endif
4812 if (memcmp(type,mng_DHDR,4) == 0)
4813 {
4814 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004815
cristy3ed852e2009-09-05 21:47:34 +00004816 if (mng_info->dhdr_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00004817 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004818 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004819
cristy3ed852e2009-09-05 21:47:34 +00004820 mng_info->dhdr_warning++;
4821 }
4822 if (memcmp(type,mng_MEND,4) == 0)
4823 break;
glennrp47b9dd52010-11-24 18:12:06 +00004824
cristy3ed852e2009-09-05 21:47:34 +00004825 if (skip_to_iend)
4826 {
4827 if (memcmp(type,mng_IEND,4) == 0)
4828 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004829
cristy3ed852e2009-09-05 21:47:34 +00004830 if (length)
4831 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004832
cristy3ed852e2009-09-05 21:47:34 +00004833 if (logging != MagickFalse)
4834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4835 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004836
cristy3ed852e2009-09-05 21:47:34 +00004837 continue;
4838 }
glennrp0fe50b42010-11-16 03:52:51 +00004839
cristy3ed852e2009-09-05 21:47:34 +00004840 if (memcmp(type,mng_MHDR,4) == 0)
4841 {
cristybb503372010-05-27 20:51:26 +00004842 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004843 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004844
cristybb503372010-05-27 20:51:26 +00004845 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004846 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy3ed852e2009-09-05 21:47:34 +00004848 if (logging != MagickFalse)
4849 {
4850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004851 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004853 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004854 }
glennrp0fe50b42010-11-16 03:52:51 +00004855
cristy3ed852e2009-09-05 21:47:34 +00004856 p+=8;
cristy8182b072010-05-30 20:10:53 +00004857 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004858
cristy3ed852e2009-09-05 21:47:34 +00004859 if (mng_info->ticks_per_second == 0)
4860 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004861
cristy3ed852e2009-09-05 21:47:34 +00004862 else
4863 default_frame_delay=1UL*image->ticks_per_second/
4864 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004865
cristy3ed852e2009-09-05 21:47:34 +00004866 frame_delay=default_frame_delay;
4867 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004868
cristy3ed852e2009-09-05 21:47:34 +00004869 if (length > 16)
4870 {
4871 p+=16;
cristy8182b072010-05-30 20:10:53 +00004872 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004873 }
glennrp0fe50b42010-11-16 03:52:51 +00004874
cristy3ed852e2009-09-05 21:47:34 +00004875 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004876
cristy3ed852e2009-09-05 21:47:34 +00004877 if ((simplicity != 0) && ((simplicity | 11) == 11))
4878 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004879
cristy3ed852e2009-09-05 21:47:34 +00004880 if ((simplicity != 0) && ((simplicity | 9) == 9))
4881 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004882
cristy3ed852e2009-09-05 21:47:34 +00004883#if defined(MNG_INSERT_LAYERS)
4884 if (mng_type != 3)
4885 insert_layers=MagickTrue;
4886#endif
cristy16ea1392012-03-21 20:38:41 +00004887 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004888 {
glennrp47b9dd52010-11-24 18:12:06 +00004889 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00004890 AcquireNextImage(image_info,image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004891
cristy3ed852e2009-09-05 21:47:34 +00004892 if (GetNextImageInList(image) == (Image *) NULL)
4893 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004894
cristy3ed852e2009-09-05 21:47:34 +00004895 image=SyncNextImageInList(image);
4896 mng_info->image=image;
4897 }
4898
4899 if ((mng_info->mng_width > 65535L) ||
4900 (mng_info->mng_height > 65535L))
4901 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004902
cristy3b6fd2e2011-05-20 12:53:50 +00004903 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004904 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004905 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004906
cristy3ed852e2009-09-05 21:47:34 +00004907 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004908 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004909 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004910 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004911 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004912
cristy3ed852e2009-09-05 21:47:34 +00004913 for (i=0; i < MNG_MAX_OBJECTS; i++)
4914 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004915
cristy3ed852e2009-09-05 21:47:34 +00004916 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4917 continue;
4918 }
4919
4920 if (memcmp(type,mng_TERM,4) == 0)
4921 {
4922 int
4923 repeat=0;
4924
4925
4926 if (length)
4927 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004928
cristy3ed852e2009-09-05 21:47:34 +00004929 if (repeat == 3)
4930 {
cristy8182b072010-05-30 20:10:53 +00004931 final_delay=(png_uint_32) mng_get_long(&p[2]);
4932 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004933
cristy3ed852e2009-09-05 21:47:34 +00004934 if (mng_iterations == PNG_UINT_31_MAX)
4935 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004936
cristy3ed852e2009-09-05 21:47:34 +00004937 image->iterations=mng_iterations;
4938 term_chunk_found=MagickTrue;
4939 }
glennrp0fe50b42010-11-16 03:52:51 +00004940
cristy3ed852e2009-09-05 21:47:34 +00004941 if (logging != MagickFalse)
4942 {
4943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4944 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004945
cristy3ed852e2009-09-05 21:47:34 +00004946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004947 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004948
cristy3ed852e2009-09-05 21:47:34 +00004949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004950 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004951 }
glennrp0fe50b42010-11-16 03:52:51 +00004952
cristy3ed852e2009-09-05 21:47:34 +00004953 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4954 continue;
4955 }
4956 if (memcmp(type,mng_DEFI,4) == 0)
4957 {
4958 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00004959 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004960 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4961 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004962
cristy3ed852e2009-09-05 21:47:34 +00004963 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004964
cristy3ed852e2009-09-05 21:47:34 +00004965 if (mng_type == 2 && object_id != 0)
cristy16ea1392012-03-21 20:38:41 +00004966 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004967 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4968 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004969
cristy3ed852e2009-09-05 21:47:34 +00004970 if (object_id > MNG_MAX_OBJECTS)
4971 {
4972 /*
glennrpedaa0382012-04-12 14:16:21 +00004973 Instead of using a warning we should allocate a larger
cristy3ed852e2009-09-05 21:47:34 +00004974 MngInfo structure and continue.
4975 */
cristy16ea1392012-03-21 20:38:41 +00004976 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00004977 CoderError,"object id too large","`%s'",image->filename);
4978 object_id=MNG_MAX_OBJECTS;
4979 }
glennrp0fe50b42010-11-16 03:52:51 +00004980
cristy3ed852e2009-09-05 21:47:34 +00004981 if (mng_info->exists[object_id])
4982 if (mng_info->frozen[object_id])
4983 {
4984 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
cristy16ea1392012-03-21 20:38:41 +00004985 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00004986 GetMagickModule(),CoderError,
4987 "DEFI cannot redefine a frozen MNG object","`%s'",
4988 image->filename);
4989 continue;
4990 }
glennrp0fe50b42010-11-16 03:52:51 +00004991
cristy3ed852e2009-09-05 21:47:34 +00004992 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004993
cristy3ed852e2009-09-05 21:47:34 +00004994 if (length > 2)
4995 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004996
cristy3ed852e2009-09-05 21:47:34 +00004997 /*
4998 Extract object offset info.
4999 */
5000 if (length > 11)
5001 {
glennrp0fe50b42010-11-16 03:52:51 +00005002 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5003 (p[5] << 16) | (p[6] << 8) | p[7]);
5004
5005 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5006 (p[9] << 16) | (p[10] << 8) | p[11]);
5007
cristy3ed852e2009-09-05 21:47:34 +00005008 if (logging != MagickFalse)
5009 {
5010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005011 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005012 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00005013
cristy3ed852e2009-09-05 21:47:34 +00005014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005015 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00005016 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00005017 }
5018 }
glennrp0fe50b42010-11-16 03:52:51 +00005019
cristy3ed852e2009-09-05 21:47:34 +00005020 /*
5021 Extract object clipping info.
5022 */
5023 if (length > 27)
5024 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5025 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00005026
cristy3ed852e2009-09-05 21:47:34 +00005027 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5028 continue;
5029 }
5030 if (memcmp(type,mng_bKGD,4) == 0)
5031 {
5032 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005033
cristy3ed852e2009-09-05 21:47:34 +00005034 if (length > 5)
5035 {
5036 mng_info->mng_global_bkgd.red=
5037 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005038
cristy3ed852e2009-09-05 21:47:34 +00005039 mng_info->mng_global_bkgd.green=
5040 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005041
cristy3ed852e2009-09-05 21:47:34 +00005042 mng_info->mng_global_bkgd.blue=
5043 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005044
cristy3ed852e2009-09-05 21:47:34 +00005045 mng_info->have_global_bkgd=MagickTrue;
5046 }
glennrp0fe50b42010-11-16 03:52:51 +00005047
cristy3ed852e2009-09-05 21:47:34 +00005048 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5049 continue;
5050 }
5051 if (memcmp(type,mng_BACK,4) == 0)
5052 {
5053#if defined(MNG_INSERT_LAYERS)
5054 if (length > 6)
5055 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005056
cristy3ed852e2009-09-05 21:47:34 +00005057 else
5058 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005059
cristy3ed852e2009-09-05 21:47:34 +00005060 if (mandatory_back && length > 5)
5061 {
5062 mng_background_color.red=
5063 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005064
cristy3ed852e2009-09-05 21:47:34 +00005065 mng_background_color.green=
5066 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005067
cristy3ed852e2009-09-05 21:47:34 +00005068 mng_background_color.blue=
5069 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005070
cristy16ea1392012-03-21 20:38:41 +00005071 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005072 }
glennrp0fe50b42010-11-16 03:52:51 +00005073
cristy3ed852e2009-09-05 21:47:34 +00005074#ifdef MNG_OBJECT_BUFFERS
5075 if (length > 8)
5076 mng_background_object=(p[7] << 8) | p[8];
5077#endif
5078#endif
5079 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5080 continue;
5081 }
glennrp47b9dd52010-11-24 18:12:06 +00005082
cristy3ed852e2009-09-05 21:47:34 +00005083 if (memcmp(type,mng_PLTE,4) == 0)
5084 {
glennrp47b9dd52010-11-24 18:12:06 +00005085 /* Read global PLTE. */
5086
cristy3ed852e2009-09-05 21:47:34 +00005087 if (length && (length < 769))
5088 {
5089 if (mng_info->global_plte == (png_colorp) NULL)
5090 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5091 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005092
cristybb503372010-05-27 20:51:26 +00005093 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005094 {
5095 mng_info->global_plte[i].red=p[3*i];
5096 mng_info->global_plte[i].green=p[3*i+1];
5097 mng_info->global_plte[i].blue=p[3*i+2];
5098 }
glennrp0fe50b42010-11-16 03:52:51 +00005099
cristy35ef8242010-06-03 16:24:13 +00005100 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005101 }
5102#ifdef MNG_LOOSE
5103 for ( ; i < 256; i++)
5104 {
5105 mng_info->global_plte[i].red=i;
5106 mng_info->global_plte[i].green=i;
5107 mng_info->global_plte[i].blue=i;
5108 }
glennrp0fe50b42010-11-16 03:52:51 +00005109
cristy3ed852e2009-09-05 21:47:34 +00005110 if (length)
5111 mng_info->global_plte_length=256;
5112#endif
5113 else
5114 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005115
cristy3ed852e2009-09-05 21:47:34 +00005116 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5117 continue;
5118 }
glennrp47b9dd52010-11-24 18:12:06 +00005119
cristy3ed852e2009-09-05 21:47:34 +00005120 if (memcmp(type,mng_tRNS,4) == 0)
5121 {
5122 /* read global tRNS */
5123
5124 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005125 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005126 mng_info->global_trns[i]=p[i];
5127
5128#ifdef MNG_LOOSE
5129 for ( ; i < 256; i++)
5130 mng_info->global_trns[i]=255;
5131#endif
cristy12560f32010-06-03 16:51:08 +00005132 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005133 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5134 continue;
5135 }
5136 if (memcmp(type,mng_gAMA,4) == 0)
5137 {
5138 if (length == 4)
5139 {
cristybb503372010-05-27 20:51:26 +00005140 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005141 igamma;
5142
cristy8182b072010-05-30 20:10:53 +00005143 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005144 mng_info->global_gamma=((float) igamma)*0.00001;
5145 mng_info->have_global_gama=MagickTrue;
5146 }
glennrp0fe50b42010-11-16 03:52:51 +00005147
cristy3ed852e2009-09-05 21:47:34 +00005148 else
5149 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005150
cristy3ed852e2009-09-05 21:47:34 +00005151 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5152 continue;
5153 }
5154
5155 if (memcmp(type,mng_cHRM,4) == 0)
5156 {
glennrp47b9dd52010-11-24 18:12:06 +00005157 /* Read global cHRM */
5158
cristy3ed852e2009-09-05 21:47:34 +00005159 if (length == 32)
5160 {
cristy8182b072010-05-30 20:10:53 +00005161 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5162 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5163 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005164 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005165 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005166 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005167 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005168 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005169 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005170 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005171 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005172 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005173 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005174 mng_info->have_global_chrm=MagickTrue;
5175 }
5176 else
5177 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005178
cristy3ed852e2009-09-05 21:47:34 +00005179 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5180 continue;
5181 }
glennrp47b9dd52010-11-24 18:12:06 +00005182
cristy3ed852e2009-09-05 21:47:34 +00005183 if (memcmp(type,mng_sRGB,4) == 0)
5184 {
5185 /*
5186 Read global sRGB.
5187 */
5188 if (length)
5189 {
glennrpe610a072010-08-05 17:08:46 +00005190 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005191 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005192 mng_info->have_global_srgb=MagickTrue;
5193 }
5194 else
5195 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005196
cristy3ed852e2009-09-05 21:47:34 +00005197 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5198 continue;
5199 }
glennrp47b9dd52010-11-24 18:12:06 +00005200
cristy3ed852e2009-09-05 21:47:34 +00005201 if (memcmp(type,mng_iCCP,4) == 0)
5202 {
glennrpfd05d622011-02-25 04:10:33 +00005203 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005204
5205 /*
5206 Read global iCCP.
5207 */
5208 if (length)
5209 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005210
cristy3ed852e2009-09-05 21:47:34 +00005211 continue;
5212 }
glennrp47b9dd52010-11-24 18:12:06 +00005213
cristy3ed852e2009-09-05 21:47:34 +00005214 if (memcmp(type,mng_FRAM,4) == 0)
5215 {
5216 if (mng_type == 3)
cristy16ea1392012-03-21 20:38:41 +00005217 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005218 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5219 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005220
cristy3ed852e2009-09-05 21:47:34 +00005221 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5222 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005223
cristy3ed852e2009-09-05 21:47:34 +00005224 frame_delay=default_frame_delay;
5225 frame_timeout=default_frame_timeout;
5226 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005227
cristy3ed852e2009-09-05 21:47:34 +00005228 if (length)
5229 if (p[0])
5230 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005231
cristy3ed852e2009-09-05 21:47:34 +00005232 if (logging != MagickFalse)
5233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5234 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005235
cristy3ed852e2009-09-05 21:47:34 +00005236 if (length > 6)
5237 {
glennrp47b9dd52010-11-24 18:12:06 +00005238 /* Note the delay and frame clipping boundaries. */
5239
cristy3ed852e2009-09-05 21:47:34 +00005240 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005241
cristybb503372010-05-27 20:51:26 +00005242 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005243 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005244
cristy3ed852e2009-09-05 21:47:34 +00005245 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005246
cristybb503372010-05-27 20:51:26 +00005247 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005248 {
5249 int
5250 change_delay,
5251 change_timeout,
5252 change_clipping;
5253
5254 change_delay=(*p++);
5255 change_timeout=(*p++);
5256 change_clipping=(*p++);
5257 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005258
cristy3ed852e2009-09-05 21:47:34 +00005259 if (change_delay)
5260 {
cristy8182b072010-05-30 20:10:53 +00005261 frame_delay=1UL*image->ticks_per_second*
5262 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005263
cristy8182b072010-05-30 20:10:53 +00005264 if (mng_info->ticks_per_second != 0)
5265 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005266
glennrpbb010dd2010-06-01 13:07:15 +00005267 else
5268 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005269
cristy3ed852e2009-09-05 21:47:34 +00005270 if (change_delay == 2)
5271 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005272
cristy3ed852e2009-09-05 21:47:34 +00005273 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005274
cristy3ed852e2009-09-05 21:47:34 +00005275 if (logging != MagickFalse)
5276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005277 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005278 }
glennrp47b9dd52010-11-24 18:12:06 +00005279
cristy3ed852e2009-09-05 21:47:34 +00005280 if (change_timeout)
5281 {
glennrpbb010dd2010-06-01 13:07:15 +00005282 frame_timeout=1UL*image->ticks_per_second*
5283 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005284
glennrpbb010dd2010-06-01 13:07:15 +00005285 if (mng_info->ticks_per_second != 0)
5286 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005287
glennrpbb010dd2010-06-01 13:07:15 +00005288 else
5289 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005290
cristy3ed852e2009-09-05 21:47:34 +00005291 if (change_delay == 2)
5292 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005293
cristy3ed852e2009-09-05 21:47:34 +00005294 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005295
cristy3ed852e2009-09-05 21:47:34 +00005296 if (logging != MagickFalse)
5297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005298 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005299 }
glennrp47b9dd52010-11-24 18:12:06 +00005300
cristy3ed852e2009-09-05 21:47:34 +00005301 if (change_clipping)
5302 {
5303 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5304 p+=17;
5305 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005306
cristy3ed852e2009-09-05 21:47:34 +00005307 if (logging != MagickFalse)
5308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005309 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005310 (double) fb.left,(double) fb.right,(double) fb.top,
5311 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005312
cristy3ed852e2009-09-05 21:47:34 +00005313 if (change_clipping == 2)
5314 default_fb=fb;
5315 }
5316 }
5317 }
5318 mng_info->clip=fb;
5319 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005320
cristybb503372010-05-27 20:51:26 +00005321 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005322 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005323
cristybb503372010-05-27 20:51:26 +00005324 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005325 -mng_info->clip.top);
5326 /*
5327 Insert a background layer behind the frame if framing_mode is 4.
5328 */
5329#if defined(MNG_INSERT_LAYERS)
5330 if (logging != MagickFalse)
5331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005332 " subframe_width=%.20g, subframe_height=%.20g",(double)
5333 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005334
cristy3ed852e2009-09-05 21:47:34 +00005335 if (insert_layers && (mng_info->framing_mode == 4) &&
5336 (subframe_width) && (subframe_height))
5337 {
glennrp47b9dd52010-11-24 18:12:06 +00005338 /* Allocate next image structure. */
cristy16ea1392012-03-21 20:38:41 +00005339 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005340 {
cristy16ea1392012-03-21 20:38:41 +00005341 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005342
cristy3ed852e2009-09-05 21:47:34 +00005343 if (GetNextImageInList(image) == (Image *) NULL)
5344 {
5345 image=DestroyImageList(image);
5346 MngInfoFreeStruct(mng_info,&have_mng_structure);
5347 return((Image *) NULL);
5348 }
glennrp47b9dd52010-11-24 18:12:06 +00005349
cristy3ed852e2009-09-05 21:47:34 +00005350 image=SyncNextImageInList(image);
5351 }
glennrp0fe50b42010-11-16 03:52:51 +00005352
cristy3ed852e2009-09-05 21:47:34 +00005353 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005354
cristy3ed852e2009-09-05 21:47:34 +00005355 if (term_chunk_found)
5356 {
5357 image->start_loop=MagickTrue;
5358 image->iterations=mng_iterations;
5359 term_chunk_found=MagickFalse;
5360 }
glennrp0fe50b42010-11-16 03:52:51 +00005361
cristy3ed852e2009-09-05 21:47:34 +00005362 else
5363 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005364
cristy3ed852e2009-09-05 21:47:34 +00005365 image->columns=subframe_width;
5366 image->rows=subframe_height;
5367 image->page.width=subframe_width;
5368 image->page.height=subframe_height;
5369 image->page.x=mng_info->clip.left;
5370 image->page.y=mng_info->clip.top;
5371 image->background_color=mng_background_color;
5372 image->matte=MagickFalse;
5373 image->delay=0;
cristy16ea1392012-03-21 20:38:41 +00005374 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005375
cristy3ed852e2009-09-05 21:47:34 +00005376 if (logging != MagickFalse)
5377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005378 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005379 (double) mng_info->clip.left,(double) mng_info->clip.right,
5380 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005381 }
5382#endif
5383 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5384 continue;
5385 }
5386 if (memcmp(type,mng_CLIP,4) == 0)
5387 {
5388 unsigned int
5389 first_object,
5390 last_object;
5391
5392 /*
5393 Read CLIP.
5394 */
5395 first_object=(p[0] << 8) | p[1];
5396 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005397
cristy3ed852e2009-09-05 21:47:34 +00005398 for (i=(int) first_object; i <= (int) last_object; i++)
5399 {
5400 if (mng_info->exists[i] && !mng_info->frozen[i])
5401 {
5402 MngBox
5403 box;
5404
5405 box=mng_info->object_clip[i];
5406 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5407 }
5408 }
glennrp47b9dd52010-11-24 18:12:06 +00005409
cristy3ed852e2009-09-05 21:47:34 +00005410 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5411 continue;
5412 }
5413 if (memcmp(type,mng_SAVE,4) == 0)
5414 {
5415 for (i=1; i < MNG_MAX_OBJECTS; i++)
5416 if (mng_info->exists[i])
5417 {
5418 mng_info->frozen[i]=MagickTrue;
5419#ifdef MNG_OBJECT_BUFFERS
5420 if (mng_info->ob[i] != (MngBuffer *) NULL)
5421 mng_info->ob[i]->frozen=MagickTrue;
5422#endif
5423 }
glennrp0fe50b42010-11-16 03:52:51 +00005424
cristy3ed852e2009-09-05 21:47:34 +00005425 if (length)
5426 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005427
cristy3ed852e2009-09-05 21:47:34 +00005428 continue;
5429 }
5430
5431 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5432 {
glennrp47b9dd52010-11-24 18:12:06 +00005433 /* Read DISC or SEEK. */
5434
cristy3ed852e2009-09-05 21:47:34 +00005435 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5436 {
5437 for (i=1; i < MNG_MAX_OBJECTS; i++)
5438 MngInfoDiscardObject(mng_info,i);
5439 }
glennrp0fe50b42010-11-16 03:52:51 +00005440
cristy3ed852e2009-09-05 21:47:34 +00005441 else
5442 {
cristybb503372010-05-27 20:51:26 +00005443 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005444 j;
5445
cristybb503372010-05-27 20:51:26 +00005446 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005447 {
5448 i=p[j] << 8 | p[j+1];
5449 MngInfoDiscardObject(mng_info,i);
5450 }
5451 }
glennrp0fe50b42010-11-16 03:52:51 +00005452
cristy3ed852e2009-09-05 21:47:34 +00005453 if (length)
5454 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005455
cristy3ed852e2009-09-05 21:47:34 +00005456 continue;
5457 }
glennrp47b9dd52010-11-24 18:12:06 +00005458
cristy3ed852e2009-09-05 21:47:34 +00005459 if (memcmp(type,mng_MOVE,4) == 0)
5460 {
cristybb503372010-05-27 20:51:26 +00005461 size_t
cristy3ed852e2009-09-05 21:47:34 +00005462 first_object,
5463 last_object;
5464
glennrp47b9dd52010-11-24 18:12:06 +00005465 /* read MOVE */
5466
cristy3ed852e2009-09-05 21:47:34 +00005467 first_object=(p[0] << 8) | p[1];
5468 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005469 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005470 {
5471 if (mng_info->exists[i] && !mng_info->frozen[i])
5472 {
5473 MngPair
5474 new_pair;
5475
5476 MngPair
5477 old_pair;
5478
5479 old_pair.a=mng_info->x_off[i];
5480 old_pair.b=mng_info->y_off[i];
5481 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5482 mng_info->x_off[i]=new_pair.a;
5483 mng_info->y_off[i]=new_pair.b;
5484 }
5485 }
glennrp47b9dd52010-11-24 18:12:06 +00005486
cristy3ed852e2009-09-05 21:47:34 +00005487 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5488 continue;
5489 }
5490
5491 if (memcmp(type,mng_LOOP,4) == 0)
5492 {
cristybb503372010-05-27 20:51:26 +00005493 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005494 loop_level=chunk[0];
5495 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005496
5497 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005498 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005499
cristy3ed852e2009-09-05 21:47:34 +00005500 if (logging != MagickFalse)
5501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005502 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5503 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005504
cristy3ed852e2009-09-05 21:47:34 +00005505 if (loop_iters == 0)
5506 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005507
cristy3ed852e2009-09-05 21:47:34 +00005508 else
5509 {
5510 mng_info->loop_jump[loop_level]=TellBlob(image);
5511 mng_info->loop_count[loop_level]=loop_iters;
5512 }
glennrp0fe50b42010-11-16 03:52:51 +00005513
cristy3ed852e2009-09-05 21:47:34 +00005514 mng_info->loop_iteration[loop_level]=0;
5515 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5516 continue;
5517 }
glennrp47b9dd52010-11-24 18:12:06 +00005518
cristy3ed852e2009-09-05 21:47:34 +00005519 if (memcmp(type,mng_ENDL,4) == 0)
5520 {
5521 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005522
cristy3ed852e2009-09-05 21:47:34 +00005523 if (skipping_loop > 0)
5524 {
5525 if (skipping_loop == loop_level)
5526 {
5527 /*
5528 Found end of zero-iteration loop.
5529 */
5530 skipping_loop=(-1);
5531 mng_info->loop_active[loop_level]=0;
5532 }
5533 }
glennrp47b9dd52010-11-24 18:12:06 +00005534
cristy3ed852e2009-09-05 21:47:34 +00005535 else
5536 {
5537 if (mng_info->loop_active[loop_level] == 1)
5538 {
5539 mng_info->loop_count[loop_level]--;
5540 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005541
cristy3ed852e2009-09-05 21:47:34 +00005542 if (logging != MagickFalse)
5543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005544 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005545 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005546 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005547
cristy3ed852e2009-09-05 21:47:34 +00005548 if (mng_info->loop_count[loop_level] != 0)
5549 {
5550 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5551 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005552
cristy3ed852e2009-09-05 21:47:34 +00005553 if (offset < 0)
5554 ThrowReaderException(CorruptImageError,
5555 "ImproperImageHeader");
5556 }
glennrp47b9dd52010-11-24 18:12:06 +00005557
cristy3ed852e2009-09-05 21:47:34 +00005558 else
5559 {
5560 short
5561 last_level;
5562
5563 /*
5564 Finished loop.
5565 */
5566 mng_info->loop_active[loop_level]=0;
5567 last_level=(-1);
5568 for (i=0; i < loop_level; i++)
5569 if (mng_info->loop_active[i] == 1)
5570 last_level=(short) i;
5571 loop_level=last_level;
5572 }
5573 }
5574 }
glennrp47b9dd52010-11-24 18:12:06 +00005575
cristy3ed852e2009-09-05 21:47:34 +00005576 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5577 continue;
5578 }
glennrp47b9dd52010-11-24 18:12:06 +00005579
cristy3ed852e2009-09-05 21:47:34 +00005580 if (memcmp(type,mng_CLON,4) == 0)
5581 {
5582 if (mng_info->clon_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005583 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005584 CoderError,"CLON is not implemented yet","`%s'",
5585 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005586
cristy3ed852e2009-09-05 21:47:34 +00005587 mng_info->clon_warning++;
5588 }
glennrp47b9dd52010-11-24 18:12:06 +00005589
cristy3ed852e2009-09-05 21:47:34 +00005590 if (memcmp(type,mng_MAGN,4) == 0)
5591 {
5592 png_uint_16
5593 magn_first,
5594 magn_last,
5595 magn_mb,
5596 magn_ml,
5597 magn_mr,
5598 magn_mt,
5599 magn_mx,
5600 magn_my,
5601 magn_methx,
5602 magn_methy;
5603
5604 if (length > 1)
5605 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005606
cristy3ed852e2009-09-05 21:47:34 +00005607 else
5608 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005609
cristy3ed852e2009-09-05 21:47:34 +00005610 if (length > 3)
5611 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005612
cristy3ed852e2009-09-05 21:47:34 +00005613 else
5614 magn_last=magn_first;
5615#ifndef MNG_OBJECT_BUFFERS
5616 if (magn_first || magn_last)
5617 if (mng_info->magn_warning == 0)
5618 {
cristy16ea1392012-03-21 20:38:41 +00005619 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005620 GetMagickModule(),CoderError,
5621 "MAGN is not implemented yet for nonzero objects",
5622 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005623
cristy3ed852e2009-09-05 21:47:34 +00005624 mng_info->magn_warning++;
5625 }
5626#endif
5627 if (length > 4)
5628 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005629
cristy3ed852e2009-09-05 21:47:34 +00005630 else
5631 magn_methx=0;
5632
5633 if (length > 6)
5634 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005635
cristy3ed852e2009-09-05 21:47:34 +00005636 else
5637 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005638
cristy3ed852e2009-09-05 21:47:34 +00005639 if (magn_mx == 0)
5640 magn_mx=1;
5641
5642 if (length > 8)
5643 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005644
cristy3ed852e2009-09-05 21:47:34 +00005645 else
5646 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005647
cristy3ed852e2009-09-05 21:47:34 +00005648 if (magn_my == 0)
5649 magn_my=1;
5650
5651 if (length > 10)
5652 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005653
cristy3ed852e2009-09-05 21:47:34 +00005654 else
5655 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005656
cristy3ed852e2009-09-05 21:47:34 +00005657 if (magn_ml == 0)
5658 magn_ml=1;
5659
5660 if (length > 12)
5661 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005662
cristy3ed852e2009-09-05 21:47:34 +00005663 else
5664 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005665
cristy3ed852e2009-09-05 21:47:34 +00005666 if (magn_mr == 0)
5667 magn_mr=1;
5668
5669 if (length > 14)
5670 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005671
cristy3ed852e2009-09-05 21:47:34 +00005672 else
5673 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005674
cristy3ed852e2009-09-05 21:47:34 +00005675 if (magn_mt == 0)
5676 magn_mt=1;
5677
5678 if (length > 16)
5679 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005680
cristy3ed852e2009-09-05 21:47:34 +00005681 else
5682 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005683
cristy3ed852e2009-09-05 21:47:34 +00005684 if (magn_mb == 0)
5685 magn_mb=1;
5686
5687 if (length > 17)
5688 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005689
cristy3ed852e2009-09-05 21:47:34 +00005690 else
5691 magn_methy=magn_methx;
5692
glennrp47b9dd52010-11-24 18:12:06 +00005693
cristy3ed852e2009-09-05 21:47:34 +00005694 if (magn_methx > 5 || magn_methy > 5)
5695 if (mng_info->magn_warning == 0)
5696 {
cristy16ea1392012-03-21 20:38:41 +00005697 (void) ThrowMagickException(exception,
cristy3ed852e2009-09-05 21:47:34 +00005698 GetMagickModule(),CoderError,
5699 "Unknown MAGN method in MNG datastream","`%s'",
5700 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005701
cristy3ed852e2009-09-05 21:47:34 +00005702 mng_info->magn_warning++;
5703 }
5704#ifdef MNG_OBJECT_BUFFERS
5705 /* Magnify existing objects in the range magn_first to magn_last */
5706#endif
5707 if (magn_first == 0 || magn_last == 0)
5708 {
5709 /* Save the magnification factors for object 0 */
5710 mng_info->magn_mb=magn_mb;
5711 mng_info->magn_ml=magn_ml;
5712 mng_info->magn_mr=magn_mr;
5713 mng_info->magn_mt=magn_mt;
5714 mng_info->magn_mx=magn_mx;
5715 mng_info->magn_my=magn_my;
5716 mng_info->magn_methx=magn_methx;
5717 mng_info->magn_methy=magn_methy;
5718 }
5719 }
glennrp47b9dd52010-11-24 18:12:06 +00005720
cristy3ed852e2009-09-05 21:47:34 +00005721 if (memcmp(type,mng_PAST,4) == 0)
5722 {
5723 if (mng_info->past_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005724 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005725 CoderError,"PAST is not implemented yet","`%s'",
5726 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005727
cristy3ed852e2009-09-05 21:47:34 +00005728 mng_info->past_warning++;
5729 }
glennrp47b9dd52010-11-24 18:12:06 +00005730
cristy3ed852e2009-09-05 21:47:34 +00005731 if (memcmp(type,mng_SHOW,4) == 0)
5732 {
5733 if (mng_info->show_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005734 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005735 CoderError,"SHOW is not implemented yet","`%s'",
5736 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005737
cristy3ed852e2009-09-05 21:47:34 +00005738 mng_info->show_warning++;
5739 }
glennrp47b9dd52010-11-24 18:12:06 +00005740
cristy3ed852e2009-09-05 21:47:34 +00005741 if (memcmp(type,mng_sBIT,4) == 0)
5742 {
5743 if (length < 4)
5744 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005745
cristy3ed852e2009-09-05 21:47:34 +00005746 else
5747 {
5748 mng_info->global_sbit.gray=p[0];
5749 mng_info->global_sbit.red=p[0];
5750 mng_info->global_sbit.green=p[1];
5751 mng_info->global_sbit.blue=p[2];
5752 mng_info->global_sbit.alpha=p[3];
5753 mng_info->have_global_sbit=MagickTrue;
5754 }
5755 }
5756 if (memcmp(type,mng_pHYs,4) == 0)
5757 {
5758 if (length > 8)
5759 {
5760 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005761 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005762 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005763 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005764 mng_info->global_phys_unit_type=p[8];
5765 mng_info->have_global_phys=MagickTrue;
5766 }
glennrp47b9dd52010-11-24 18:12:06 +00005767
cristy3ed852e2009-09-05 21:47:34 +00005768 else
5769 mng_info->have_global_phys=MagickFalse;
5770 }
5771 if (memcmp(type,mng_pHYg,4) == 0)
5772 {
5773 if (mng_info->phyg_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005774 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005775 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005776
cristy3ed852e2009-09-05 21:47:34 +00005777 mng_info->phyg_warning++;
5778 }
5779 if (memcmp(type,mng_BASI,4) == 0)
5780 {
5781 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005782
cristy3ed852e2009-09-05 21:47:34 +00005783 if (mng_info->basi_warning == 0)
cristy16ea1392012-03-21 20:38:41 +00005784 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00005785 CoderError,"BASI is not implemented yet","`%s'",
5786 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005787
cristy3ed852e2009-09-05 21:47:34 +00005788 mng_info->basi_warning++;
5789#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005790 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005791 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005792 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005793 (p[6] << 8) | p[7]);
5794 basi_color_type=p[8];
5795 basi_compression_method=p[9];
5796 basi_filter_type=p[10];
5797 basi_interlace_method=p[11];
5798 if (length > 11)
5799 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005800
cristy3ed852e2009-09-05 21:47:34 +00005801 else
5802 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005803
cristy3ed852e2009-09-05 21:47:34 +00005804 if (length > 13)
5805 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005806
cristy3ed852e2009-09-05 21:47:34 +00005807 else
5808 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005809
cristy3ed852e2009-09-05 21:47:34 +00005810 if (length > 15)
5811 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005812
cristy3ed852e2009-09-05 21:47:34 +00005813 else
5814 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005815
cristy3ed852e2009-09-05 21:47:34 +00005816 if (length > 17)
5817 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005818
cristy3ed852e2009-09-05 21:47:34 +00005819 else
5820 {
5821 if (basi_sample_depth == 16)
5822 basi_alpha=65535L;
5823 else
5824 basi_alpha=255;
5825 }
glennrp47b9dd52010-11-24 18:12:06 +00005826
cristy3ed852e2009-09-05 21:47:34 +00005827 if (length > 19)
5828 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005829
cristy3ed852e2009-09-05 21:47:34 +00005830 else
5831 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005832
cristy3ed852e2009-09-05 21:47:34 +00005833#endif
5834 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5835 continue;
5836 }
glennrp47b9dd52010-11-24 18:12:06 +00005837
cristy3ed852e2009-09-05 21:47:34 +00005838 if (memcmp(type,mng_IHDR,4)
5839#if defined(JNG_SUPPORTED)
5840 && memcmp(type,mng_JHDR,4)
5841#endif
5842 )
5843 {
5844 /* Not an IHDR or JHDR chunk */
5845 if (length)
5846 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005847
cristy3ed852e2009-09-05 21:47:34 +00005848 continue;
5849 }
5850/* Process IHDR */
5851 if (logging != MagickFalse)
5852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5853 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005854
cristy3ed852e2009-09-05 21:47:34 +00005855 mng_info->exists[object_id]=MagickTrue;
5856 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005857
cristy3ed852e2009-09-05 21:47:34 +00005858 if (mng_info->invisible[object_id])
5859 {
5860 if (logging != MagickFalse)
5861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5862 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005863
cristy3ed852e2009-09-05 21:47:34 +00005864 skip_to_iend=MagickTrue;
5865 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5866 continue;
5867 }
5868#if defined(MNG_INSERT_LAYERS)
5869 if (length < 8)
5870 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005871
cristy8182b072010-05-30 20:10:53 +00005872 image_width=(size_t) mng_get_long(p);
5873 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005874#endif
5875 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5876
5877 /*
5878 Insert a transparent background layer behind the entire animation
5879 if it is not full screen.
5880 */
5881#if defined(MNG_INSERT_LAYERS)
5882 if (insert_layers && mng_type && first_mng_object)
5883 {
5884 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5885 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005886 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005887 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005888 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005889 {
cristy16ea1392012-03-21 20:38:41 +00005890 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005891 {
5892 /*
5893 Allocate next image structure.
5894 */
cristy16ea1392012-03-21 20:38:41 +00005895 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005896
cristy3ed852e2009-09-05 21:47:34 +00005897 if (GetNextImageInList(image) == (Image *) NULL)
5898 {
5899 image=DestroyImageList(image);
5900 MngInfoFreeStruct(mng_info,&have_mng_structure);
5901 return((Image *) NULL);
5902 }
glennrp47b9dd52010-11-24 18:12:06 +00005903
cristy3ed852e2009-09-05 21:47:34 +00005904 image=SyncNextImageInList(image);
5905 }
5906 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005907
cristy3ed852e2009-09-05 21:47:34 +00005908 if (term_chunk_found)
5909 {
5910 image->start_loop=MagickTrue;
5911 image->iterations=mng_iterations;
5912 term_chunk_found=MagickFalse;
5913 }
glennrp47b9dd52010-11-24 18:12:06 +00005914
cristy3ed852e2009-09-05 21:47:34 +00005915 else
5916 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005917
5918 /* Make a background rectangle. */
5919
cristy3ed852e2009-09-05 21:47:34 +00005920 image->delay=0;
5921 image->columns=mng_info->mng_width;
5922 image->rows=mng_info->mng_height;
5923 image->page.width=mng_info->mng_width;
5924 image->page.height=mng_info->mng_height;
5925 image->page.x=0;
5926 image->page.y=0;
5927 image->background_color=mng_background_color;
cristy16ea1392012-03-21 20:38:41 +00005928 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00005929 if (logging != MagickFalse)
5930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005931 " Inserted transparent background layer, W=%.20g, H=%.20g",
5932 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005933 }
5934 }
5935 /*
5936 Insert a background layer behind the upcoming image if
5937 framing_mode is 3, and we haven't already inserted one.
5938 */
5939 if (insert_layers && (mng_info->framing_mode == 3) &&
5940 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5941 (simplicity & 0x08)))
5942 {
cristy16ea1392012-03-21 20:38:41 +00005943 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005944 {
5945 /*
5946 Allocate next image structure.
5947 */
cristy16ea1392012-03-21 20:38:41 +00005948 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005949
cristy3ed852e2009-09-05 21:47:34 +00005950 if (GetNextImageInList(image) == (Image *) NULL)
5951 {
5952 image=DestroyImageList(image);
5953 MngInfoFreeStruct(mng_info,&have_mng_structure);
5954 return((Image *) NULL);
5955 }
glennrp47b9dd52010-11-24 18:12:06 +00005956
cristy3ed852e2009-09-05 21:47:34 +00005957 image=SyncNextImageInList(image);
5958 }
glennrp0fe50b42010-11-16 03:52:51 +00005959
cristy3ed852e2009-09-05 21:47:34 +00005960 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005961
cristy3ed852e2009-09-05 21:47:34 +00005962 if (term_chunk_found)
5963 {
5964 image->start_loop=MagickTrue;
5965 image->iterations=mng_iterations;
5966 term_chunk_found=MagickFalse;
5967 }
glennrp0fe50b42010-11-16 03:52:51 +00005968
cristy3ed852e2009-09-05 21:47:34 +00005969 else
5970 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005971
cristy3ed852e2009-09-05 21:47:34 +00005972 image->delay=0;
5973 image->columns=subframe_width;
5974 image->rows=subframe_height;
5975 image->page.width=subframe_width;
5976 image->page.height=subframe_height;
5977 image->page.x=mng_info->clip.left;
5978 image->page.y=mng_info->clip.top;
5979 image->background_color=mng_background_color;
5980 image->matte=MagickFalse;
cristy16ea1392012-03-21 20:38:41 +00005981 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00005982
cristy3ed852e2009-09-05 21:47:34 +00005983 if (logging != MagickFalse)
5984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005985 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005986 (double) mng_info->clip.left,(double) mng_info->clip.right,
5987 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005988 }
5989#endif /* MNG_INSERT_LAYERS */
5990 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005991
cristy16ea1392012-03-21 20:38:41 +00005992 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005993 {
5994 /*
5995 Allocate next image structure.
5996 */
cristy16ea1392012-03-21 20:38:41 +00005997 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00005998
cristy3ed852e2009-09-05 21:47:34 +00005999 if (GetNextImageInList(image) == (Image *) NULL)
6000 {
6001 image=DestroyImageList(image);
6002 MngInfoFreeStruct(mng_info,&have_mng_structure);
6003 return((Image *) NULL);
6004 }
glennrp47b9dd52010-11-24 18:12:06 +00006005
cristy3ed852e2009-09-05 21:47:34 +00006006 image=SyncNextImageInList(image);
6007 }
6008 mng_info->image=image;
6009 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6010 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00006011
cristy3ed852e2009-09-05 21:47:34 +00006012 if (status == MagickFalse)
6013 break;
glennrp0fe50b42010-11-16 03:52:51 +00006014
cristy3ed852e2009-09-05 21:47:34 +00006015 if (term_chunk_found)
6016 {
6017 image->start_loop=MagickTrue;
6018 term_chunk_found=MagickFalse;
6019 }
glennrp0fe50b42010-11-16 03:52:51 +00006020
cristy3ed852e2009-09-05 21:47:34 +00006021 else
6022 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006023
cristy3ed852e2009-09-05 21:47:34 +00006024 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6025 {
6026 image->delay=frame_delay;
6027 frame_delay=default_frame_delay;
6028 }
glennrp0fe50b42010-11-16 03:52:51 +00006029
cristy3ed852e2009-09-05 21:47:34 +00006030 else
6031 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006032
cristy3ed852e2009-09-05 21:47:34 +00006033 image->page.width=mng_info->mng_width;
6034 image->page.height=mng_info->mng_height;
6035 image->page.x=mng_info->x_off[object_id];
6036 image->page.y=mng_info->y_off[object_id];
6037 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006038
cristy3ed852e2009-09-05 21:47:34 +00006039 /*
6040 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6041 */
glennrp47b9dd52010-11-24 18:12:06 +00006042
cristy3ed852e2009-09-05 21:47:34 +00006043 if (logging != MagickFalse)
6044 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6045 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6046 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006047
cristybb503372010-05-27 20:51:26 +00006048 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006049
cristy3ed852e2009-09-05 21:47:34 +00006050 if (offset < 0)
6051 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6052 }
6053
6054 previous=image;
6055 mng_info->image=image;
6056 mng_info->mng_type=mng_type;
6057 mng_info->object_id=object_id;
6058
6059 if (memcmp(type,mng_IHDR,4) == 0)
6060 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006061
cristy3ed852e2009-09-05 21:47:34 +00006062#if defined(JNG_SUPPORTED)
6063 else
6064 image=ReadOneJNGImage(mng_info,image_info,exception);
6065#endif
6066
6067 if (image == (Image *) NULL)
6068 {
6069 if (IsImageObject(previous) != MagickFalse)
6070 {
6071 (void) DestroyImageList(previous);
6072 (void) CloseBlob(previous);
6073 }
glennrp47b9dd52010-11-24 18:12:06 +00006074
cristy3ed852e2009-09-05 21:47:34 +00006075 MngInfoFreeStruct(mng_info,&have_mng_structure);
6076 return((Image *) NULL);
6077 }
glennrp0fe50b42010-11-16 03:52:51 +00006078
cristy3ed852e2009-09-05 21:47:34 +00006079 if (image->columns == 0 || image->rows == 0)
6080 {
6081 (void) CloseBlob(image);
6082 image=DestroyImageList(image);
6083 MngInfoFreeStruct(mng_info,&have_mng_structure);
6084 return((Image *) NULL);
6085 }
glennrp0fe50b42010-11-16 03:52:51 +00006086
cristy3ed852e2009-09-05 21:47:34 +00006087 mng_info->image=image;
6088
6089 if (mng_type)
6090 {
6091 MngBox
6092 crop_box;
6093
6094 if (mng_info->magn_methx || mng_info->magn_methy)
6095 {
6096 png_uint_32
6097 magnified_height,
6098 magnified_width;
6099
6100 if (logging != MagickFalse)
6101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6102 " Processing MNG MAGN chunk");
6103
6104 if (mng_info->magn_methx == 1)
6105 {
6106 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006107
cristy3ed852e2009-09-05 21:47:34 +00006108 if (image->columns > 1)
6109 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006110
cristy3ed852e2009-09-05 21:47:34 +00006111 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006112 magnified_width += (png_uint_32)
6113 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006114 }
glennrp47b9dd52010-11-24 18:12:06 +00006115
cristy3ed852e2009-09-05 21:47:34 +00006116 else
6117 {
cristy4e5bc842010-06-09 13:56:01 +00006118 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006119
cristy3ed852e2009-09-05 21:47:34 +00006120 if (image->columns > 1)
6121 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006122
cristy3ed852e2009-09-05 21:47:34 +00006123 if (image->columns > 2)
6124 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006125
cristy3ed852e2009-09-05 21:47:34 +00006126 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006127 magnified_width += (png_uint_32)
6128 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006129 }
glennrp47b9dd52010-11-24 18:12:06 +00006130
cristy3ed852e2009-09-05 21:47:34 +00006131 if (mng_info->magn_methy == 1)
6132 {
6133 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006134
cristy3ed852e2009-09-05 21:47:34 +00006135 if (image->rows > 1)
6136 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006137
cristy3ed852e2009-09-05 21:47:34 +00006138 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006139 magnified_height += (png_uint_32)
6140 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006141 }
glennrp47b9dd52010-11-24 18:12:06 +00006142
cristy3ed852e2009-09-05 21:47:34 +00006143 else
6144 {
cristy4e5bc842010-06-09 13:56:01 +00006145 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006146
cristy3ed852e2009-09-05 21:47:34 +00006147 if (image->rows > 1)
6148 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006149
cristy3ed852e2009-09-05 21:47:34 +00006150 if (image->rows > 2)
6151 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006152
cristy3ed852e2009-09-05 21:47:34 +00006153 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006154 magnified_height += (png_uint_32)
6155 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006156 }
glennrp47b9dd52010-11-24 18:12:06 +00006157
cristy3ed852e2009-09-05 21:47:34 +00006158 if (magnified_height > image->rows ||
6159 magnified_width > image->columns)
6160 {
6161 Image
6162 *large_image;
6163
6164 int
6165 yy;
6166
cristy16ea1392012-03-21 20:38:41 +00006167 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006168 *next,
6169 *prev;
6170
6171 png_uint_16
6172 magn_methx,
6173 magn_methy;
6174
cristy16ea1392012-03-21 20:38:41 +00006175 ssize_t
6176 m,
6177 y;
6178
6179 register Quantum
6180 *n,
6181 *q;
6182
6183 register ssize_t
6184 x;
6185
glennrp47b9dd52010-11-24 18:12:06 +00006186 /* Allocate next image structure. */
6187
cristy3ed852e2009-09-05 21:47:34 +00006188 if (logging != MagickFalse)
6189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6190 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006191
cristy16ea1392012-03-21 20:38:41 +00006192 AcquireNextImage(image_info,image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006193
cristy3ed852e2009-09-05 21:47:34 +00006194 if (GetNextImageInList(image) == (Image *) NULL)
6195 {
6196 image=DestroyImageList(image);
6197 MngInfoFreeStruct(mng_info,&have_mng_structure);
6198 return((Image *) NULL);
6199 }
6200
6201 large_image=SyncNextImageInList(image);
6202
6203 large_image->columns=magnified_width;
6204 large_image->rows=magnified_height;
6205
6206 magn_methx=mng_info->magn_methx;
6207 magn_methy=mng_info->magn_methy;
6208
glennrp3faa9a32011-04-23 14:00:25 +00006209#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006210#define QM unsigned short
6211 if (magn_methx != 1 || magn_methy != 1)
6212 {
6213 /*
6214 Scale pixels to unsigned shorts to prevent
6215 overflow of intermediate values of interpolations
6216 */
cristybb503372010-05-27 20:51:26 +00006217 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006218 {
6219 q=GetAuthenticPixels(image,0,y,image->columns,1,
6220 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006221
cristybb503372010-05-27 20:51:26 +00006222 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006223 {
cristy16ea1392012-03-21 20:38:41 +00006224 SetPixelRed(image,ScaleQuantumToShort(
6225 GetPixelRed(image,q)),q);
6226 SetPixelGreen(image,ScaleQuantumToShort(
6227 GetPixelGreen(image,q)),q);
6228 SetPixelBlue(image,ScaleQuantumToShort(
6229 GetPixelBlue(image,q)),q);
6230 SetPixelAlpha(image,ScaleQuantumToShort(
6231 GetPixelAlpha(image,q)),q);
6232 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006233 }
glennrp47b9dd52010-11-24 18:12:06 +00006234
cristy3ed852e2009-09-05 21:47:34 +00006235 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6236 break;
6237 }
6238 }
6239#else
6240#define QM Quantum
6241#endif
6242
6243 if (image->matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006244 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006245
cristy3ed852e2009-09-05 21:47:34 +00006246 else
6247 {
cristy16ea1392012-03-21 20:38:41 +00006248 large_image->background_color.alpha=OpaqueAlpha;
6249 (void) SetImageBackgroundColor(large_image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006250
cristy3ed852e2009-09-05 21:47:34 +00006251 if (magn_methx == 4)
6252 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006253
cristy3ed852e2009-09-05 21:47:34 +00006254 if (magn_methx == 5)
6255 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006256
cristy3ed852e2009-09-05 21:47:34 +00006257 if (magn_methy == 4)
6258 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006259
cristy3ed852e2009-09-05 21:47:34 +00006260 if (magn_methy == 5)
6261 magn_methy=3;
6262 }
6263
6264 /* magnify the rows into the right side of the large image */
6265
6266 if (logging != MagickFalse)
6267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006268 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006269 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006270 yy=0;
cristy16ea1392012-03-21 20:38:41 +00006271 length=(size_t) image->columns*GetPixelChannels(image);
6272 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6273 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006274
cristy16ea1392012-03-21 20:38:41 +00006275 if ((prev == (Quantum *) NULL) ||
6276 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006277 {
6278 image=DestroyImageList(image);
6279 MngInfoFreeStruct(mng_info,&have_mng_structure);
6280 ThrowReaderException(ResourceLimitError,
6281 "MemoryAllocationFailed");
6282 }
glennrp47b9dd52010-11-24 18:12:06 +00006283
cristy3ed852e2009-09-05 21:47:34 +00006284 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6285 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006286
cristybb503372010-05-27 20:51:26 +00006287 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006288 {
6289 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006290 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006291
cristybb503372010-05-27 20:51:26 +00006292 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6293 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006294
cristybb503372010-05-27 20:51:26 +00006295 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6296 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006297
cristybb503372010-05-27 20:51:26 +00006298 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006299 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006300
cristy3ed852e2009-09-05 21:47:34 +00006301 else
cristybb503372010-05-27 20:51:26 +00006302 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006303
cristy3ed852e2009-09-05 21:47:34 +00006304 n=prev;
6305 prev=next;
6306 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006307
cristybb503372010-05-27 20:51:26 +00006308 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006309 {
6310 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6311 exception);
6312 (void) CopyMagickMemory(next,n,length);
6313 }
glennrp47b9dd52010-11-24 18:12:06 +00006314
cristy3ed852e2009-09-05 21:47:34 +00006315 for (i=0; i < m; i++, yy++)
6316 {
cristy16ea1392012-03-21 20:38:41 +00006317 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006318 *pixels;
6319
cristybb503372010-05-27 20:51:26 +00006320 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006321 pixels=prev;
6322 n=next;
6323 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006324 1,exception);
cristy16ea1392012-03-21 20:38:41 +00006325 q+=(large_image->columns-image->columns)*
6326 GetPixelChannels(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006327
cristybb503372010-05-27 20:51:26 +00006328 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006329 {
glennrpfd05d622011-02-25 04:10:33 +00006330 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006331 /*
6332 if (image->storage_class == PseudoClass)
6333 {
6334 }
6335 */
6336
6337 if (magn_methy <= 1)
6338 {
glennrpbb4f99d2011-05-22 11:13:17 +00006339 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006340 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6341 SetPixelGreen(large_image,GetPixelGreen(image,
6342 pixels),q);
6343 SetPixelBlue(large_image,GetPixelBlue(image,
6344 pixels),q);
6345 SetPixelAlpha(large_image,GetPixelAlpha(image,
6346 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006347 }
glennrp47b9dd52010-11-24 18:12:06 +00006348
cristy3ed852e2009-09-05 21:47:34 +00006349 else if (magn_methy == 2 || magn_methy == 4)
6350 {
6351 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006352 {
cristy16ea1392012-03-21 20:38:41 +00006353 SetPixelRed(large_image,GetPixelRed(image,
6354 pixels),q);
6355 SetPixelGreen(large_image,GetPixelGreen(image,
6356 pixels),q);
6357 SetPixelBlue(large_image,GetPixelBlue(image,
6358 pixels),q);
6359 SetPixelAlpha(large_image,GetPixelAlpha(image,
6360 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006361 }
glennrp47b9dd52010-11-24 18:12:06 +00006362
cristy3ed852e2009-09-05 21:47:34 +00006363 else
6364 {
6365 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006366 SetPixelRed(large_image,((QM) (((ssize_t)
6367 (2*i*(GetPixelRed(image,n)
6368 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006369 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006370 +GetPixelRed(image,pixels)))),q);
6371 SetPixelGreen(large_image,((QM) (((ssize_t)
6372 (2*i*(GetPixelGreen(image,n)
6373 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006374 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006375 +GetPixelGreen(image,pixels)))),q);
6376 SetPixelBlue(large_image,((QM) (((ssize_t)
6377 (2*i*(GetPixelBlue(image,n)
6378 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006379 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006380 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006381
cristy3ed852e2009-09-05 21:47:34 +00006382 if (image->matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006383 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6384 (2*i*(GetPixelAlpha(image,n)
6385 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006386 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006387 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006388 }
glennrp47b9dd52010-11-24 18:12:06 +00006389
cristy3ed852e2009-09-05 21:47:34 +00006390 if (magn_methy == 4)
6391 {
6392 /* Replicate nearest */
6393 if (i <= ((m+1) << 1))
cristy16ea1392012-03-21 20:38:41 +00006394 SetPixelAlpha(large_image,GetPixelAlpha(image,
6395 pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006396 else
cristy16ea1392012-03-21 20:38:41 +00006397 SetPixelAlpha(large_image,GetPixelAlpha(image,
6398 n),q);
cristy3ed852e2009-09-05 21:47:34 +00006399 }
6400 }
glennrp47b9dd52010-11-24 18:12:06 +00006401
cristy3ed852e2009-09-05 21:47:34 +00006402 else /* if (magn_methy == 3 || magn_methy == 5) */
6403 {
6404 /* Replicate nearest */
6405 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006406 {
cristy16ea1392012-03-21 20:38:41 +00006407 SetPixelRed(large_image,GetPixelRed(image,
6408 pixels),q);
6409 SetPixelGreen(large_image,GetPixelGreen(image,
6410 pixels),q);
6411 SetPixelBlue(large_image,GetPixelBlue(image,
6412 pixels),q);
6413 SetPixelAlpha(large_image,GetPixelAlpha(image,
6414 pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006415 }
glennrp47b9dd52010-11-24 18:12:06 +00006416
cristy3ed852e2009-09-05 21:47:34 +00006417 else
glennrpbb4f99d2011-05-22 11:13:17 +00006418 {
cristy16ea1392012-03-21 20:38:41 +00006419 SetPixelRed(large_image,GetPixelRed(image,n),q);
6420 SetPixelGreen(large_image,GetPixelGreen(image,n),
6421 q);
6422 SetPixelBlue(large_image,GetPixelBlue(image,n),
6423 q);
6424 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6425 q);
glennrpbb4f99d2011-05-22 11:13:17 +00006426 }
glennrp47b9dd52010-11-24 18:12:06 +00006427
cristy3ed852e2009-09-05 21:47:34 +00006428 if (magn_methy == 5)
6429 {
cristy16ea1392012-03-21 20:38:41 +00006430 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6431 (GetPixelAlpha(image,n)
6432 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006433 +m))/((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006434 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006435 }
6436 }
cristy16ea1392012-03-21 20:38:41 +00006437 n+=GetPixelChannels(image);
6438 q+=GetPixelChannels(large_image);
6439 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006440 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006441
cristy3ed852e2009-09-05 21:47:34 +00006442 if (SyncAuthenticPixels(large_image,exception) == 0)
6443 break;
glennrp47b9dd52010-11-24 18:12:06 +00006444
cristy3ed852e2009-09-05 21:47:34 +00006445 } /* i */
6446 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006447
cristy16ea1392012-03-21 20:38:41 +00006448 prev=(Quantum *) RelinquishMagickMemory(prev);
6449 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006450
6451 length=image->columns;
6452
6453 if (logging != MagickFalse)
6454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6455 " Delete original image");
6456
6457 DeleteImageFromList(&image);
6458
6459 image=large_image;
6460
6461 mng_info->image=image;
6462
6463 /* magnify the columns */
6464 if (logging != MagickFalse)
6465 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006466 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006467
cristybb503372010-05-27 20:51:26 +00006468 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006469 {
cristy16ea1392012-03-21 20:38:41 +00006470 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006471 *pixels;
6472
6473 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy16ea1392012-03-21 20:38:41 +00006474 pixels=q+(image->columns-length)*GetPixelChannels(image);
6475 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006476
cristybb503372010-05-27 20:51:26 +00006477 for (x=(ssize_t) (image->columns-length);
6478 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006479 {
cristy16ea1392012-03-21 20:38:41 +00006480 /* To do: Rewrite using Get/Set***PixelChannel() */
glennrp7c7b3152011-04-26 04:01:27 +00006481
cristybb503372010-05-27 20:51:26 +00006482 if (x == (ssize_t) (image->columns-length))
6483 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006484
cristybb503372010-05-27 20:51:26 +00006485 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6486 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006487
cristybb503372010-05-27 20:51:26 +00006488 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6489 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006490
cristybb503372010-05-27 20:51:26 +00006491 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006492 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006493
cristy3ed852e2009-09-05 21:47:34 +00006494 else
cristybb503372010-05-27 20:51:26 +00006495 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006496
cristy3ed852e2009-09-05 21:47:34 +00006497 for (i=0; i < m; i++)
6498 {
6499 if (magn_methx <= 1)
6500 {
6501 /* replicate previous */
cristy16ea1392012-03-21 20:38:41 +00006502 SetPixelRed(image,GetPixelRed(image,pixels),q);
6503 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6504 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6505 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006506 }
glennrp47b9dd52010-11-24 18:12:06 +00006507
cristy3ed852e2009-09-05 21:47:34 +00006508 else if (magn_methx == 2 || magn_methx == 4)
6509 {
6510 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006511 {
cristy16ea1392012-03-21 20:38:41 +00006512 SetPixelRed(image,GetPixelRed(image,pixels),q);
6513 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6514 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6515 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006516 }
glennrp47b9dd52010-11-24 18:12:06 +00006517
cristy16ea1392012-03-21 20:38:41 +00006518 /* To do: Rewrite using Get/Set***PixelChannel() */
cristy3ed852e2009-09-05 21:47:34 +00006519 else
6520 {
6521 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006522 SetPixelRed(image,(QM) ((2*i*(
6523 GetPixelRed(image,n)
6524 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006525 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006526 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006527
cristy16ea1392012-03-21 20:38:41 +00006528 SetPixelGreen(image,(QM) ((2*i*(
6529 GetPixelGreen(image,n)
6530 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006531 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006532 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006533
cristy16ea1392012-03-21 20:38:41 +00006534 SetPixelBlue(image,(QM) ((2*i*(
6535 GetPixelBlue(image,n)
6536 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006537 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006538 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006539 if (image->matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006540 SetPixelAlpha(image,(QM) ((2*i*(
6541 GetPixelAlpha(image,n)
6542 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006543 /((ssize_t) (m*2))+
cristy16ea1392012-03-21 20:38:41 +00006544 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006545 }
glennrp47b9dd52010-11-24 18:12:06 +00006546
cristy3ed852e2009-09-05 21:47:34 +00006547 if (magn_methx == 4)
6548 {
6549 /* Replicate nearest */
6550 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006551 {
cristy16ea1392012-03-21 20:38:41 +00006552 SetPixelAlpha(image,
6553 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006554 }
cristy3ed852e2009-09-05 21:47:34 +00006555 else
glennrpbb4f99d2011-05-22 11:13:17 +00006556 {
cristy16ea1392012-03-21 20:38:41 +00006557 SetPixelAlpha(image,
6558 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006559 }
cristy3ed852e2009-09-05 21:47:34 +00006560 }
6561 }
glennrp47b9dd52010-11-24 18:12:06 +00006562
cristy3ed852e2009-09-05 21:47:34 +00006563 else /* if (magn_methx == 3 || magn_methx == 5) */
6564 {
6565 /* Replicate nearest */
6566 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006567 {
cristy16ea1392012-03-21 20:38:41 +00006568 SetPixelRed(image,GetPixelRed(image,pixels),q);
6569 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6570 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6571 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006572 }
glennrp47b9dd52010-11-24 18:12:06 +00006573
cristy3ed852e2009-09-05 21:47:34 +00006574 else
glennrpbb4f99d2011-05-22 11:13:17 +00006575 {
cristy16ea1392012-03-21 20:38:41 +00006576 SetPixelRed(image,GetPixelRed(image,n),q);
6577 SetPixelGreen(image,GetPixelGreen(image,n),q);
6578 SetPixelBlue(image,GetPixelBlue(image,n),q);
6579 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006580 }
glennrp47b9dd52010-11-24 18:12:06 +00006581
cristy3ed852e2009-09-05 21:47:34 +00006582 if (magn_methx == 5)
6583 {
6584 /* Interpolate */
cristy16ea1392012-03-21 20:38:41 +00006585 SetPixelAlpha(image,
6586 (QM) ((2*i*( GetPixelAlpha(image,n)
6587 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006588 ((ssize_t) (m*2))
cristy16ea1392012-03-21 20:38:41 +00006589 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006590 }
6591 }
cristy16ea1392012-03-21 20:38:41 +00006592 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006593 }
cristy16ea1392012-03-21 20:38:41 +00006594 n+=GetPixelChannels(image);
6595 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006596 }
glennrp47b9dd52010-11-24 18:12:06 +00006597
cristy3ed852e2009-09-05 21:47:34 +00006598 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6599 break;
6600 }
glennrp3faa9a32011-04-23 14:00:25 +00006601#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006602 if (magn_methx != 1 || magn_methy != 1)
6603 {
6604 /*
6605 Rescale pixels to Quantum
6606 */
cristybb503372010-05-27 20:51:26 +00006607 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006608 {
6609 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006610
cristybb503372010-05-27 20:51:26 +00006611 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006612 {
cristy16ea1392012-03-21 20:38:41 +00006613 SetPixelRed(image,ScaleShortToQuantum(
6614 GetPixelRed(image,q)),q);
6615 SetPixelGreen(image,ScaleShortToQuantum(
6616 GetPixelGreen(image,q)),q);
6617 SetPixelBlue(image,ScaleShortToQuantum(
6618 GetPixelBlue(image,q)),q);
6619 SetPixelAlpha(image,ScaleShortToQuantum(
6620 GetPixelAlpha(image,q)),q);
6621 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006622 }
glennrp47b9dd52010-11-24 18:12:06 +00006623
cristy3ed852e2009-09-05 21:47:34 +00006624 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6625 break;
6626 }
6627 }
6628#endif
6629 if (logging != MagickFalse)
6630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6631 " Finished MAGN processing");
6632 }
6633 }
6634
6635 /*
6636 Crop_box is with respect to the upper left corner of the MNG.
6637 */
6638 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6639 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6640 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6641 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6642 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6643 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6644 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6645 if ((crop_box.left != (mng_info->image_box.left
6646 +mng_info->x_off[object_id])) ||
6647 (crop_box.right != (mng_info->image_box.right
6648 +mng_info->x_off[object_id])) ||
6649 (crop_box.top != (mng_info->image_box.top
6650 +mng_info->y_off[object_id])) ||
6651 (crop_box.bottom != (mng_info->image_box.bottom
6652 +mng_info->y_off[object_id])))
6653 {
6654 if (logging != MagickFalse)
6655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6656 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006657
cristy3ed852e2009-09-05 21:47:34 +00006658 if ((crop_box.left < crop_box.right) &&
6659 (crop_box.top < crop_box.bottom))
6660 {
6661 Image
6662 *im;
6663
6664 RectangleInfo
6665 crop_info;
6666
6667 /*
6668 Crop_info is with respect to the upper left corner of
6669 the image.
6670 */
6671 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6672 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006673 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6674 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006675 image->page.width=image->columns;
6676 image->page.height=image->rows;
6677 image->page.x=0;
6678 image->page.y=0;
6679 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006680
cristy3ed852e2009-09-05 21:47:34 +00006681 if (im != (Image *) NULL)
6682 {
6683 image->columns=im->columns;
6684 image->rows=im->rows;
6685 im=DestroyImage(im);
6686 image->page.width=image->columns;
6687 image->page.height=image->rows;
6688 image->page.x=crop_box.left;
6689 image->page.y=crop_box.top;
6690 }
6691 }
glennrp47b9dd52010-11-24 18:12:06 +00006692
cristy3ed852e2009-09-05 21:47:34 +00006693 else
6694 {
6695 /*
6696 No pixels in crop area. The MNG spec still requires
6697 a layer, though, so make a single transparent pixel in
6698 the top left corner.
6699 */
6700 image->columns=1;
6701 image->rows=1;
6702 image->colors=2;
cristy16ea1392012-03-21 20:38:41 +00006703 (void) SetImageBackgroundColor(image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006704 image->page.width=1;
6705 image->page.height=1;
6706 image->page.x=0;
6707 image->page.y=0;
6708 }
6709 }
6710#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6711 image=mng_info->image;
6712#endif
6713 }
6714
glennrp2b013e42010-11-24 16:55:50 +00006715#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6716 /* PNG does not handle depths greater than 16 so reduce it even
cristy16ea1392012-03-21 20:38:41 +00006717 * if lossy.
glennrp2b013e42010-11-24 16:55:50 +00006718 */
6719 if (image->depth > 16)
6720 image->depth=16;
6721#endif
6722
glennrp3faa9a32011-04-23 14:00:25 +00006723#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00006724 if (image->depth > 8)
6725 {
6726 /* To do: fill low byte properly */
6727 image->depth=16;
6728 }
6729
cristy16ea1392012-03-21 20:38:41 +00006730 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00006731 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006732#endif
glennrpd6afd542010-11-19 01:53:05 +00006733
cristy3ed852e2009-09-05 21:47:34 +00006734 if (image_info->number_scenes != 0)
6735 {
6736 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006737 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006738 break;
6739 }
glennrpd6afd542010-11-19 01:53:05 +00006740
cristy3ed852e2009-09-05 21:47:34 +00006741 if (logging != MagickFalse)
6742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6743 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006744
cristy3ed852e2009-09-05 21:47:34 +00006745 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006746
cristy3ed852e2009-09-05 21:47:34 +00006747 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006748
cristy3ed852e2009-09-05 21:47:34 +00006749 if (logging != MagickFalse)
6750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6751 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006752
cristy3ed852e2009-09-05 21:47:34 +00006753#if defined(MNG_INSERT_LAYERS)
6754 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6755 (mng_info->mng_height))
6756 {
6757 /*
6758 Insert a background layer if nothing else was found.
6759 */
6760 if (logging != MagickFalse)
6761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6762 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006763
cristy16ea1392012-03-21 20:38:41 +00006764 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006765 {
6766 /*
6767 Allocate next image structure.
6768 */
cristy16ea1392012-03-21 20:38:41 +00006769 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +00006770 if (GetNextImageInList(image) == (Image *) NULL)
6771 {
6772 image=DestroyImageList(image);
6773 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006774
cristy3ed852e2009-09-05 21:47:34 +00006775 if (logging != MagickFalse)
6776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6777 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006778
cristy3ed852e2009-09-05 21:47:34 +00006779 return((Image *) NULL);
6780 }
6781 image=SyncNextImageInList(image);
6782 }
6783 image->columns=mng_info->mng_width;
6784 image->rows=mng_info->mng_height;
6785 image->page.width=mng_info->mng_width;
6786 image->page.height=mng_info->mng_height;
6787 image->page.x=0;
6788 image->page.y=0;
6789 image->background_color=mng_background_color;
6790 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006791
cristy3ed852e2009-09-05 21:47:34 +00006792 if (image_info->ping == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00006793 (void) SetImageBackgroundColor(image,exception);
glennrp0fe50b42010-11-16 03:52:51 +00006794
cristy3ed852e2009-09-05 21:47:34 +00006795 mng_info->image_found++;
6796 }
6797#endif
6798 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006799
cristy3ed852e2009-09-05 21:47:34 +00006800 if (mng_iterations == 1)
6801 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006802
cristy3ed852e2009-09-05 21:47:34 +00006803 while (GetPreviousImageInList(image) != (Image *) NULL)
6804 {
6805 image_count++;
6806 if (image_count > 10*mng_info->image_found)
6807 {
6808 if (logging != MagickFalse)
6809 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006810
cristy16ea1392012-03-21 20:38:41 +00006811 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006812 CoderError,"Linked list is corrupted, beginning of list not found",
6813 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006814
cristy3ed852e2009-09-05 21:47:34 +00006815 return((Image *) NULL);
6816 }
glennrp0fe50b42010-11-16 03:52:51 +00006817
cristy3ed852e2009-09-05 21:47:34 +00006818 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006819
cristy3ed852e2009-09-05 21:47:34 +00006820 if (GetNextImageInList(image) == (Image *) NULL)
6821 {
6822 if (logging != MagickFalse)
6823 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006824
cristy16ea1392012-03-21 20:38:41 +00006825 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006826 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6827 image_info->filename);
6828 }
6829 }
glennrp47b9dd52010-11-24 18:12:06 +00006830
cristy3ed852e2009-09-05 21:47:34 +00006831 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6832 GetNextImageInList(image) ==
6833 (Image *) NULL)
6834 {
6835 if (logging != MagickFalse)
6836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6837 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006838
cristy16ea1392012-03-21 20:38:41 +00006839 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006840 CoderError,"image->next for first image is NULL but shouldn't be.",
6841 "`%s'",image_info->filename);
6842 }
glennrp47b9dd52010-11-24 18:12:06 +00006843
cristy3ed852e2009-09-05 21:47:34 +00006844 if (mng_info->image_found == 0)
6845 {
6846 if (logging != MagickFalse)
6847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6848 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006849
cristy16ea1392012-03-21 20:38:41 +00006850 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +00006851 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006852
cristy3ed852e2009-09-05 21:47:34 +00006853 if (image != (Image *) NULL)
6854 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006855
cristy3ed852e2009-09-05 21:47:34 +00006856 MngInfoFreeStruct(mng_info,&have_mng_structure);
6857 return((Image *) NULL);
6858 }
6859
6860 if (mng_info->ticks_per_second)
6861 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6862 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006863
cristy3ed852e2009-09-05 21:47:34 +00006864 else
6865 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006866
cristy3ed852e2009-09-05 21:47:34 +00006867 /* Find final nonzero image delay */
6868 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006869
cristy3ed852e2009-09-05 21:47:34 +00006870 while (GetNextImageInList(image) != (Image *) NULL)
6871 {
6872 if (image->delay)
6873 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006874
cristy3ed852e2009-09-05 21:47:34 +00006875 image=GetNextImageInList(image);
6876 }
glennrp0fe50b42010-11-16 03:52:51 +00006877
cristy3ed852e2009-09-05 21:47:34 +00006878 if (final_delay < final_image_delay)
6879 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006880
cristy3ed852e2009-09-05 21:47:34 +00006881 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006882
cristy3ed852e2009-09-05 21:47:34 +00006883 if (logging != MagickFalse)
6884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006885 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6886 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006887
cristy3ed852e2009-09-05 21:47:34 +00006888 if (logging != MagickFalse)
6889 {
6890 int
6891 scene;
6892
6893 scene=0;
6894 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006895
cristy3ed852e2009-09-05 21:47:34 +00006896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6897 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006898
cristy3ed852e2009-09-05 21:47:34 +00006899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006900 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006901
cristy3ed852e2009-09-05 21:47:34 +00006902 while (GetNextImageInList(image) != (Image *) NULL)
6903 {
6904 image=GetNextImageInList(image);
6905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006906 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006907 }
6908 }
6909
6910 image=GetFirstImageInList(image);
6911#ifdef MNG_COALESCE_LAYERS
6912 if (insert_layers)
6913 {
6914 Image
6915 *next_image,
6916 *next;
6917
cristybb503372010-05-27 20:51:26 +00006918 size_t
cristy3ed852e2009-09-05 21:47:34 +00006919 scene;
6920
6921 if (logging != MagickFalse)
6922 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006923
cristy3ed852e2009-09-05 21:47:34 +00006924 scene=image->scene;
cristy16ea1392012-03-21 20:38:41 +00006925 next_image=CoalesceImages(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006926
cristy3ed852e2009-09-05 21:47:34 +00006927 if (next_image == (Image *) NULL)
6928 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006929
cristy3ed852e2009-09-05 21:47:34 +00006930 image=DestroyImageList(image);
6931 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006932
cristy3ed852e2009-09-05 21:47:34 +00006933 for (next=image; next != (Image *) NULL; next=next_image)
6934 {
6935 next->page.width=mng_info->mng_width;
6936 next->page.height=mng_info->mng_height;
6937 next->page.x=0;
6938 next->page.y=0;
6939 next->scene=scene++;
6940 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006941
cristy3ed852e2009-09-05 21:47:34 +00006942 if (next_image == (Image *) NULL)
6943 break;
glennrp47b9dd52010-11-24 18:12:06 +00006944
cristy3ed852e2009-09-05 21:47:34 +00006945 if (next->delay == 0)
6946 {
6947 scene--;
6948 next_image->previous=GetPreviousImageInList(next);
6949 if (GetPreviousImageInList(next) == (Image *) NULL)
6950 image=next_image;
6951 else
6952 next->previous->next=next_image;
6953 next=DestroyImage(next);
6954 }
6955 }
6956 }
6957#endif
6958
6959 while (GetNextImageInList(image) != (Image *) NULL)
6960 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006961
cristy3ed852e2009-09-05 21:47:34 +00006962 image->dispose=BackgroundDispose;
6963
6964 if (logging != MagickFalse)
6965 {
6966 int
6967 scene;
6968
6969 scene=0;
6970 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006971
cristy3ed852e2009-09-05 21:47:34 +00006972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6973 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006974
cristy3ed852e2009-09-05 21:47:34 +00006975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006976 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6977 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006978
cristy3ed852e2009-09-05 21:47:34 +00006979 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006980 {
6981 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006982
cristyf2faecf2010-05-28 19:19:36 +00006983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006984 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6985 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006986 }
6987 }
glennrp47b9dd52010-11-24 18:12:06 +00006988
cristy3ed852e2009-09-05 21:47:34 +00006989 image=GetFirstImageInList(image);
6990 MngInfoFreeStruct(mng_info,&have_mng_structure);
6991 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006992
cristy3ed852e2009-09-05 21:47:34 +00006993 if (logging != MagickFalse)
6994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006995
cristy3ed852e2009-09-05 21:47:34 +00006996 return(GetFirstImageInList(image));
6997}
glennrp25c1e2b2010-03-25 01:39:56 +00006998#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006999static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7000{
7001 printf("Your PNG library is too old: You have libpng-%s\n",
7002 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00007003
cristy3ed852e2009-09-05 21:47:34 +00007004 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7005 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00007006
cristy3ed852e2009-09-05 21:47:34 +00007007 return(Image *) NULL;
7008}
glennrp47b9dd52010-11-24 18:12:06 +00007009
cristy3ed852e2009-09-05 21:47:34 +00007010static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7011{
7012 return(ReadPNGImage(image_info,exception));
7013}
glennrp25c1e2b2010-03-25 01:39:56 +00007014#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00007015#endif
7016
7017/*
7018%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7019% %
7020% %
7021% %
7022% R e g i s t e r P N G I m a g e %
7023% %
7024% %
7025% %
7026%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7027%
7028% RegisterPNGImage() adds properties for the PNG image format to
7029% the list of supported formats. The properties include the image format
7030% tag, a method to read and/or write the format, whether the format
7031% supports the saving of more than one frame to the same file or blob,
7032% whether the format supports native in-memory I/O, and a brief
7033% description of the format.
7034%
7035% The format of the RegisterPNGImage method is:
7036%
cristybb503372010-05-27 20:51:26 +00007037% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007038%
7039*/
cristybb503372010-05-27 20:51:26 +00007040ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00007041{
7042 char
7043 version[MaxTextExtent];
7044
7045 MagickInfo
7046 *entry;
7047
7048 static const char
7049 *PNGNote=
7050 {
7051 "See http://www.libpng.org/ for details about the PNG format."
7052 },
glennrp47b9dd52010-11-24 18:12:06 +00007053
cristy3ed852e2009-09-05 21:47:34 +00007054 *JNGNote=
7055 {
7056 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7057 "format."
7058 },
glennrp47b9dd52010-11-24 18:12:06 +00007059
cristy3ed852e2009-09-05 21:47:34 +00007060 *MNGNote=
7061 {
7062 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7063 "format."
7064 };
7065
7066 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007067
cristy3ed852e2009-09-05 21:47:34 +00007068#if defined(PNG_LIBPNG_VER_STRING)
7069 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7070 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007071
cristy3ed852e2009-09-05 21:47:34 +00007072 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7073 {
7074 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7075 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7076 MaxTextExtent);
7077 }
7078#endif
glennrp47b9dd52010-11-24 18:12:06 +00007079
cristy3ed852e2009-09-05 21:47:34 +00007080 entry=SetMagickInfo("MNG");
7081 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007082
cristy3ed852e2009-09-05 21:47:34 +00007083#if defined(MAGICKCORE_PNG_DELEGATE)
7084 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7085 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7086#endif
glennrp47b9dd52010-11-24 18:12:06 +00007087
cristy3ed852e2009-09-05 21:47:34 +00007088 entry->magick=(IsImageFormatHandler *) IsMNG;
7089 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007090
cristy3ed852e2009-09-05 21:47:34 +00007091 if (*version != '\0')
7092 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007093
cristy3ed852e2009-09-05 21:47:34 +00007094 entry->module=ConstantString("PNG");
7095 entry->note=ConstantString(MNGNote);
7096 (void) RegisterMagickInfo(entry);
7097
7098 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007099
cristy3ed852e2009-09-05 21:47:34 +00007100#if defined(MAGICKCORE_PNG_DELEGATE)
7101 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7102 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7103#endif
glennrp47b9dd52010-11-24 18:12:06 +00007104
cristy3ed852e2009-09-05 21:47:34 +00007105 entry->magick=(IsImageFormatHandler *) IsPNG;
7106 entry->adjoin=MagickFalse;
7107 entry->description=ConstantString("Portable Network Graphics");
7108 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007109
cristy3ed852e2009-09-05 21:47:34 +00007110 if (*version != '\0')
7111 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007112
cristy3ed852e2009-09-05 21:47:34 +00007113 entry->note=ConstantString(PNGNote);
7114 (void) RegisterMagickInfo(entry);
7115
7116 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007117
cristy3ed852e2009-09-05 21:47:34 +00007118#if defined(MAGICKCORE_PNG_DELEGATE)
7119 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7120 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7121#endif
glennrp47b9dd52010-11-24 18:12:06 +00007122
cristy3ed852e2009-09-05 21:47:34 +00007123 entry->magick=(IsImageFormatHandler *) IsPNG;
7124 entry->adjoin=MagickFalse;
7125 entry->description=ConstantString(
7126 "8-bit indexed with optional binary transparency");
7127 entry->module=ConstantString("PNG");
7128 (void) RegisterMagickInfo(entry);
7129
7130 entry=SetMagickInfo("PNG24");
7131 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007132
cristy3ed852e2009-09-05 21:47:34 +00007133#if defined(ZLIB_VERSION)
7134 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7135 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007136
cristy3ed852e2009-09-05 21:47:34 +00007137 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7138 {
7139 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7140 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7141 }
7142#endif
glennrp47b9dd52010-11-24 18:12:06 +00007143
cristy3ed852e2009-09-05 21:47:34 +00007144 if (*version != '\0')
7145 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007146
cristy3ed852e2009-09-05 21:47:34 +00007147#if defined(MAGICKCORE_PNG_DELEGATE)
7148 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7149 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7150#endif
glennrp47b9dd52010-11-24 18:12:06 +00007151
cristy3ed852e2009-09-05 21:47:34 +00007152 entry->magick=(IsImageFormatHandler *) IsPNG;
7153 entry->adjoin=MagickFalse;
7154 entry->description=ConstantString("opaque 24-bit RGB");
7155 entry->module=ConstantString("PNG");
7156 (void) RegisterMagickInfo(entry);
7157
7158 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007159
cristy3ed852e2009-09-05 21:47:34 +00007160#if defined(MAGICKCORE_PNG_DELEGATE)
7161 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7162 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7163#endif
glennrp47b9dd52010-11-24 18:12:06 +00007164
cristy3ed852e2009-09-05 21:47:34 +00007165 entry->magick=(IsImageFormatHandler *) IsPNG;
7166 entry->adjoin=MagickFalse;
7167 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7168 entry->module=ConstantString("PNG");
7169 (void) RegisterMagickInfo(entry);
7170
7171 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007172
cristy3ed852e2009-09-05 21:47:34 +00007173#if defined(JNG_SUPPORTED)
7174#if defined(MAGICKCORE_PNG_DELEGATE)
7175 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7176 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7177#endif
7178#endif
glennrp47b9dd52010-11-24 18:12:06 +00007179
cristy3ed852e2009-09-05 21:47:34 +00007180 entry->magick=(IsImageFormatHandler *) IsJNG;
7181 entry->adjoin=MagickFalse;
7182 entry->description=ConstantString("JPEG Network Graphics");
7183 entry->module=ConstantString("PNG");
7184 entry->note=ConstantString(JNGNote);
7185 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007186
glennrpedaa0382012-04-12 14:16:21 +00007187#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007188 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007189#endif
glennrp47b9dd52010-11-24 18:12:06 +00007190
cristy3ed852e2009-09-05 21:47:34 +00007191 return(MagickImageCoderSignature);
7192}
7193
7194/*
7195%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7196% %
7197% %
7198% %
7199% U n r e g i s t e r P N G I m a g e %
7200% %
7201% %
7202% %
7203%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7204%
7205% UnregisterPNGImage() removes format registrations made by the
7206% PNG module from the list of supported formats.
7207%
7208% The format of the UnregisterPNGImage method is:
7209%
7210% UnregisterPNGImage(void)
7211%
7212*/
7213ModuleExport void UnregisterPNGImage(void)
7214{
7215 (void) UnregisterMagickInfo("MNG");
7216 (void) UnregisterMagickInfo("PNG");
7217 (void) UnregisterMagickInfo("PNG8");
7218 (void) UnregisterMagickInfo("PNG24");
7219 (void) UnregisterMagickInfo("PNG32");
7220 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007221
glennrpedaa0382012-04-12 14:16:21 +00007222#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00007223 if (ping_semaphore != (SemaphoreInfo *) NULL)
7224 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007225#endif
7226}
7227
7228#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007229#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007230/*
7231%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7232% %
7233% %
7234% %
7235% W r i t e M N G I m a g e %
7236% %
7237% %
7238% %
7239%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7240%
7241% WriteMNGImage() writes an image in the Portable Network Graphics
7242% Group's "Multiple-image Network Graphics" encoded image format.
7243%
7244% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7245%
7246% The format of the WriteMNGImage method is:
7247%
cristy16ea1392012-03-21 20:38:41 +00007248% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7249% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00007250%
7251% A description of each parameter follows.
7252%
7253% o image_info: the image info.
7254%
7255% o image: The image.
7256%
cristy16ea1392012-03-21 20:38:41 +00007257% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +00007258%
7259% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7260% "To do" under ReadPNGImage):
7261%
cristy3ed852e2009-09-05 21:47:34 +00007262% Preserve all unknown and not-yet-handled known chunks found in input
7263% PNG file and copy them into output PNG files according to the PNG
7264% copying rules.
7265%
7266% Write the iCCP chunk at MNG level when (icc profile length > 0)
7267%
7268% Improve selection of color type (use indexed-colour or indexed-colour
7269% with tRNS when 256 or fewer unique RGBA values are present).
7270%
7271% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7272% This will be complicated if we limit ourselves to generating MNG-LC
7273% files. For now we ignore disposal method 3 and simply overlay the next
7274% image on it.
7275%
7276% Check for identical PLTE's or PLTE/tRNS combinations and use a
7277% global MNG PLTE or PLTE/tRNS combination when appropriate.
7278% [mostly done 15 June 1999 but still need to take care of tRNS]
7279%
7280% Check for identical sRGB and replace with a global sRGB (and remove
7281% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7282% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7283% local gAMA/cHRM with local sRGB if appropriate).
7284%
7285% Check for identical sBIT chunks and write global ones.
7286%
7287% Provide option to skip writing the signature tEXt chunks.
7288%
7289% Use signatures to detect identical objects and reuse the first
7290% instance of such objects instead of writing duplicate objects.
7291%
7292% Use a smaller-than-32k value of compression window size when
7293% appropriate.
7294%
7295% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7296% ancillary text chunks and save profiles.
7297%
7298% Provide an option to force LC files (to ensure exact framing rate)
7299% instead of VLC.
7300%
7301% Provide an option to force VLC files instead of LC, even when offsets
7302% are present. This will involve expanding the embedded images with a
7303% transparent region at the top and/or left.
7304*/
7305
cristy3ed852e2009-09-05 21:47:34 +00007306static void
glennrpcf002022011-01-30 02:38:15 +00007307Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007308 png_info *ping_info, unsigned char *profile_type, unsigned char
7309 *profile_description, unsigned char *profile_data, png_uint_32 length)
7310{
cristy3ed852e2009-09-05 21:47:34 +00007311 png_textp
7312 text;
7313
cristybb503372010-05-27 20:51:26 +00007314 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007315 i;
7316
7317 unsigned char
7318 *sp;
7319
7320 png_charp
7321 dp;
7322
7323 png_uint_32
7324 allocated_length,
7325 description_length;
7326
7327 unsigned char
7328 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007329
cristy3ed852e2009-09-05 21:47:34 +00007330 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7331 return;
7332
7333 if (image_info->verbose)
7334 {
glennrp0fe50b42010-11-16 03:52:51 +00007335 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7336 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007337 }
glennrp0fe50b42010-11-16 03:52:51 +00007338
cristy3ed852e2009-09-05 21:47:34 +00007339 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7340 description_length=(png_uint_32) strlen((const char *) profile_description);
7341 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7342 + description_length);
7343 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7344 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7345 text[0].key[0]='\0';
7346 (void) ConcatenateMagickString(text[0].key,
7347 "Raw profile type ",MaxTextExtent);
7348 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7349 sp=profile_data;
7350 dp=text[0].text;
7351 *dp++='\n';
7352 (void) CopyMagickString(dp,(const char *) profile_description,
7353 allocated_length);
7354 dp+=description_length;
7355 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007356 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007357 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007358 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007359
cristybb503372010-05-27 20:51:26 +00007360 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007361 {
7362 if (i%36 == 0)
7363 *dp++='\n';
7364 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7365 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7366 }
glennrp47b9dd52010-11-24 18:12:06 +00007367
cristy3ed852e2009-09-05 21:47:34 +00007368 *dp++='\n';
7369 *dp='\0';
7370 text[0].text_length=(png_size_t) (dp-text[0].text);
7371 text[0].compression=image_info->compression == NoCompression ||
7372 (image_info->compression == UndefinedCompression &&
7373 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007374
cristy3ed852e2009-09-05 21:47:34 +00007375 if (text[0].text_length <= allocated_length)
7376 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007377
cristy3ed852e2009-09-05 21:47:34 +00007378 png_free(ping,text[0].text);
7379 png_free(ping,text[0].key);
7380 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007381}
7382
glennrpcf002022011-01-30 02:38:15 +00007383static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007384 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007385{
7386 char
7387 *name;
7388
7389 const StringInfo
7390 *profile;
7391
7392 unsigned char
7393 *data;
7394
7395 png_uint_32 length;
7396
7397 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007398
7399 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7400 {
cristy3ed852e2009-09-05 21:47:34 +00007401 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007402
cristy3ed852e2009-09-05 21:47:34 +00007403 if (profile != (const StringInfo *) NULL)
7404 {
7405 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007406 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007407
glennrp47b9dd52010-11-24 18:12:06 +00007408 if (LocaleNCompare(name,string,11) == 0)
7409 {
7410 if (logging != MagickFalse)
7411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7412 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007413
glennrpcf002022011-01-30 02:38:15 +00007414 ping_profile=CloneStringInfo(profile);
7415 data=GetStringInfoDatum(ping_profile),
7416 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007417 data[4]=data[3];
7418 data[3]=data[2];
7419 data[2]=data[1];
7420 data[1]=data[0];
7421 (void) WriteBlobMSBULong(image,length-5); /* data length */
7422 (void) WriteBlob(image,length-1,data+1);
7423 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007424 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007425 }
cristy3ed852e2009-09-05 21:47:34 +00007426 }
glennrp47b9dd52010-11-24 18:12:06 +00007427
cristy3ed852e2009-09-05 21:47:34 +00007428 name=GetNextImageProfile(image);
7429 }
glennrp47b9dd52010-11-24 18:12:06 +00007430
cristy3ed852e2009-09-05 21:47:34 +00007431 return(MagickTrue);
7432}
7433
glennrpb9cfe272010-12-21 15:08:06 +00007434
cristy3ed852e2009-09-05 21:47:34 +00007435/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007436static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +00007437 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
glennrpb9cfe272010-12-21 15:08:06 +00007438{
cristy16ea1392012-03-21 20:38:41 +00007439 Image
7440 *image;
7441
7442 ImageInfo
7443 *image_info;
7444
cristy3ed852e2009-09-05 21:47:34 +00007445 char
7446 s[2];
7447
7448 const char
7449 *name,
7450 *property,
7451 *value;
7452
7453 const StringInfo
7454 *profile;
7455
cristy3ed852e2009-09-05 21:47:34 +00007456 int
cristy3ed852e2009-09-05 21:47:34 +00007457 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007458 pass;
7459
glennrpe9c26dc2010-05-30 01:56:35 +00007460 png_byte
7461 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007462
glennrp39992b42010-11-14 00:03:43 +00007463 png_color
7464 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007465
glennrp5af765f2010-03-30 11:12:18 +00007466 png_color_16
7467 ping_background,
7468 ping_trans_color;
7469
cristy3ed852e2009-09-05 21:47:34 +00007470 png_info
7471 *ping_info;
7472
7473 png_struct
7474 *ping;
7475
glennrp5af765f2010-03-30 11:12:18 +00007476 png_uint_32
7477 ping_height,
7478 ping_width;
7479
cristybb503372010-05-27 20:51:26 +00007480 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007481 y;
7482
7483 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007484 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007485 logging,
glennrp58e01762011-01-07 15:28:54 +00007486 matte,
7487
glennrpda8f3a72011-02-27 23:54:12 +00007488 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007489 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007490 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007491 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007492 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007493 ping_have_bKGD,
7494 ping_have_pHYs,
7495 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007496
7497 ping_exclude_bKGD,
7498 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007499 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007500 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007501 ping_exclude_gAMA,
7502 ping_exclude_iCCP,
7503 /* ping_exclude_iTXt, */
7504 ping_exclude_oFFs,
7505 ping_exclude_pHYs,
7506 ping_exclude_sRGB,
7507 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007508 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007509 ping_exclude_vpAg,
7510 ping_exclude_zCCP, /* hex-encoded iCCP */
7511 ping_exclude_zTXt,
7512
glennrp8d3d6e52011-04-19 04:39:51 +00007513 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007514 ping_need_colortype_warning,
7515
glennrp82b3c532011-03-22 19:20:54 +00007516 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007517 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007518 tried_333,
7519 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007520
7521 QuantumInfo
7522 *quantum_info;
7523
cristy16ea1392012-03-21 20:38:41 +00007524 PNGErrorInfo
7525 error_info;
7526
cristybb503372010-05-27 20:51:26 +00007527 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007528 i,
7529 x;
7530
7531 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007532 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007533
glennrp5af765f2010-03-30 11:12:18 +00007534 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007535 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007536 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007537 ping_color_type,
7538 ping_interlace_method,
7539 ping_compression_method,
7540 ping_filter_method,
7541 ping_num_trans;
7542
cristybb503372010-05-27 20:51:26 +00007543 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007544 image_depth,
7545 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007546
cristybb503372010-05-27 20:51:26 +00007547 size_t
cristy3ed852e2009-09-05 21:47:34 +00007548 quality,
7549 rowbytes,
7550 save_image_depth;
7551
glennrpdfd70802010-11-14 01:23:35 +00007552 int
glennrpfd05d622011-02-25 04:10:33 +00007553 j,
glennrpf09bded2011-01-08 01:15:59 +00007554 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007555 number_opaque,
7556 number_semitransparent,
7557 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007558 ping_pHYs_unit_type;
7559
7560 png_uint_32
7561 ping_pHYs_x_resolution,
7562 ping_pHYs_y_resolution;
7563
cristy3ed852e2009-09-05 21:47:34 +00007564 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007565 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007566
cristy16ea1392012-03-21 20:38:41 +00007567 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7568 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7569 if (image_info == (ImageInfo *) NULL)
7570 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007571
glennrp5af765f2010-03-30 11:12:18 +00007572 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007573 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007574 ping_color_type=0,
7575 ping_interlace_method=0,
7576 ping_compression_method=0,
7577 ping_filter_method=0,
7578 ping_num_trans = 0;
7579
7580 ping_background.red = 0;
7581 ping_background.green = 0;
7582 ping_background.blue = 0;
7583 ping_background.gray = 0;
7584 ping_background.index = 0;
7585
7586 ping_trans_color.red=0;
7587 ping_trans_color.green=0;
7588 ping_trans_color.blue=0;
7589 ping_trans_color.gray=0;
7590
glennrpdfd70802010-11-14 01:23:35 +00007591 ping_pHYs_unit_type = 0;
7592 ping_pHYs_x_resolution = 0;
7593 ping_pHYs_y_resolution = 0;
7594
glennrpda8f3a72011-02-27 23:54:12 +00007595 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007596 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007597 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007598 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007599 ping_have_bKGD=MagickFalse;
7600 ping_have_pHYs=MagickFalse;
7601 ping_have_tRNS=MagickFalse;
7602
glennrp0e8ea192010-12-24 18:00:33 +00007603 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7604 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007605 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007606 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007607 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007608 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7609 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7610 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7611 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7612 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7613 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007614 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007615 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7616 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7617 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7618
glennrp8d3d6e52011-04-19 04:39:51 +00007619 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007620 ping_need_colortype_warning = MagickFalse;
7621
cristy0d57eec2011-09-04 22:13:56 +00007622 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
7623 * i.e., eliminate the ICC profile and set image->rendering_intent.
7624 * Note that this will not involve any changes to the actual pixels
7625 * but merely passes information to applications that read the resulting
7626 * PNG image.
7627 */
7628 if (ping_exclude_sRGB == MagickFalse)
7629 {
7630 char
7631 *name;
7632
7633 const StringInfo
7634 *profile;
7635
7636 ResetImageProfileIterator(image);
7637 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7638 {
7639 profile=GetImageProfile(image,name);
7640
7641 if (profile != (StringInfo *) NULL)
7642 {
7643 if ((LocaleCompare(name,"ICC") == 0) ||
cristy16ea1392012-03-21 20:38:41 +00007644 (LocaleCompare(name,"ICM") == 0))
7645 {
glennrpee7b4c02011-10-04 01:21:09 +00007646 int
7647 icheck;
7648
7649 /* 0: not a known sRGB profile
7650 * 1: HP-Microsoft sRGB v2
7651 * 2: ICC sRGB v4 perceptual
7652 * 3: ICC sRGB v2 perceptual no black-compensation
7653 */
7654 png_uint_32
7655 check_crc[4] = {0, 0xf29e526dUL, 0xbbef7812UL, 0x427ebb21UL},
7656 check_len[4] = {0, 3144, 60960, 3052};
7657
7658 png_uint_32
7659 length,
7660 profile_crc;
7661
cristy0d57eec2011-09-04 22:13:56 +00007662 unsigned char
7663 *data;
7664
glennrp29a106e2011-09-06 17:11:42 +00007665 length=(png_uint_32) GetStringInfoLength(profile);
7666
glennrpee7b4c02011-10-04 01:21:09 +00007667 for (icheck=3; icheck > 0; icheck--)
cristy0d57eec2011-09-04 22:13:56 +00007668 {
glennrpee7b4c02011-10-04 01:21:09 +00007669 if (length == check_len[icheck])
glennrp29a106e2011-09-06 17:11:42 +00007670 {
glennrpee7b4c02011-10-04 01:21:09 +00007671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7672 " Got a %lu-byte ICC profile (potentially sRGB)",
7673 (unsigned long) length);
glennrp29a106e2011-09-06 17:11:42 +00007674
glennrpee7b4c02011-10-04 01:21:09 +00007675 data=GetStringInfoDatum(profile);
7676 profile_crc=crc32(0,data,length);
glennrp29a106e2011-09-06 17:11:42 +00007677
glennrpee7b4c02011-10-04 01:21:09 +00007678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe54cd4b2011-10-04 01:31:35 +00007679 " with crc=%8x",(unsigned int) profile_crc);
glennrpee7b4c02011-10-04 01:21:09 +00007680
7681 if (profile_crc == check_crc[icheck])
7682 {
7683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7684 " It is sRGB.");
7685 if (image->rendering_intent==UndefinedIntent)
7686 image->rendering_intent=PerceptualIntent;
7687 break;
7688 }
glennrp29a106e2011-09-06 17:11:42 +00007689 }
glennrp29a106e2011-09-06 17:11:42 +00007690 }
glennrpee7b4c02011-10-04 01:21:09 +00007691 if (icheck == 0)
glennrp29a106e2011-09-06 17:11:42 +00007692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpee7b4c02011-10-04 01:21:09 +00007693 " Got a %lu-byte ICC profile",
glennrp29a106e2011-09-06 17:11:42 +00007694 (unsigned long) length);
7695 }
cristy0d57eec2011-09-04 22:13:56 +00007696 }
7697 name=GetNextImageProfile(image);
7698 }
7699 }
7700
glennrp8bb3a022010-12-13 20:40:04 +00007701 number_opaque = 0;
7702 number_semitransparent = 0;
7703 number_transparent = 0;
7704
glennrpfd05d622011-02-25 04:10:33 +00007705 if (logging != MagickFalse)
7706 {
7707 if (image->storage_class == UndefinedClass)
7708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7709 " storage_class=UndefinedClass");
7710 if (image->storage_class == DirectClass)
7711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7712 " storage_class=DirectClass");
7713 if (image->storage_class == PseudoClass)
7714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7715 " storage_class=PseudoClass");
7716 }
glennrp28af3712011-04-06 18:07:30 +00007717
glennrp7e65e932011-08-19 02:31:16 +00007718 if (image->storage_class == PseudoClass &&
7719 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
7720 (mng_info->write_png_colortype != 0 &&
7721 mng_info->write_png_colortype != 4)))
7722 {
cristy16ea1392012-03-21 20:38:41 +00007723 (void) SyncImage(image,exception);
glennrp7e65e932011-08-19 02:31:16 +00007724 image->storage_class = DirectClass;
7725 }
7726
glennrpc6c391a2011-04-27 02:23:56 +00007727 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007728 {
glennrpc6c391a2011-04-27 02:23:56 +00007729 if (image->storage_class != PseudoClass && image->colormap != NULL)
7730 {
7731 /* Free the bogus colormap; it can cause trouble later */
7732 if (logging != MagickFalse)
7733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7734 " Freeing bogus colormap");
cristye9ac4c32011-09-26 18:47:22 +00007735 (void) RelinquishMagickMemory(image->colormap);
glennrpc6c391a2011-04-27 02:23:56 +00007736 image->colormap=NULL;
7737 }
glennrp28af3712011-04-06 18:07:30 +00007738 }
glennrpbb4f99d2011-05-22 11:13:17 +00007739
cristy510d06a2011-07-06 23:43:54 +00007740 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristy16ea1392012-03-21 20:38:41 +00007741 (void) TransformImageColorspace(image,sRGBColorspace,exception);
glennrp0fe50b42010-11-16 03:52:51 +00007742
glennrp3241bd02010-12-12 04:36:28 +00007743 /*
7744 Sometimes we get PseudoClass images whose RGB values don't match
7745 the colors in the colormap. This code syncs the RGB values.
7746 */
7747 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
cristy16ea1392012-03-21 20:38:41 +00007748 (void) SyncImage(image,exception);
glennrp3241bd02010-12-12 04:36:28 +00007749
glennrpa6a06632011-01-19 15:15:34 +00007750#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7751 if (image->depth > 8)
7752 {
7753 if (logging != MagickFalse)
7754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7755 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7756
7757 image->depth=8;
7758 }
7759#endif
7760
glennrp8e58efd2011-05-20 12:16:29 +00007761 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007762 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7763 {
cristy16ea1392012-03-21 20:38:41 +00007764 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007765 *r;
7766
glennrp8e58efd2011-05-20 12:16:29 +00007767 if (image->depth > 8)
7768 {
7769#if MAGICKCORE_QUANTUM_DEPTH > 16
7770 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007771 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007772
7773 for (y=0; y < (ssize_t) image->rows; y++)
7774 {
cristy16ea1392012-03-21 20:38:41 +00007775 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007776
cristy16ea1392012-03-21 20:38:41 +00007777 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007778 break;
7779
7780 for (x=0; x < (ssize_t) image->columns; x++)
7781 {
cristy16ea1392012-03-21 20:38:41 +00007782 LBR16PixelRGBA(r);
7783 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007784 }
glennrpbb4f99d2011-05-22 11:13:17 +00007785
glennrp8e58efd2011-05-20 12:16:29 +00007786 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7787 break;
7788 }
7789
7790 if (image->storage_class == PseudoClass && image->colormap != NULL)
7791 {
cristy3e08f112011-05-24 13:19:30 +00007792 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007793 {
glennrp91d99252011-06-25 14:30:13 +00007794 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007795 }
7796 }
7797#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7798 }
7799
7800 else if (image->depth > 4)
7801 {
7802#if MAGICKCORE_QUANTUM_DEPTH > 8
7803 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007804 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007805
7806 for (y=0; y < (ssize_t) image->rows; y++)
7807 {
cristy16ea1392012-03-21 20:38:41 +00007808 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007809
cristy16ea1392012-03-21 20:38:41 +00007810 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007811 break;
7812
7813 for (x=0; x < (ssize_t) image->columns; x++)
7814 {
cristy16ea1392012-03-21 20:38:41 +00007815 LBR08PixelRGBA(r);
7816 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007817 }
glennrpbb4f99d2011-05-22 11:13:17 +00007818
glennrp8e58efd2011-05-20 12:16:29 +00007819 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7820 break;
7821 }
7822
7823 if (image->storage_class == PseudoClass && image->colormap != NULL)
7824 {
cristy3e08f112011-05-24 13:19:30 +00007825 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007826 {
glennrp91d99252011-06-25 14:30:13 +00007827 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007828 }
7829 }
7830#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7831 }
7832 else
7833 if (image->depth > 2)
7834 {
7835 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007836 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007837
7838 for (y=0; y < (ssize_t) image->rows; y++)
7839 {
cristy16ea1392012-03-21 20:38:41 +00007840 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007841
cristy16ea1392012-03-21 20:38:41 +00007842 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007843 break;
7844
7845 for (x=0; x < (ssize_t) image->columns; x++)
7846 {
cristy16ea1392012-03-21 20:38:41 +00007847 LBR04PixelRGBA(r);
7848 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007849 }
glennrpbb4f99d2011-05-22 11:13:17 +00007850
glennrp8e58efd2011-05-20 12:16:29 +00007851 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7852 break;
7853 }
7854
7855 if (image->storage_class == PseudoClass && image->colormap != NULL)
7856 {
cristy3e08f112011-05-24 13:19:30 +00007857 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007858 {
glennrp91d99252011-06-25 14:30:13 +00007859 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007860 }
7861 }
7862 }
7863
7864 else if (image->depth > 1)
7865 {
7866 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007867 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007868
7869 for (y=0; y < (ssize_t) image->rows; y++)
7870 {
cristy16ea1392012-03-21 20:38:41 +00007871 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007872
cristy16ea1392012-03-21 20:38:41 +00007873 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007874 break;
7875
7876 for (x=0; x < (ssize_t) image->columns; x++)
7877 {
cristy16ea1392012-03-21 20:38:41 +00007878 LBR02PixelRGBA(r);
7879 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007880 }
glennrpbb4f99d2011-05-22 11:13:17 +00007881
glennrp8e58efd2011-05-20 12:16:29 +00007882 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7883 break;
7884 }
7885
7886 if (image->storage_class == PseudoClass && image->colormap != NULL)
7887 {
cristy3e08f112011-05-24 13:19:30 +00007888 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007889 {
glennrp91d99252011-06-25 14:30:13 +00007890 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007891 }
7892 }
7893 }
7894 else
7895 {
7896 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007897 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007898
7899 for (y=0; y < (ssize_t) image->rows; y++)
7900 {
cristy16ea1392012-03-21 20:38:41 +00007901 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp8e58efd2011-05-20 12:16:29 +00007902
cristy16ea1392012-03-21 20:38:41 +00007903 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007904 break;
7905
7906 for (x=0; x < (ssize_t) image->columns; x++)
7907 {
cristy16ea1392012-03-21 20:38:41 +00007908 LBR01PixelRGBA(r);
7909 r+=GetPixelChannels(image);
glennrp8e58efd2011-05-20 12:16:29 +00007910 }
glennrpbb4f99d2011-05-22 11:13:17 +00007911
glennrp8e58efd2011-05-20 12:16:29 +00007912 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7913 break;
7914 }
7915
7916 if (image->storage_class == PseudoClass && image->colormap != NULL)
7917 {
cristy3e08f112011-05-24 13:19:30 +00007918 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007919 {
glennrp91d99252011-06-25 14:30:13 +00007920 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007921 }
7922 }
7923 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007924 }
7925
glennrp67b9c1a2011-04-22 18:47:36 +00007926 /* To do: set to next higher multiple of 8 */
7927 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007928 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007929
glennrp2b013e42010-11-24 16:55:50 +00007930#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7931 /* PNG does not handle depths greater than 16 so reduce it even
7932 * if lossy
7933 */
glennrp8e58efd2011-05-20 12:16:29 +00007934 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007935 image->depth=16;
7936#endif
7937
glennrp3faa9a32011-04-23 14:00:25 +00007938#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpcc5d45b2012-01-06 04:06:10 +00007939 if (image->depth > 8)
7940 {
7941 /* To do: fill low byte properly */
7942 image->depth=16;
7943 }
7944
glennrpc722dd82011-02-24 05:13:21 +00007945 if (image->depth == 16 && mng_info->write_png_depth != 16)
cristy16ea1392012-03-21 20:38:41 +00007946 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007947 image->depth = 8;
7948#endif
7949
glennrpc8c2f062011-02-25 19:00:33 +00007950 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007951 * we reduce the transparency to binary and run again, then if there
7952 * 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 +00007953 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7954 * palette. Then (To do) we take care of a final reduction that is only
7955 * needed if there are still 256 colors present and one of them has both
7956 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007957 */
glennrp82b3c532011-03-22 19:20:54 +00007958
glennrp8ca51ad2011-05-12 21:22:32 +00007959 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007960 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007961 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007962
glennrp8ca51ad2011-05-12 21:22:32 +00007963 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007964 {
7965 /* BUILD_PALETTE
7966 *
7967 * Sometimes we get DirectClass images that have 256 colors or fewer.
7968 * This code will build a colormap.
7969 *
7970 * Also, sometimes we get PseudoClass images with an out-of-date
7971 * colormap. This code will replace the colormap with a new one.
7972 * Sometimes we get PseudoClass images that have more than 256 colors.
7973 * This code will delete the colormap and change the image to
7974 * DirectClass.
7975 *
cristy16ea1392012-03-21 20:38:41 +00007976 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00007977 * even though it sometimes contains left-over non-opaque values.
7978 *
7979 * Also we gather some information (number of opaque, transparent,
7980 * and semitransparent pixels, and whether the image has any non-gray
7981 * pixels or only black-and-white pixels) that we might need later.
7982 *
7983 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7984 * we need to check for bogus non-opaque values, at least.
7985 */
glennrp3c218112010-11-27 15:31:26 +00007986
glennrpd71e86a2011-02-24 01:28:37 +00007987 int
7988 n;
glennrp3c218112010-11-27 15:31:26 +00007989
cristy16ea1392012-03-21 20:38:41 +00007990 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00007991 opaque[260],
7992 semitransparent[260],
7993 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007994
cristy16ea1392012-03-21 20:38:41 +00007995 register const Quantum
7996 *s;
glennrp8bb3a022010-12-13 20:40:04 +00007997
cristy16ea1392012-03-21 20:38:41 +00007998 register Quantum
7999 *q,
glennrpfd05d622011-02-25 04:10:33 +00008000 *r;
8001
glennrpd71e86a2011-02-24 01:28:37 +00008002 if (logging != MagickFalse)
8003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8004 " Enter BUILD_PALETTE:");
8005
8006 if (logging != MagickFalse)
8007 {
glennrp03812ae2010-12-24 01:31:34 +00008008 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008009 " image->columns=%.20g",(double) image->columns);
8010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8011 " image->rows=%.20g",(double) image->rows);
8012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8013 " image->matte=%.20g",(double) image->matte);
8014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8015 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00008016
glennrpfd05d622011-02-25 04:10:33 +00008017 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00008018 {
8019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00008020 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00008021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008022 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00008023
glennrpd71e86a2011-02-24 01:28:37 +00008024 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00008025 {
glennrpd71e86a2011-02-24 01:28:37 +00008026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8027 " %d (%d,%d,%d,%d)",
8028 (int) i,
8029 (int) image->colormap[i].red,
8030 (int) image->colormap[i].green,
8031 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008032 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00008033 }
glennrp2cc891a2010-12-24 13:44:32 +00008034
glennrpd71e86a2011-02-24 01:28:37 +00008035 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8036 {
8037 if (i > 255)
8038 {
8039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8040 " %d (%d,%d,%d,%d)",
8041 (int) i,
8042 (int) image->colormap[i].red,
8043 (int) image->colormap[i].green,
8044 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008045 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008046 }
8047 }
glennrp03812ae2010-12-24 01:31:34 +00008048 }
glennrp7ddcc222010-12-11 05:01:05 +00008049
glennrpd71e86a2011-02-24 01:28:37 +00008050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8051 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00008052
glennrpd71e86a2011-02-24 01:28:37 +00008053 if (image->colors == 0)
cristy16ea1392012-03-21 20:38:41 +00008054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8055 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00008056
glennrp8d3d6e52011-04-19 04:39:51 +00008057 if (ping_preserve_colormap == MagickFalse)
8058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8059 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00008060 }
8061
glennrpd71e86a2011-02-24 01:28:37 +00008062 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00008063 number_opaque = 0;
8064 number_semitransparent = 0;
8065 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00008066
8067 for (y=0; y < (ssize_t) image->rows; y++)
8068 {
8069 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8070
cristy16ea1392012-03-21 20:38:41 +00008071 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008072 break;
8073
8074 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00008075 {
glennrp4737d522011-04-29 03:33:42 +00008076 if (image->matte == MagickFalse ||
cristy16ea1392012-03-21 20:38:41 +00008077 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008078 {
8079 if (number_opaque < 259)
8080 {
8081 if (number_opaque == 0)
8082 {
cristy16ea1392012-03-21 20:38:41 +00008083 GetPixelInfoPixel(image, q, opaque);
8084 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008085 number_opaque=1;
8086 }
glennrp2cc891a2010-12-24 13:44:32 +00008087
glennrpd71e86a2011-02-24 01:28:37 +00008088 for (i=0; i< (ssize_t) number_opaque; i++)
8089 {
cristy16ea1392012-03-21 20:38:41 +00008090 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00008091 break;
8092 }
glennrp7ddcc222010-12-11 05:01:05 +00008093
cristy16ea1392012-03-21 20:38:41 +00008094 if (i == (ssize_t) number_opaque && number_opaque < 259)
glennrpd71e86a2011-02-24 01:28:37 +00008095 {
8096 number_opaque++;
cristy16ea1392012-03-21 20:38:41 +00008097 GetPixelInfoPixel(image, q, opaque+i);
8098 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00008099 }
8100 }
8101 }
cristy16ea1392012-03-21 20:38:41 +00008102 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00008103 {
8104 if (number_transparent < 259)
8105 {
8106 if (number_transparent == 0)
8107 {
cristy16ea1392012-03-21 20:38:41 +00008108 GetPixelInfoPixel(image, q, transparent);
8109 ping_trans_color.red=(unsigned short)
8110 GetPixelRed(image,q);
8111 ping_trans_color.green=(unsigned short)
8112 GetPixelGreen(image,q);
8113 ping_trans_color.blue=(unsigned short)
8114 GetPixelBlue(image,q);
8115 ping_trans_color.gray=(unsigned short)
8116 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00008117 number_transparent = 1;
8118 }
8119
8120 for (i=0; i< (ssize_t) number_transparent; i++)
8121 {
cristy16ea1392012-03-21 20:38:41 +00008122 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00008123 break;
8124 }
8125
8126 if (i == (ssize_t) number_transparent &&
8127 number_transparent < 259)
8128 {
8129 number_transparent++;
cristy16ea1392012-03-21 20:38:41 +00008130 GetPixelInfoPixel(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008131 }
8132 }
8133 }
8134 else
8135 {
8136 if (number_semitransparent < 259)
8137 {
8138 if (number_semitransparent == 0)
8139 {
cristy16ea1392012-03-21 20:38:41 +00008140 GetPixelInfoPixel(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008141 number_semitransparent = 1;
8142 }
8143
8144 for (i=0; i< (ssize_t) number_semitransparent; i++)
8145 {
cristy16ea1392012-03-21 20:38:41 +00008146 if (IsPixelEquivalent(image,q, semitransparent+i)
8147 && GetPixelAlpha(image,q) ==
8148 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008149 break;
8150 }
8151
8152 if (i == (ssize_t) number_semitransparent &&
8153 number_semitransparent < 259)
8154 {
8155 number_semitransparent++;
cristy16ea1392012-03-21 20:38:41 +00008156 GetPixelInfoPixel(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008157 }
8158 }
8159 }
cristy16ea1392012-03-21 20:38:41 +00008160 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008161 }
8162 }
8163
cristy4054bfb2011-08-29 23:41:39 +00008164 if (mng_info->write_png8 == MagickFalse &&
8165 ping_exclude_bKGD == MagickFalse)
glennrpd71e86a2011-02-24 01:28:37 +00008166 {
8167 /* Add the background color to the palette, if it
8168 * isn't already there.
8169 */
glennrpc6c391a2011-04-27 02:23:56 +00008170 if (logging != MagickFalse)
8171 {
8172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8173 " Check colormap for background (%d,%d,%d)",
8174 (int) image->background_color.red,
8175 (int) image->background_color.green,
8176 (int) image->background_color.blue);
8177 }
glennrpd71e86a2011-02-24 01:28:37 +00008178 for (i=0; i<number_opaque; i++)
8179 {
glennrpca7ad3a2011-04-26 04:44:54 +00008180 if (opaque[i].red == image->background_color.red &&
8181 opaque[i].green == image->background_color.green &&
8182 opaque[i].blue == image->background_color.blue)
8183 break;
glennrpd71e86a2011-02-24 01:28:37 +00008184 }
glennrpd71e86a2011-02-24 01:28:37 +00008185 if (number_opaque < 259 && i == number_opaque)
8186 {
glennrp8e045c82011-04-27 16:40:27 +00008187 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008188 ping_background.index = i;
8189 if (logging != MagickFalse)
8190 {
8191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192 " background_color index is %d",(int) i);
8193 }
8194
glennrpd71e86a2011-02-24 01:28:37 +00008195 }
glennrpa080bc32011-03-11 18:03:44 +00008196 else if (logging != MagickFalse)
8197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8198 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008199 }
8200
8201 image_colors=number_opaque+number_transparent+number_semitransparent;
8202
glennrpa080bc32011-03-11 18:03:44 +00008203 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8204 {
8205 /* No room for the background color; remove it. */
8206 number_opaque--;
8207 image_colors--;
8208 }
8209
glennrpd71e86a2011-02-24 01:28:37 +00008210 if (logging != MagickFalse)
8211 {
8212 if (image_colors > 256)
8213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8214 " image has more than 256 colors");
8215
8216 else
8217 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8218 " image has %d colors",image_colors);
8219 }
8220
glennrp8d3d6e52011-04-19 04:39:51 +00008221 if (ping_preserve_colormap != MagickFalse)
8222 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008223
glennrpfd05d622011-02-25 04:10:33 +00008224 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008225 {
8226 ping_have_color=MagickFalse;
8227 ping_have_non_bw=MagickFalse;
8228
8229 if(image_colors > 256)
8230 {
8231 for (y=0; y < (ssize_t) image->rows; y++)
8232 {
8233 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8234
cristy16ea1392012-03-21 20:38:41 +00008235 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008236 break;
8237
glennrpe5e6b802011-07-20 14:44:40 +00008238 s=q;
8239 for (x=0; x < (ssize_t) image->columns; x++)
8240 {
cristy16ea1392012-03-21 20:38:41 +00008241 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8242 GetPixelRed(image,s) != GetPixelBlue(image,s))
glennrpe5e6b802011-07-20 14:44:40 +00008243 {
8244 ping_have_color=MagickTrue;
8245 ping_have_non_bw=MagickTrue;
8246 break;
8247 }
cristy16ea1392012-03-21 20:38:41 +00008248 s+=GetPixelChannels(image);
glennrpe5e6b802011-07-20 14:44:40 +00008249 }
8250
8251 if (ping_have_color != MagickFalse)
8252 break;
8253
glennrpd71e86a2011-02-24 01:28:37 +00008254 /* Worst case is black-and-white; we are looking at every
8255 * pixel twice.
8256 */
8257
glennrpd71e86a2011-02-24 01:28:37 +00008258 if (ping_have_non_bw == MagickFalse)
8259 {
8260 s=q;
8261 for (x=0; x < (ssize_t) image->columns; x++)
8262 {
cristy16ea1392012-03-21 20:38:41 +00008263 if (GetPixelRed(image,s) != 0 &&
8264 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008265 {
8266 ping_have_non_bw=MagickTrue;
glennrpe5e6b802011-07-20 14:44:40 +00008267 break;
glennrpd71e86a2011-02-24 01:28:37 +00008268 }
cristy16ea1392012-03-21 20:38:41 +00008269 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008270 }
glennrpe5e6b802011-07-20 14:44:40 +00008271 }
glennrpd71e86a2011-02-24 01:28:37 +00008272 }
glennrpbb4f99d2011-05-22 11:13:17 +00008273 }
8274 }
glennrpd71e86a2011-02-24 01:28:37 +00008275
8276 if (image_colors < 257)
8277 {
cristy16ea1392012-03-21 20:38:41 +00008278 PixelInfo
glennrpd71e86a2011-02-24 01:28:37 +00008279 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008280
glennrpd71e86a2011-02-24 01:28:37 +00008281 /*
8282 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008283 */
8284
glennrpd71e86a2011-02-24 01:28:37 +00008285 if (logging != MagickFalse)
8286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8287 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008288
glennrpd71e86a2011-02-24 01:28:37 +00008289 /* Sort palette, transparent first */;
8290
8291 n = 0;
8292
8293 for (i=0; i<number_transparent; i++)
8294 colormap[n++] = transparent[i];
8295
8296 for (i=0; i<number_semitransparent; i++)
8297 colormap[n++] = semitransparent[i];
8298
8299 for (i=0; i<number_opaque; i++)
8300 colormap[n++] = opaque[i];
8301
glennrpc6c391a2011-04-27 02:23:56 +00008302 ping_background.index +=
8303 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008304
glennrpd71e86a2011-02-24 01:28:37 +00008305 /* image_colors < 257; search the colormap instead of the pixels
8306 * to get ping_have_color and ping_have_non_bw
8307 */
8308 for (i=0; i<n; i++)
8309 {
8310 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008311 {
glennrpd71e86a2011-02-24 01:28:37 +00008312 if (colormap[i].red != colormap[i].green ||
8313 colormap[i].red != colormap[i].blue)
8314 {
8315 ping_have_color=MagickTrue;
8316 ping_have_non_bw=MagickTrue;
8317 break;
8318 }
8319 }
8320
8321 if (ping_have_non_bw == MagickFalse)
8322 {
8323 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008324 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008325 }
glennrp8bb3a022010-12-13 20:40:04 +00008326 }
8327
glennrpd71e86a2011-02-24 01:28:37 +00008328 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8329 (number_transparent == 0 && number_semitransparent == 0)) &&
8330 (((mng_info->write_png_colortype-1) ==
8331 PNG_COLOR_TYPE_PALETTE) ||
8332 (mng_info->write_png_colortype == 0)))
8333 {
glennrp6185c532011-01-14 17:58:40 +00008334 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008335 {
glennrpd71e86a2011-02-24 01:28:37 +00008336 if (n != (ssize_t) image_colors)
8337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8338 " image_colors (%d) and n (%d) don't match",
8339 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008340
glennrpd71e86a2011-02-24 01:28:37 +00008341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8342 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008343 }
glennrp03812ae2010-12-24 01:31:34 +00008344
glennrpd71e86a2011-02-24 01:28:37 +00008345 image->colors = image_colors;
8346
cristy16ea1392012-03-21 20:38:41 +00008347 if (AcquireImageColormap(image,image_colors,exception) ==
glennrpd71e86a2011-02-24 01:28:37 +00008348 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008349 ThrowWriterException(ResourceLimitError,
8350 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008351
8352 for (i=0; i< (ssize_t) image_colors; i++)
8353 image->colormap[i] = colormap[i];
8354
8355 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008356 {
glennrpd71e86a2011-02-24 01:28:37 +00008357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8358 " image->colors=%d (%d)",
8359 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008360
glennrpd71e86a2011-02-24 01:28:37 +00008361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8362 " Update the pixel indexes");
8363 }
glennrp6185c532011-01-14 17:58:40 +00008364
glennrpfd05d622011-02-25 04:10:33 +00008365 /* Sync the pixel indices with the new colormap */
8366
glennrpd71e86a2011-02-24 01:28:37 +00008367 for (y=0; y < (ssize_t) image->rows; y++)
8368 {
cristy16ea1392012-03-21 20:38:41 +00008369 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp6185c532011-01-14 17:58:40 +00008370
cristy16ea1392012-03-21 20:38:41 +00008371 if (q == (Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008372 break;
glennrp6185c532011-01-14 17:58:40 +00008373
glennrpd71e86a2011-02-24 01:28:37 +00008374 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008375 {
glennrpd71e86a2011-02-24 01:28:37 +00008376 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008377 {
glennrpd71e86a2011-02-24 01:28:37 +00008378 if ((image->matte == MagickFalse ||
cristy16ea1392012-03-21 20:38:41 +00008379 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8380 image->colormap[i].red == GetPixelRed(image,q) &&
8381 image->colormap[i].green == GetPixelGreen(image,q) &&
8382 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008383 {
cristy16ea1392012-03-21 20:38:41 +00008384 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008385 break;
glennrp6185c532011-01-14 17:58:40 +00008386 }
glennrp6185c532011-01-14 17:58:40 +00008387 }
cristy16ea1392012-03-21 20:38:41 +00008388 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008389 }
glennrp6185c532011-01-14 17:58:40 +00008390
glennrpd71e86a2011-02-24 01:28:37 +00008391 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8392 break;
8393 }
8394 }
8395 }
8396
8397 if (logging != MagickFalse)
8398 {
8399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8400 " image->colors=%d", (int) image->colors);
8401
8402 if (image->colormap != NULL)
8403 {
8404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00008405 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008406
8407 for (i=0; i < (ssize_t) image->colors; i++)
8408 {
cristy72988482011-03-29 16:34:38 +00008409 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008410 {
8411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8412 " %d (%d,%d,%d,%d)",
8413 (int) i,
8414 (int) image->colormap[i].red,
8415 (int) image->colormap[i].green,
8416 (int) image->colormap[i].blue,
cristy16ea1392012-03-21 20:38:41 +00008417 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008418 }
glennrp6185c532011-01-14 17:58:40 +00008419 }
8420 }
glennrp03812ae2010-12-24 01:31:34 +00008421
glennrpd71e86a2011-02-24 01:28:37 +00008422 if (number_transparent < 257)
8423 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8424 " number_transparent = %d",
8425 number_transparent);
8426 else
glennrp03812ae2010-12-24 01:31:34 +00008427
glennrpd71e86a2011-02-24 01:28:37 +00008428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8429 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008430
glennrpd71e86a2011-02-24 01:28:37 +00008431 if (number_opaque < 257)
8432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8433 " number_opaque = %d",
8434 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008435
glennrpd71e86a2011-02-24 01:28:37 +00008436 else
8437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8438 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008439
glennrpd71e86a2011-02-24 01:28:37 +00008440 if (number_semitransparent < 257)
8441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8442 " number_semitransparent = %d",
8443 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008444
glennrpd71e86a2011-02-24 01:28:37 +00008445 else
8446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008448
glennrpd71e86a2011-02-24 01:28:37 +00008449 if (ping_have_non_bw == MagickFalse)
8450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8451 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008452
glennrpd71e86a2011-02-24 01:28:37 +00008453 else if (ping_have_color == MagickFalse)
8454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8455 " All pixels and the background are gray");
8456
8457 else
8458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8459 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008460
glennrp03812ae2010-12-24 01:31:34 +00008461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8462 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008463 }
glennrpfd05d622011-02-25 04:10:33 +00008464
glennrpc8c2f062011-02-25 19:00:33 +00008465 if (mng_info->write_png8 == MagickFalse)
8466 break;
glennrpfd05d622011-02-25 04:10:33 +00008467
glennrpc8c2f062011-02-25 19:00:33 +00008468 /* Make any reductions necessary for the PNG8 format */
8469 if (image_colors <= 256 &&
8470 image_colors != 0 && image->colormap != NULL &&
8471 number_semitransparent == 0 &&
8472 number_transparent <= 1)
8473 break;
8474
8475 /* PNG8 can't have semitransparent colors so we threshold the
glennrp130fc452011-08-20 03:43:18 +00008476 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8477 * transparent color so if more than one is transparent we merge
8478 * them into image->background_color.
glennrpc8c2f062011-02-25 19:00:33 +00008479 */
glennrp130fc452011-08-20 03:43:18 +00008480 if (number_semitransparent != 0 || number_transparent > 1)
glennrpc8c2f062011-02-25 19:00:33 +00008481 {
8482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8483 " Thresholding the alpha channel to binary");
8484
8485 for (y=0; y < (ssize_t) image->rows; y++)
8486 {
cristy16ea1392012-03-21 20:38:41 +00008487 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpc8c2f062011-02-25 19:00:33 +00008488
cristy16ea1392012-03-21 20:38:41 +00008489 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008490 break;
8491
8492 for (x=0; x < (ssize_t) image->columns; x++)
8493 {
cristy16ea1392012-03-21 20:38:41 +00008494 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008495 {
cristy16ea1392012-03-21 20:38:41 +00008496 SetPixelInfoPixel(image,&image->background_color,r);
8497 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008498 }
8499 else
cristy16ea1392012-03-21 20:38:41 +00008500 SetPixelAlpha(image,OpaqueAlpha,r);
8501 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008502 }
glennrpbb4f99d2011-05-22 11:13:17 +00008503
glennrpc8c2f062011-02-25 19:00:33 +00008504 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8505 break;
8506
8507 if (image_colors != 0 && image_colors <= 256 &&
8508 image->colormap != NULL)
8509 for (i=0; i<image_colors; i++)
cristy16ea1392012-03-21 20:38:41 +00008510 image->colormap[i].alpha =
8511 (image->colormap[i].alpha > TransparentAlpha/2 ?
8512 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008513 }
8514 continue;
8515 }
8516
8517 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008518 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8519 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8520 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008521 */
glennrpd3371642011-03-22 19:42:23 +00008522 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8523 {
8524 if (logging != MagickFalse)
8525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8526 " Quantizing the background color to 4-4-4");
8527
8528 tried_444 = MagickTrue;
8529
glennrp91d99252011-06-25 14:30:13 +00008530 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008531
8532 if (logging != MagickFalse)
8533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8534 " Quantizing the pixel colors to 4-4-4");
8535
8536 if (image->colormap == NULL)
8537 {
8538 for (y=0; y < (ssize_t) image->rows; y++)
8539 {
cristy16ea1392012-03-21 20:38:41 +00008540 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpd3371642011-03-22 19:42:23 +00008541
cristy16ea1392012-03-21 20:38:41 +00008542 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008543 break;
8544
8545 for (x=0; x < (ssize_t) image->columns; x++)
8546 {
cristy16ea1392012-03-21 20:38:41 +00008547 if (GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp54cf7972011-08-06 14:28:09 +00008548 LBR04PixelRGB(r);
cristy16ea1392012-03-21 20:38:41 +00008549 r+=GetPixelChannels(image);
glennrpd3371642011-03-22 19:42:23 +00008550 }
glennrpbb4f99d2011-05-22 11:13:17 +00008551
glennrpd3371642011-03-22 19:42:23 +00008552 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8553 break;
8554 }
8555 }
8556
8557 else /* Should not reach this; colormap already exists and
8558 must be <= 256 */
8559 {
8560 if (logging != MagickFalse)
8561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8562 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008563
glennrpd3371642011-03-22 19:42:23 +00008564 for (i=0; i<image_colors; i++)
8565 {
glennrp91d99252011-06-25 14:30:13 +00008566 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008567 }
8568 }
8569 continue;
8570 }
8571
glennrp82b3c532011-03-22 19:20:54 +00008572 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8573 {
8574 if (logging != MagickFalse)
8575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8576 " Quantizing the background color to 3-3-3");
8577
8578 tried_333 = MagickTrue;
8579
glennrp91d99252011-06-25 14:30:13 +00008580 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008581
8582 if (logging != MagickFalse)
8583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008584 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008585
8586 if (image->colormap == NULL)
8587 {
8588 for (y=0; y < (ssize_t) image->rows; y++)
8589 {
cristy16ea1392012-03-21 20:38:41 +00008590 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp82b3c532011-03-22 19:20:54 +00008591
cristy16ea1392012-03-21 20:38:41 +00008592 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008593 break;
8594
8595 for (x=0; x < (ssize_t) image->columns; x++)
8596 {
cristy16ea1392012-03-21 20:38:41 +00008597 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8598 LBR03RGB(r);
8599 r+=GetPixelChannels(image);
glennrp82b3c532011-03-22 19:20:54 +00008600 }
glennrpbb4f99d2011-05-22 11:13:17 +00008601
glennrp82b3c532011-03-22 19:20:54 +00008602 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8603 break;
8604 }
8605 }
8606
8607 else /* Should not reach this; colormap already exists and
8608 must be <= 256 */
8609 {
8610 if (logging != MagickFalse)
8611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008612 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008613 for (i=0; i<image_colors; i++)
8614 {
glennrp91d99252011-06-25 14:30:13 +00008615 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008616 }
glennrpd3371642011-03-22 19:42:23 +00008617 }
8618 continue;
glennrp82b3c532011-03-22 19:20:54 +00008619 }
glennrpc8c2f062011-02-25 19:00:33 +00008620
glennrp8ca51ad2011-05-12 21:22:32 +00008621 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008622 {
8623 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008625 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008626
glennrp8ca51ad2011-05-12 21:22:32 +00008627 tried_332 = MagickTrue;
8628
glennrp3faa9a32011-04-23 14:00:25 +00008629 /* Red and green were already done so we only quantize the blue
8630 * channel
8631 */
8632
glennrp91d99252011-06-25 14:30:13 +00008633 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008634
glennrpc8c2f062011-02-25 19:00:33 +00008635 if (logging != MagickFalse)
8636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008637 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008638
glennrpc8c2f062011-02-25 19:00:33 +00008639 if (image->colormap == NULL)
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)
glennrp54cf7972011-08-06 14:28:09 +00008651 LBR02PixelBlue(r);
cristy16ea1392012-03-21 20:38:41 +00008652 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008653 }
glennrpbb4f99d2011-05-22 11:13:17 +00008654
glennrpc8c2f062011-02-25 19:00:33 +00008655 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8656 break;
8657 }
8658 }
glennrpfd05d622011-02-25 04:10:33 +00008659
glennrpc8c2f062011-02-25 19:00:33 +00008660 else /* Should not reach this; colormap already exists and
8661 must be <= 256 */
8662 {
8663 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008665 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008666 for (i=0; i<image_colors; i++)
8667 {
glennrp91d99252011-06-25 14:30:13 +00008668 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008669 }
8670 }
8671 continue;
8672 }
8673 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008674
8675 if (image_colors == 0 || image_colors > 256)
8676 {
8677 /* Take care of special case with 256 colors + 1 transparent
8678 * color. We don't need to quantize to 2-3-2-1; we only need to
8679 * eliminate one color, so we'll merge the two darkest red
8680 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8681 */
8682 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8683 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8684 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8685 {
8686 image->background_color.red=ScaleCharToQuantum(0x24);
8687 }
glennrpbb4f99d2011-05-22 11:13:17 +00008688
glennrp8ca51ad2011-05-12 21:22:32 +00008689 if (image->colormap == NULL)
8690 {
8691 for (y=0; y < (ssize_t) image->rows; y++)
8692 {
cristy16ea1392012-03-21 20:38:41 +00008693 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008694
cristy16ea1392012-03-21 20:38:41 +00008695 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008696 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008697
glennrp8ca51ad2011-05-12 21:22:32 +00008698 for (x=0; x < (ssize_t) image->columns; x++)
8699 {
cristy16ea1392012-03-21 20:38:41 +00008700 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8701 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8702 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8703 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008704 {
cristy16ea1392012-03-21 20:38:41 +00008705 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008706 }
cristy16ea1392012-03-21 20:38:41 +00008707 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008708 }
glennrpbb4f99d2011-05-22 11:13:17 +00008709
glennrp8ca51ad2011-05-12 21:22:32 +00008710 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8711 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008712
glennrp8ca51ad2011-05-12 21:22:32 +00008713 }
8714 }
8715
8716 else
8717 {
8718 for (i=0; i<image_colors; i++)
8719 {
8720 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8721 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8722 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8723 {
8724 image->colormap[i].red=ScaleCharToQuantum(0x24);
8725 }
8726 }
8727 }
8728 }
glennrpd71e86a2011-02-24 01:28:37 +00008729 }
glennrpfd05d622011-02-25 04:10:33 +00008730 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008731
glennrpfd05d622011-02-25 04:10:33 +00008732 /* If we are excluding the tRNS chunk and there is transparency,
8733 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8734 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008735 */
glennrp0e8ea192010-12-24 18:00:33 +00008736 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8737 (number_transparent != 0 || number_semitransparent != 0))
8738 {
glennrpd17915c2011-04-29 14:24:22 +00008739 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008740
8741 if (ping_have_color == MagickFalse)
8742 mng_info->write_png_colortype = 5;
8743
8744 else
8745 mng_info->write_png_colortype = 7;
8746
glennrp8d579662011-02-23 02:05:02 +00008747 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008748 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008749 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008750
glennrp0e8ea192010-12-24 18:00:33 +00008751 }
8752
glennrpfd05d622011-02-25 04:10:33 +00008753 /* See if cheap transparency is possible. It is only possible
8754 * when there is a single transparent color, no semitransparent
8755 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008756 * as the transparent color. We only need this information if
8757 * we are writing a PNG with colortype 0 or 2, and we have not
8758 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008759 */
glennrp5a39f372011-02-25 04:52:16 +00008760 if (number_transparent == 1 &&
8761 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008762 {
8763 ping_have_cheap_transparency = MagickTrue;
8764
8765 if (number_semitransparent != 0)
8766 ping_have_cheap_transparency = MagickFalse;
8767
8768 else if (image_colors == 0 || image_colors > 256 ||
8769 image->colormap == NULL)
8770 {
cristy16ea1392012-03-21 20:38:41 +00008771 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008772 *q;
8773
glennrpfd05d622011-02-25 04:10:33 +00008774 for (y=0; y < (ssize_t) image->rows; y++)
8775 {
8776 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8777
cristy16ea1392012-03-21 20:38:41 +00008778 if (q == (Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008779 break;
8780
8781 for (x=0; x < (ssize_t) image->columns; x++)
8782 {
cristy16ea1392012-03-21 20:38:41 +00008783 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8784 (unsigned short) GetPixelRed(image,q) ==
8785 ping_trans_color.red &&
8786 (unsigned short) GetPixelGreen(image,q) ==
8787 ping_trans_color.green &&
8788 (unsigned short) GetPixelBlue(image,q) ==
8789 ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008790 {
8791 ping_have_cheap_transparency = MagickFalse;
8792 break;
8793 }
8794
cristy16ea1392012-03-21 20:38:41 +00008795 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008796 }
glennrpbb4f99d2011-05-22 11:13:17 +00008797
glennrpfd05d622011-02-25 04:10:33 +00008798 if (ping_have_cheap_transparency == MagickFalse)
8799 break;
8800 }
8801 }
8802 else
8803 {
glennrp67b9c1a2011-04-22 18:47:36 +00008804 /* Assuming that image->colormap[0] is the one transparent color
8805 * and that all others are opaque.
8806 */
glennrpfd05d622011-02-25 04:10:33 +00008807 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008808 for (i=1; i<image_colors; i++)
8809 if (image->colormap[i].red == image->colormap[0].red &&
8810 image->colormap[i].green == image->colormap[0].green &&
8811 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008812 {
glennrp67b9c1a2011-04-22 18:47:36 +00008813 ping_have_cheap_transparency = MagickFalse;
8814 break;
glennrpfd05d622011-02-25 04:10:33 +00008815 }
8816 }
glennrpbb4f99d2011-05-22 11:13:17 +00008817
glennrpfd05d622011-02-25 04:10:33 +00008818 if (logging != MagickFalse)
8819 {
8820 if (ping_have_cheap_transparency == MagickFalse)
8821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8822 " Cheap transparency is not possible.");
8823
8824 else
8825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8826 " Cheap transparency is possible.");
8827 }
8828 }
8829 else
8830 ping_have_cheap_transparency = MagickFalse;
8831
glennrp8640fb52010-11-23 15:48:26 +00008832 image_depth=image->depth;
8833
glennrp26c990a2010-11-23 02:23:20 +00008834 quantum_info = (QuantumInfo *) NULL;
8835 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008836 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008837 image_matte=image->matte;
8838
glennrp0fe50b42010-11-16 03:52:51 +00008839 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008840 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008841
glennrp52a479c2011-02-26 21:14:38 +00008842 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8843 (image->colors == 0 || image->colormap == NULL))
8844 {
cristy16ea1392012-03-21 20:38:41 +00008845 image_info=DestroyImageInfo(image_info);
8846 image=DestroyImage(image);
8847 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
glennrp15e01552011-03-06 23:29:17 +00008848 "Cannot write PNG8 or color-type 3; colormap is NULL",
cristy16ea1392012-03-21 20:38:41 +00008849 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008850 return(MagickFalse);
8851 }
8852
cristy3ed852e2009-09-05 21:47:34 +00008853 /*
8854 Allocate the PNG structures
8855 */
8856#ifdef PNG_USER_MEM_SUPPORTED
cristy16ea1392012-03-21 20:38:41 +00008857 error_info.image=image;
8858 error_info.exception=exception;
8859 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008860 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8861 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008862
cristy3ed852e2009-09-05 21:47:34 +00008863#else
cristy16ea1392012-03-21 20:38:41 +00008864 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
glennrpcf002022011-01-30 02:38:15 +00008865 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008866
cristy3ed852e2009-09-05 21:47:34 +00008867#endif
8868 if (ping == (png_struct *) NULL)
8869 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008870
cristy3ed852e2009-09-05 21:47:34 +00008871 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008872
cristy3ed852e2009-09-05 21:47:34 +00008873 if (ping_info == (png_info *) NULL)
8874 {
8875 png_destroy_write_struct(&ping,(png_info **) NULL);
8876 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8877 }
glennrp0fe50b42010-11-16 03:52:51 +00008878
cristy3ed852e2009-09-05 21:47:34 +00008879 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008880 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008881
glennrp5af765f2010-03-30 11:12:18 +00008882 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008883 {
8884 /*
8885 PNG write failed.
8886 */
8887#ifdef PNG_DEBUG
8888 if (image_info->verbose)
8889 (void) printf("PNG write has failed.\n");
8890#endif
8891 png_destroy_write_struct(&ping,&ping_info);
glennrpedaa0382012-04-12 14:16:21 +00008892#ifdef PNG_SETJMP_NOT_THREAD_SAFE
glennrpcf002022011-01-30 02:38:15 +00008893 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008894#endif
glennrpedaa0382012-04-12 14:16:21 +00008895
8896 if (ping_pixels != (unsigned char *) NULL)
8897 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
8898
8899 if (quantum_info != (QuantumInfo *) NULL)
8900 quantum_info=DestroyQuantumInfo(quantum_info);
8901
cristy16ea1392012-03-21 20:38:41 +00008902 if (ping_have_blob != MagickFalse)
8903 (void) CloseBlob(image);
8904 image_info=DestroyImageInfo(image_info);
8905 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008906 return(MagickFalse);
8907 }
glennrpedaa0382012-04-12 14:16:21 +00008908
8909 /* { For navigation to end of SETJMP-protected block. Within this
8910 * block, use png_error() instead of Throwing an Exception, to ensure
8911 * that libpng is able to clean up, and that the semaphore is unlocked.
8912 */
8913
8914#ifdef PNG_SETJMP_NOT_THREAD_SAFE
8915 LockSemaphoreInfo(ping_semaphore);
8916#endif
8917
cristy3ed852e2009-09-05 21:47:34 +00008918 /*
8919 Prepare PNG for writing.
8920 */
8921#if defined(PNG_MNG_FEATURES_SUPPORTED)
8922 if (mng_info->write_mng)
8923 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008924
cristy3ed852e2009-09-05 21:47:34 +00008925#else
8926# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8927 if (mng_info->write_mng)
8928 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008929
cristy3ed852e2009-09-05 21:47:34 +00008930# endif
8931#endif
glennrp2b013e42010-11-24 16:55:50 +00008932
cristy3ed852e2009-09-05 21:47:34 +00008933 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008934
cristy4e5bc842010-06-09 13:56:01 +00008935 ping_width=(png_uint_32) image->columns;
8936 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008937
cristy3ed852e2009-09-05 21:47:34 +00008938 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8939 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008940
cristy3ed852e2009-09-05 21:47:34 +00008941 if (mng_info->write_png_depth != 0)
8942 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008943
cristy3ed852e2009-09-05 21:47:34 +00008944 /* Adjust requested depth to next higher valid depth if necessary */
8945 if (image_depth > 8)
8946 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008947
cristy3ed852e2009-09-05 21:47:34 +00008948 if ((image_depth > 4) && (image_depth < 8))
8949 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008950
cristy3ed852e2009-09-05 21:47:34 +00008951 if (image_depth == 3)
8952 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008953
cristy3ed852e2009-09-05 21:47:34 +00008954 if (logging != MagickFalse)
8955 {
8956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008957 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008959 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008961 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008963 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008965 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008966 }
glennrp8640fb52010-11-23 15:48:26 +00008967
cristy3ed852e2009-09-05 21:47:34 +00008968 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008969 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008970
glennrp26f37912010-12-23 16:22:42 +00008971
cristy3ed852e2009-09-05 21:47:34 +00008972#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008973 if (ping_exclude_pHYs == MagickFalse)
8974 {
cristy16ea1392012-03-21 20:38:41 +00008975 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
cristy3ed852e2009-09-05 21:47:34 +00008976 (!mng_info->write_mng || !mng_info->equal_physs))
8977 {
glennrp0fe50b42010-11-16 03:52:51 +00008978 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8980 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008981
8982 if (image->units == PixelsPerInchResolution)
8983 {
glennrpdfd70802010-11-14 01:23:35 +00008984 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008985 ping_pHYs_x_resolution=
cristy16ea1392012-03-21 20:38:41 +00008986 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
glennrp823b55c2011-03-14 18:46:46 +00008987 ping_pHYs_y_resolution=
cristy16ea1392012-03-21 20:38:41 +00008988 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008989 }
glennrpdfd70802010-11-14 01:23:35 +00008990
cristy3ed852e2009-09-05 21:47:34 +00008991 else if (image->units == PixelsPerCentimeterResolution)
8992 {
glennrpdfd70802010-11-14 01:23:35 +00008993 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
cristy16ea1392012-03-21 20:38:41 +00008994 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
8995 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008996 }
glennrp991d11d2010-11-12 21:55:28 +00008997
cristy3ed852e2009-09-05 21:47:34 +00008998 else
8999 {
glennrpdfd70802010-11-14 01:23:35 +00009000 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
cristy16ea1392012-03-21 20:38:41 +00009001 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9002 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +00009003 }
glennrp991d11d2010-11-12 21:55:28 +00009004
glennrp823b55c2011-03-14 18:46:46 +00009005 if (logging != MagickFalse)
9006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9007 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9008 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9009 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00009010 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009011 }
glennrp26f37912010-12-23 16:22:42 +00009012 }
cristy3ed852e2009-09-05 21:47:34 +00009013#endif
glennrpa521b2f2010-10-29 04:11:03 +00009014
glennrp26f37912010-12-23 16:22:42 +00009015 if (ping_exclude_bKGD == MagickFalse)
9016 {
glennrpa521b2f2010-10-29 04:11:03 +00009017 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00009018 {
glennrpa521b2f2010-10-29 04:11:03 +00009019 unsigned int
9020 mask;
cristy3ed852e2009-09-05 21:47:34 +00009021
glennrpa521b2f2010-10-29 04:11:03 +00009022 mask=0xffff;
9023 if (ping_bit_depth == 8)
9024 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00009025
glennrpa521b2f2010-10-29 04:11:03 +00009026 if (ping_bit_depth == 4)
9027 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00009028
glennrpa521b2f2010-10-29 04:11:03 +00009029 if (ping_bit_depth == 2)
9030 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00009031
glennrpa521b2f2010-10-29 04:11:03 +00009032 if (ping_bit_depth == 1)
9033 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00009034
glennrpa521b2f2010-10-29 04:11:03 +00009035 ping_background.red=(png_uint_16)
9036 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009037
glennrpa521b2f2010-10-29 04:11:03 +00009038 ping_background.green=(png_uint_16)
9039 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00009040
glennrpa521b2f2010-10-29 04:11:03 +00009041 ping_background.blue=(png_uint_16)
9042 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00009043
9044 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00009045 }
cristy3ed852e2009-09-05 21:47:34 +00009046
glennrp0fe50b42010-11-16 03:52:51 +00009047 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00009048 {
9049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9050 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00009051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9052 " background_color index is %d",
9053 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009054
9055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9056 " ping_bit_depth=%d",ping_bit_depth);
9057 }
glennrp0fe50b42010-11-16 03:52:51 +00009058
9059 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009060 }
glennrp0fe50b42010-11-16 03:52:51 +00009061
cristy3ed852e2009-09-05 21:47:34 +00009062 /*
9063 Select the color type.
9064 */
9065 matte=image_matte;
9066 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00009067
glennrp1273f7b2011-02-24 03:20:30 +00009068 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00009069 {
glennrp0fe50b42010-11-16 03:52:51 +00009070
glennrpfd05d622011-02-25 04:10:33 +00009071 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00009072 for reducing the sample depth from 8. */
9073
glennrp0fe50b42010-11-16 03:52:51 +00009074 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00009075
glennrp8bb3a022010-12-13 20:40:04 +00009076 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009077
9078 /*
9079 Set image palette.
9080 */
9081 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9082
glennrp0fe50b42010-11-16 03:52:51 +00009083 if (logging != MagickFalse)
9084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9085 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00009086 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009087
9088 for (i=0; i < (ssize_t) number_colors; i++)
9089 {
9090 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9091 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9092 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9093 if (logging != MagickFalse)
9094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00009095#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00009096 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00009097#else
9098 " %5ld (%5d,%5d,%5d)",
9099#endif
glennrp0fe50b42010-11-16 03:52:51 +00009100 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9101
9102 }
glennrp2b013e42010-11-24 16:55:50 +00009103
glennrp8bb3a022010-12-13 20:40:04 +00009104 ping_have_PLTE=MagickTrue;
9105 image_depth=ping_bit_depth;
9106 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00009107
glennrp58e01762011-01-07 15:28:54 +00009108 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009109 {
glennrp0fe50b42010-11-16 03:52:51 +00009110 /*
9111 Identify which colormap entry is transparent.
9112 */
9113 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00009114 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00009115
glennrp8bb3a022010-12-13 20:40:04 +00009116 for (i=0; i < (ssize_t) number_transparent; i++)
9117 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00009118
glennrp0fe50b42010-11-16 03:52:51 +00009119
glennrp2cc891a2010-12-24 13:44:32 +00009120 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00009121 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009122
9123 if (ping_num_trans == 0)
9124 ping_have_tRNS=MagickFalse;
9125
glennrp8bb3a022010-12-13 20:40:04 +00009126 else
9127 ping_have_tRNS=MagickTrue;
9128 }
glennrp0fe50b42010-11-16 03:52:51 +00009129
glennrp1273f7b2011-02-24 03:20:30 +00009130 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00009131 {
glennrp1273f7b2011-02-24 03:20:30 +00009132 /*
9133 * Identify which colormap entry is the background color.
9134 */
9135
glennrp4f25bd02011-01-01 18:51:28 +00009136 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9137 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9138 break;
glennrp0fe50b42010-11-16 03:52:51 +00009139
glennrp4f25bd02011-01-01 18:51:28 +00009140 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009141
9142 if (logging != MagickFalse)
9143 {
9144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9145 " background_color index is %d",
9146 (int) ping_background.index);
9147 }
glennrp4f25bd02011-01-01 18:51:28 +00009148 }
cristy3ed852e2009-09-05 21:47:34 +00009149 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009150
glennrp7e65e932011-08-19 02:31:16 +00009151 else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
cristy3ed852e2009-09-05 21:47:34 +00009152 {
9153 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009154 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009155 }
glennrp0fe50b42010-11-16 03:52:51 +00009156
glennrp7e65e932011-08-19 02:31:16 +00009157 else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
cristy3ed852e2009-09-05 21:47:34 +00009158 {
9159 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009160 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009161 }
glennrp0fe50b42010-11-16 03:52:51 +00009162
glennrp8bb3a022010-12-13 20:40:04 +00009163 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009164 {
glennrp5af765f2010-03-30 11:12:18 +00009165 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009166
glennrp8bb3a022010-12-13 20:40:04 +00009167 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009168 {
glennrp5af765f2010-03-30 11:12:18 +00009169 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009170
glennrp5af765f2010-03-30 11:12:18 +00009171 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9172 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009173 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009174
glennrp8bb3a022010-12-13 20:40:04 +00009175 else
9176 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009177
9178 if (logging != MagickFalse)
9179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9180 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009181 }
glennrp0fe50b42010-11-16 03:52:51 +00009182
glennrp7c4c9e62011-03-21 20:23:32 +00009183 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009184 {
9185 if (logging != MagickFalse)
9186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009187 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009188
glennrpd6bf1612010-12-17 17:28:54 +00009189 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009190 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009191
glennrpd6bf1612010-12-17 17:28:54 +00009192 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009193 {
glennrp5af765f2010-03-30 11:12:18 +00009194 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009195 image_matte=MagickFalse;
9196 }
glennrp0fe50b42010-11-16 03:52:51 +00009197
glennrpd6bf1612010-12-17 17:28:54 +00009198 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009199 {
glennrp5af765f2010-03-30 11:12:18 +00009200 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009201 image_matte=MagickTrue;
9202 }
glennrp0fe50b42010-11-16 03:52:51 +00009203
glennrp5aa37f62011-01-02 03:07:57 +00009204 if (image_info->type == PaletteType ||
9205 image_info->type == PaletteMatteType)
9206 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9207
glennrp7c4c9e62011-03-21 20:23:32 +00009208 if (mng_info->write_png_colortype == 0 &&
9209 (image_info->type == UndefinedType ||
9210 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009211 {
glennrp5aa37f62011-01-02 03:07:57 +00009212 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009213 {
glennrp5aa37f62011-01-02 03:07:57 +00009214 if (image_matte == MagickFalse)
9215 {
9216 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9217 image_matte=MagickFalse;
9218 }
glennrp0fe50b42010-11-16 03:52:51 +00009219
glennrp0b206f52011-01-07 04:55:32 +00009220 else
glennrp5aa37f62011-01-02 03:07:57 +00009221 {
9222 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9223 image_matte=MagickTrue;
9224 }
9225 }
9226 else
glennrp8bb3a022010-12-13 20:40:04 +00009227 {
glennrp5aa37f62011-01-02 03:07:57 +00009228 if (image_matte == MagickFalse)
9229 {
9230 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9231 image_matte=MagickFalse;
9232 }
glennrp8bb3a022010-12-13 20:40:04 +00009233
glennrp0b206f52011-01-07 04:55:32 +00009234 else
glennrp5aa37f62011-01-02 03:07:57 +00009235 {
9236 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9237 image_matte=MagickTrue;
9238 }
9239 }
glennrp0fe50b42010-11-16 03:52:51 +00009240 }
glennrp5aa37f62011-01-02 03:07:57 +00009241
cristy3ed852e2009-09-05 21:47:34 +00009242 }
glennrp0fe50b42010-11-16 03:52:51 +00009243
cristy3ed852e2009-09-05 21:47:34 +00009244 if (logging != MagickFalse)
9245 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009246 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009247
glennrp5af765f2010-03-30 11:12:18 +00009248 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009249 {
9250 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9251 ping_color_type == PNG_COLOR_TYPE_RGB ||
9252 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9253 ping_bit_depth=8;
9254 }
cristy3ed852e2009-09-05 21:47:34 +00009255
glennrpd6bf1612010-12-17 17:28:54 +00009256 old_bit_depth=ping_bit_depth;
9257
glennrp5af765f2010-03-30 11:12:18 +00009258 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009259 {
glennrp8d579662011-02-23 02:05:02 +00009260 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9261 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009262 }
glennrp8640fb52010-11-23 15:48:26 +00009263
glennrp5af765f2010-03-30 11:12:18 +00009264 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009265 {
cristy35ef8242010-06-03 16:24:13 +00009266 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009267 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009268
9269 if (image->colors == 0)
9270 {
glennrp0fe50b42010-11-16 03:52:51 +00009271 /* DO SOMETHING */
glennrpedaa0382012-04-12 14:16:21 +00009272 png_error(ping,"image has 0 colors");
glennrp0f111982010-07-07 20:18:33 +00009273 }
9274
cristy35ef8242010-06-03 16:24:13 +00009275 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009276 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009277 }
glennrp2b013e42010-11-24 16:55:50 +00009278
glennrpd6bf1612010-12-17 17:28:54 +00009279 if (logging != MagickFalse)
9280 {
9281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9282 " Number of colors: %.20g",(double) image_colors);
9283
9284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9285 " Tentative PNG bit depth: %d",ping_bit_depth);
9286 }
9287
9288 if (ping_bit_depth < (int) mng_info->write_png_depth)
9289 ping_bit_depth = mng_info->write_png_depth;
9290 }
glennrp2cc891a2010-12-24 13:44:32 +00009291
glennrp5af765f2010-03-30 11:12:18 +00009292 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009293
cristy3ed852e2009-09-05 21:47:34 +00009294 if (logging != MagickFalse)
9295 {
9296 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009297 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009298
cristy3ed852e2009-09-05 21:47:34 +00009299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009300 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009301
cristy3ed852e2009-09-05 21:47:34 +00009302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009303 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009304
cristy3ed852e2009-09-05 21:47:34 +00009305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009306
glennrp8640fb52010-11-23 15:48:26 +00009307 " image->depth: %.20g",(double) image->depth);
9308
9309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009310 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009311 }
9312
glennrp58e01762011-01-07 15:28:54 +00009313 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009314 {
glennrp4f25bd02011-01-01 18:51:28 +00009315 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009316 {
glennrp7c4c9e62011-03-21 20:23:32 +00009317 if (mng_info->write_png_colortype == 0)
9318 {
9319 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009320
glennrp7c4c9e62011-03-21 20:23:32 +00009321 if (ping_have_color != MagickFalse)
9322 ping_color_type=PNG_COLOR_TYPE_RGBA;
9323 }
glennrp4f25bd02011-01-01 18:51:28 +00009324
9325 /*
9326 * Determine if there is any transparent color.
9327 */
9328 if (number_transparent + number_semitransparent == 0)
9329 {
9330 /*
9331 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9332 */
glennrpa6a06632011-01-19 15:15:34 +00009333
glennrp4f25bd02011-01-01 18:51:28 +00009334 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009335
9336 if (mng_info->write_png_colortype == 0)
9337 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009338 }
9339
9340 else
9341 {
9342 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009343 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009344
9345 mask=0xffff;
9346
9347 if (ping_bit_depth == 8)
9348 mask=0x00ff;
9349
9350 if (ping_bit_depth == 4)
9351 mask=0x000f;
9352
9353 if (ping_bit_depth == 2)
9354 mask=0x0003;
9355
9356 if (ping_bit_depth == 1)
9357 mask=0x0001;
9358
9359 ping_trans_color.red=(png_uint_16)
9360 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9361
9362 ping_trans_color.green=(png_uint_16)
9363 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9364
9365 ping_trans_color.blue=(png_uint_16)
9366 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9367
9368 ping_trans_color.gray=(png_uint_16)
cristy16ea1392012-03-21 20:38:41 +00009369 (ScaleQuantumToShort(GetPixelInfoIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009370 image->colormap)) & mask);
9371
9372 ping_trans_color.index=(png_byte) 0;
9373
9374 ping_have_tRNS=MagickTrue;
9375 }
9376
9377 if (ping_have_tRNS != MagickFalse)
9378 {
9379 /*
glennrpfd05d622011-02-25 04:10:33 +00009380 * Determine if there is one and only one transparent color
9381 * and if so if it is fully transparent.
9382 */
9383 if (ping_have_cheap_transparency == MagickFalse)
9384 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009385 }
9386
9387 if (ping_have_tRNS != MagickFalse)
9388 {
glennrp7c4c9e62011-03-21 20:23:32 +00009389 if (mng_info->write_png_colortype == 0)
9390 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009391
9392 if (image_depth == 8)
9393 {
9394 ping_trans_color.red&=0xff;
9395 ping_trans_color.green&=0xff;
9396 ping_trans_color.blue&=0xff;
9397 ping_trans_color.gray&=0xff;
9398 }
9399 }
9400 }
cristy3ed852e2009-09-05 21:47:34 +00009401 else
9402 {
cristy3ed852e2009-09-05 21:47:34 +00009403 if (image_depth == 8)
9404 {
glennrp5af765f2010-03-30 11:12:18 +00009405 ping_trans_color.red&=0xff;
9406 ping_trans_color.green&=0xff;
9407 ping_trans_color.blue&=0xff;
9408 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009409 }
9410 }
9411 }
glennrp8640fb52010-11-23 15:48:26 +00009412
cristy3ed852e2009-09-05 21:47:34 +00009413 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009414
glennrp2e09f552010-11-14 00:38:48 +00009415 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009416 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009417
glennrp39992b42010-11-14 00:03:43 +00009418 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009419 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009420 ping_have_color == MagickFalse &&
9421 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009422 {
cristy35ef8242010-06-03 16:24:13 +00009423 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009424
cristy3ed852e2009-09-05 21:47:34 +00009425 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009426 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009427
glennrp7c4c9e62011-03-21 20:23:32 +00009428 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009429 {
glennrp5af765f2010-03-30 11:12:18 +00009430 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009431
cristy3ed852e2009-09-05 21:47:34 +00009432 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009433 {
9434 if (logging != MagickFalse)
9435 {
9436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9437 " Scaling ping_trans_color (0)");
9438 }
9439 ping_trans_color.gray*=0x0101;
9440 }
cristy3ed852e2009-09-05 21:47:34 +00009441 }
glennrp0fe50b42010-11-16 03:52:51 +00009442
cristy3ed852e2009-09-05 21:47:34 +00009443 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9444 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009445
glennrp136ee3a2011-04-27 15:47:45 +00009446 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009447 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009448 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009449
cristy3ed852e2009-09-05 21:47:34 +00009450 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009451 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009452
cristy3ed852e2009-09-05 21:47:34 +00009453 else
9454 {
glennrp5af765f2010-03-30 11:12:18 +00009455 ping_bit_depth=8;
9456 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009457 {
9458 if(!mng_info->write_png_depth)
9459 {
glennrp5af765f2010-03-30 11:12:18 +00009460 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009461
cristy35ef8242010-06-03 16:24:13 +00009462 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009463 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009464 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009465 }
9466 }
glennrp2b013e42010-11-24 16:55:50 +00009467
glennrp0fe50b42010-11-16 03:52:51 +00009468 else if (ping_color_type ==
9469 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009470 mng_info->IsPalette)
9471 {
cristy3ed852e2009-09-05 21:47:34 +00009472 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009473
cristy3ed852e2009-09-05 21:47:34 +00009474 int
9475 depth_4_ok=MagickTrue,
9476 depth_2_ok=MagickTrue,
9477 depth_1_ok=MagickTrue;
9478
cristybb503372010-05-27 20:51:26 +00009479 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009480 {
9481 unsigned char
9482 intensity;
9483
9484 intensity=ScaleQuantumToChar(image->colormap[i].red);
9485
9486 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9487 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9488 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9489 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009490 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009491 depth_1_ok=MagickFalse;
9492 }
glennrp2b013e42010-11-24 16:55:50 +00009493
cristy3ed852e2009-09-05 21:47:34 +00009494 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009495 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009496
cristy3ed852e2009-09-05 21:47:34 +00009497 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009498 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009499
cristy3ed852e2009-09-05 21:47:34 +00009500 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009501 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009502 }
9503 }
glennrp2b013e42010-11-24 16:55:50 +00009504
glennrp5af765f2010-03-30 11:12:18 +00009505 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009506 }
glennrp0fe50b42010-11-16 03:52:51 +00009507
cristy3ed852e2009-09-05 21:47:34 +00009508 else
glennrp0fe50b42010-11-16 03:52:51 +00009509
cristy3ed852e2009-09-05 21:47:34 +00009510 if (mng_info->IsPalette)
9511 {
glennrp17a14852010-05-10 03:01:59 +00009512 number_colors=image_colors;
9513
cristy3ed852e2009-09-05 21:47:34 +00009514 if (image_depth <= 8)
9515 {
cristy3ed852e2009-09-05 21:47:34 +00009516 /*
9517 Set image palette.
9518 */
glennrp5af765f2010-03-30 11:12:18 +00009519 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009520
glennrp58e01762011-01-07 15:28:54 +00009521 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009522 {
glennrp9c1eb072010-06-06 22:19:15 +00009523 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009524
glennrp3b51f0e2010-11-27 18:14:08 +00009525 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9527 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009528 }
glennrp0fe50b42010-11-16 03:52:51 +00009529
cristy3ed852e2009-09-05 21:47:34 +00009530 else
9531 {
cristybb503372010-05-27 20:51:26 +00009532 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009533 {
9534 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9535 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9536 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9537 }
glennrp0fe50b42010-11-16 03:52:51 +00009538
glennrp3b51f0e2010-11-27 18:14:08 +00009539 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009541 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009542 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009543
glennrp39992b42010-11-14 00:03:43 +00009544 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009545 }
glennrp0fe50b42010-11-16 03:52:51 +00009546
cristy3ed852e2009-09-05 21:47:34 +00009547 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009548 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009549 {
cristybefe4d22010-06-07 01:18:58 +00009550 size_t
9551 one;
9552
glennrp5af765f2010-03-30 11:12:18 +00009553 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009554 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009555
cristy16ea1392012-03-21 20:38:41 +00009556 while ((one << ping_bit_depth) < (size_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009557 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009558 }
glennrp0fe50b42010-11-16 03:52:51 +00009559
glennrp5af765f2010-03-30 11:12:18 +00009560 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009561
glennrp58e01762011-01-07 15:28:54 +00009562 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009563 {
glennrp0fe50b42010-11-16 03:52:51 +00009564 /*
glennrpd6bf1612010-12-17 17:28:54 +00009565 * Set up trans_colors array.
9566 */
glennrp0fe50b42010-11-16 03:52:51 +00009567 assert(number_colors <= 256);
9568
glennrpd6bf1612010-12-17 17:28:54 +00009569 ping_num_trans=(unsigned short) (number_transparent +
9570 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009571
9572 if (ping_num_trans == 0)
9573 ping_have_tRNS=MagickFalse;
9574
glennrpd6bf1612010-12-17 17:28:54 +00009575 else
glennrp0fe50b42010-11-16 03:52:51 +00009576 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009577 if (logging != MagickFalse)
9578 {
9579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9580 " Scaling ping_trans_color (1)");
9581 }
glennrpd6bf1612010-12-17 17:28:54 +00009582 ping_have_tRNS=MagickTrue;
9583
9584 for (i=0; i < ping_num_trans; i++)
9585 {
cristy16ea1392012-03-21 20:38:41 +00009586 ping_trans_alpha[i]= (png_byte)
9587 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009588 }
glennrp0fe50b42010-11-16 03:52:51 +00009589 }
9590 }
cristy3ed852e2009-09-05 21:47:34 +00009591 }
9592 }
glennrp0fe50b42010-11-16 03:52:51 +00009593
cristy3ed852e2009-09-05 21:47:34 +00009594 else
9595 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009596
cristy3ed852e2009-09-05 21:47:34 +00009597 if (image_depth < 8)
9598 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009599
cristy3ed852e2009-09-05 21:47:34 +00009600 if ((save_image_depth == 16) && (image_depth == 8))
9601 {
glennrp4f25bd02011-01-01 18:51:28 +00009602 if (logging != MagickFalse)
9603 {
9604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9605 " Scaling ping_trans_color from (%d,%d,%d)",
9606 (int) ping_trans_color.red,
9607 (int) ping_trans_color.green,
9608 (int) ping_trans_color.blue);
9609 }
9610
glennrp5af765f2010-03-30 11:12:18 +00009611 ping_trans_color.red*=0x0101;
9612 ping_trans_color.green*=0x0101;
9613 ping_trans_color.blue*=0x0101;
9614 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009615
9616 if (logging != MagickFalse)
9617 {
9618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9619 " to (%d,%d,%d)",
9620 (int) ping_trans_color.red,
9621 (int) ping_trans_color.green,
9622 (int) ping_trans_color.blue);
9623 }
cristy3ed852e2009-09-05 21:47:34 +00009624 }
9625 }
9626
cristy4383ec82011-01-05 15:42:32 +00009627 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9628 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009629
cristy3ed852e2009-09-05 21:47:34 +00009630 /*
9631 Adjust background and transparency samples in sub-8-bit grayscale files.
9632 */
glennrp5af765f2010-03-30 11:12:18 +00009633 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009634 PNG_COLOR_TYPE_GRAY)
9635 {
9636 png_uint_16
9637 maxval;
9638
cristy35ef8242010-06-03 16:24:13 +00009639 size_t
9640 one=1;
9641
cristy22ffd972010-06-03 16:51:47 +00009642 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009643
glennrp4f25bd02011-01-01 18:51:28 +00009644 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009645 {
cristy3ed852e2009-09-05 21:47:34 +00009646
cristy16ea1392012-03-21 20:38:41 +00009647 ping_background.gray=(png_uint_16) ((maxval/65535.)*
9648 (ScaleQuantumToShort(((GetPixelInfoIntensity(
9649 &image->background_color))) +.5)));
9650
cristy3ed852e2009-09-05 21:47:34 +00009651 if (logging != MagickFalse)
9652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +00009653 " Setting up bKGD chunk (2)");
9654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9655 " background_color index is %d",
9656 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009657
glennrp991d11d2010-11-12 21:55:28 +00009658 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009659 }
cristy3ed852e2009-09-05 21:47:34 +00009660
glennrp3e3e20f2011-06-09 04:21:43 +00009661 if (logging != MagickFalse)
9662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9663 " Scaling ping_trans_color.gray from %d",
9664 (int)ping_trans_color.gray);
9665
glennrp9be9b1c2011-06-09 12:21:45 +00009666 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009667 ping_trans_color.gray)+.5);
9668
9669 if (logging != MagickFalse)
9670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9671 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009672 }
glennrp17a14852010-05-10 03:01:59 +00009673
glennrp26f37912010-12-23 16:22:42 +00009674 if (ping_exclude_bKGD == MagickFalse)
9675 {
glennrp1273f7b2011-02-24 03:20:30 +00009676 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009677 {
9678 /*
9679 Identify which colormap entry is the background color.
9680 */
9681
glennrp17a14852010-05-10 03:01:59 +00009682 number_colors=image_colors;
9683
glennrpa521b2f2010-10-29 04:11:03 +00009684 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9685 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009686 break;
9687
9688 ping_background.index=(png_byte) i;
9689
glennrp3b51f0e2010-11-27 18:14:08 +00009690 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009691 {
9692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009693 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009694 }
glennrp0fe50b42010-11-16 03:52:51 +00009695
cristy13d07042010-11-21 20:56:18 +00009696 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009697 {
9698 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009699
9700 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009701 {
9702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9703 " background =(%d,%d,%d)",
9704 (int) ping_background.red,
9705 (int) ping_background.green,
9706 (int) ping_background.blue);
9707 }
9708 }
glennrpa521b2f2010-10-29 04:11:03 +00009709
glennrpd6bf1612010-12-17 17:28:54 +00009710 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009711 {
glennrp3b51f0e2010-11-27 18:14:08 +00009712 if (logging != MagickFalse)
9713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9714 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009715 ping_have_bKGD = MagickFalse;
9716 }
glennrp17a14852010-05-10 03:01:59 +00009717 }
glennrp26f37912010-12-23 16:22:42 +00009718 }
glennrp17a14852010-05-10 03:01:59 +00009719
cristy3ed852e2009-09-05 21:47:34 +00009720 if (logging != MagickFalse)
9721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009722 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009723 /*
9724 Initialize compression level and filtering.
9725 */
9726 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009727 {
9728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9729 " Setting up deflate compression");
9730
9731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9732 " Compression buffer size: 32768");
9733 }
9734
cristy3ed852e2009-09-05 21:47:34 +00009735 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009736
cristy3ed852e2009-09-05 21:47:34 +00009737 if (logging != MagickFalse)
9738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9739 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009740
cristy4054bfb2011-08-29 23:41:39 +00009741 png_set_compression_mem_level(ping, 9);
9742
glennrp10d739e2011-06-29 18:00:52 +00009743 /* Untangle the "-quality" setting:
9744
9745 Undefined is 0; the default is used.
9746 Default is 75
9747
9748 10's digit:
9749
9750 0: Use Z_HUFFMAN_ONLY strategy with the
9751 zlib default compression level
9752
9753 1-9: the zlib compression level
9754
9755 1's digit:
9756
9757 0-4: the PNG filter method
9758
9759 5: libpng adaptive filtering if compression level > 5
9760 libpng filter type "none" if compression level <= 5
9761 or if image is grayscale or palette
9762
9763 6: libpng adaptive filtering
9764
9765 7: "LOCO" filtering (intrapixel differing) if writing
9766 a MNG, othewise "none". Did not work in IM-6.7.0-9
9767 and earlier because of a missing "else".
9768
9769 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009770 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009771
9772 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009773 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009774
9775 Note that using the -quality option, not all combinations of
9776 PNG filter type, zlib compression level, and zlib compression
cristy16ea1392012-03-21 20:38:41 +00009777 strategy are possible. This will be addressed soon in a
9778 release that accomodates "-define png:compression-strategy", etc.
glennrp10d739e2011-06-29 18:00:52 +00009779
9780 */
9781
cristy3ed852e2009-09-05 21:47:34 +00009782 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9783 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009784
glennrp18682582011-06-30 18:11:47 +00009785 if (quality <= 9)
9786 {
9787 if (mng_info->write_png_compression_strategy == 0)
9788 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9789 }
9790
9791 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009792 {
9793 int
9794 level;
9795
cristybb503372010-05-27 20:51:26 +00009796 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009797
glennrp18682582011-06-30 18:11:47 +00009798 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009799 }
glennrp0fe50b42010-11-16 03:52:51 +00009800
glennrp18682582011-06-30 18:11:47 +00009801 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009802 {
glennrp18682582011-06-30 18:11:47 +00009803 if ((quality %10) == 8 || (quality %10) == 9)
9804 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009805 }
glennrp0fe50b42010-11-16 03:52:51 +00009806
glennrp18682582011-06-30 18:11:47 +00009807 if (mng_info->write_png_compression_filter == 0)
9808 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9809
cristy3ed852e2009-09-05 21:47:34 +00009810 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009811 {
glennrp18682582011-06-30 18:11:47 +00009812 if (mng_info->write_png_compression_level)
9813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9814 " Compression level: %d",
9815 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009816
glennrp18682582011-06-30 18:11:47 +00009817 if (mng_info->write_png_compression_strategy)
9818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9819 " Compression strategy: %d",
9820 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009821
glennrp18682582011-06-30 18:11:47 +00009822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9823 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009824
cristy4054bfb2011-08-29 23:41:39 +00009825 if (mng_info->write_png_compression_filter == 6)
cristy3ed852e2009-09-05 21:47:34 +00009826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9827 " Base filter method: ADAPTIVE");
cristy4054bfb2011-08-29 23:41:39 +00009828 else if (mng_info->write_png_compression_filter == 0 ||
9829 mng_info->write_png_compression_filter == 1)
cristy3ed852e2009-09-05 21:47:34 +00009830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9831 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009832 else
9833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9834 " Base filter method: %d",
9835 (int) mng_info->write_png_compression_filter-1);
9836 }
glennrp2b013e42010-11-24 16:55:50 +00009837
glennrp18682582011-06-30 18:11:47 +00009838 if (mng_info->write_png_compression_level != 0)
9839 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9840
9841 if (mng_info->write_png_compression_filter == 6)
9842 {
9843 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9844 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9845 (quality < 50))
9846 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9847 else
9848 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9849 }
cristy4054bfb2011-08-29 23:41:39 +00009850 else if (mng_info->write_png_compression_filter == 7 ||
glennrp18682582011-06-30 18:11:47 +00009851 mng_info->write_png_compression_filter == 10)
9852 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9853
9854 else if (mng_info->write_png_compression_filter == 8)
9855 {
9856#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9857 if (mng_info->write_mng)
9858 {
9859 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9860 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9861 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9862 }
9863#endif
cristy4054bfb2011-08-29 23:41:39 +00009864 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
glennrp18682582011-06-30 18:11:47 +00009865 }
9866
9867 else if (mng_info->write_png_compression_filter == 9)
9868 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9869
9870 else if (mng_info->write_png_compression_filter != 0)
9871 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9872 mng_info->write_png_compression_filter-1);
9873
9874 if (mng_info->write_png_compression_strategy != 0)
9875 png_set_compression_strategy(ping,
9876 mng_info->write_png_compression_strategy-1);
9877
cristy0d57eec2011-09-04 22:13:56 +00009878 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
9879 if (ping_exclude_sRGB != MagickFalse ||
9880 (image->rendering_intent == UndefinedIntent))
9881 {
9882 if ((ping_exclude_tEXt == MagickFalse ||
9883 ping_exclude_zTXt == MagickFalse) &&
9884 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009885 {
9886 ResetImageProfileIterator(image);
9887 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009888 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009889 profile=GetImageProfile(image,name);
9890
9891 if (profile != (StringInfo *) NULL)
9892 {
glennrp5af765f2010-03-30 11:12:18 +00009893#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009894 if ((LocaleCompare(name,"ICC") == 0) ||
9895 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009896 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009897
9898 if (ping_exclude_iCCP == MagickFalse)
9899 {
cristy16ea1392012-03-21 20:38:41 +00009900 png_set_iCCP(ping,ping_info,(png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009901#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009902 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009903#else
9904 (png_const_bytep) GetStringInfoDatum(profile),
9905#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009906 (png_uint_32) GetStringInfoLength(profile));
9907 }
glennrp26f37912010-12-23 16:22:42 +00009908 }
glennrp0fe50b42010-11-16 03:52:51 +00009909
glennrpc8cbc5d2011-01-01 00:12:34 +00009910 else
cristy3ed852e2009-09-05 21:47:34 +00009911#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009912 if (ping_exclude_zCCP == MagickFalse)
9913 {
glennrpcf002022011-01-30 02:38:15 +00009914 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009915 (unsigned char *) name,(unsigned char *) name,
9916 GetStringInfoDatum(profile),
9917 (png_uint_32) GetStringInfoLength(profile));
9918 }
9919 }
glennrp0b206f52011-01-07 04:55:32 +00009920
glennrpc8cbc5d2011-01-01 00:12:34 +00009921 if (logging != MagickFalse)
9922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9923 " Setting up text chunk with %s profile",name);
9924
9925 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009926 }
cristy0d57eec2011-09-04 22:13:56 +00009927 }
cristy3ed852e2009-09-05 21:47:34 +00009928 }
9929
9930#if defined(PNG_WRITE_sRGB_SUPPORTED)
9931 if ((mng_info->have_write_global_srgb == 0) &&
9932 ((image->rendering_intent != UndefinedIntent) ||
9933 (image->colorspace == sRGBColorspace)))
9934 {
glennrp26f37912010-12-23 16:22:42 +00009935 if (ping_exclude_sRGB == MagickFalse)
9936 {
9937 /*
9938 Note image rendering intent.
9939 */
9940 if (logging != MagickFalse)
9941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9942 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009943
glennrp26f37912010-12-23 16:22:42 +00009944 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009945 Magick_RenderingIntent_to_PNG_RenderingIntent(
9946 image->rendering_intent)));
glennrp26f37912010-12-23 16:22:42 +00009947 }
cristy3ed852e2009-09-05 21:47:34 +00009948 }
glennrp26f37912010-12-23 16:22:42 +00009949
glennrp5af765f2010-03-30 11:12:18 +00009950 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009951#endif
9952 {
glennrp2cc891a2010-12-24 13:44:32 +00009953 if (ping_exclude_gAMA == MagickFalse &&
9954 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009955 (image->gamma < .45 || image->gamma > .46)))
9956 {
cristy3ed852e2009-09-05 21:47:34 +00009957 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9958 {
9959 /*
9960 Note image gamma.
9961 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9962 */
9963 if (logging != MagickFalse)
9964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9965 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009966
cristy3ed852e2009-09-05 21:47:34 +00009967 png_set_gAMA(ping,ping_info,image->gamma);
9968 }
glennrp26f37912010-12-23 16:22:42 +00009969 }
glennrp2b013e42010-11-24 16:55:50 +00009970
glennrp26f37912010-12-23 16:22:42 +00009971 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009972 {
glennrp26f37912010-12-23 16:22:42 +00009973 if ((mng_info->have_write_global_chrm == 0) &&
9974 (image->chromaticity.red_primary.x != 0.0))
9975 {
9976 /*
9977 Note image chromaticity.
9978 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9979 */
9980 PrimaryInfo
9981 bp,
9982 gp,
9983 rp,
9984 wp;
cristy3ed852e2009-09-05 21:47:34 +00009985
glennrp26f37912010-12-23 16:22:42 +00009986 wp=image->chromaticity.white_point;
9987 rp=image->chromaticity.red_primary;
9988 gp=image->chromaticity.green_primary;
9989 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009990
glennrp26f37912010-12-23 16:22:42 +00009991 if (logging != MagickFalse)
9992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9993 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009994
glennrp26f37912010-12-23 16:22:42 +00009995 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9996 bp.x,bp.y);
9997 }
9998 }
cristy3ed852e2009-09-05 21:47:34 +00009999 }
glennrpdfd70802010-11-14 01:23:35 +000010000
glennrp5af765f2010-03-30 11:12:18 +000010001 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +000010002
10003 if (mng_info->write_mng)
10004 png_set_sig_bytes(ping,8);
10005
cristy5d6fc9c2011-12-27 03:10:42 +000010006 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
cristy3ed852e2009-09-05 21:47:34 +000010007
glennrpd6bf1612010-12-17 17:28:54 +000010008 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +000010009 {
10010 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +000010011 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010012 {
glennrp5af765f2010-03-30 11:12:18 +000010013 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +000010014
glennrp5af765f2010-03-30 11:12:18 +000010015 if (ping_bit_depth < 8)
10016 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +000010017 }
glennrp0fe50b42010-11-16 03:52:51 +000010018
cristy3ed852e2009-09-05 21:47:34 +000010019 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +000010020 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +000010021 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +000010022 }
10023
glennrp0e8ea192010-12-24 18:00:33 +000010024 if (ping_need_colortype_warning != MagickFalse ||
10025 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +000010026 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +000010027 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +000010028 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +000010029 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +000010030 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +000010031 {
10032 if (logging != MagickFalse)
10033 {
glennrp0e8ea192010-12-24 18:00:33 +000010034 if (ping_need_colortype_warning != MagickFalse)
10035 {
10036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10037 " Image has transparency but tRNS chunk was excluded");
10038 }
10039
cristy3ed852e2009-09-05 21:47:34 +000010040 if (mng_info->write_png_depth)
10041 {
10042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010043 " Defined png:bit-depth=%u, Computed depth=%u",
cristy3ed852e2009-09-05 21:47:34 +000010044 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +000010045 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +000010046 }
glennrp0e8ea192010-12-24 18:00:33 +000010047
cristy3ed852e2009-09-05 21:47:34 +000010048 if (mng_info->write_png_colortype)
10049 {
10050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010051 " Defined png:color-type=%u, Computed color type=%u",
cristy3ed852e2009-09-05 21:47:34 +000010052 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +000010053 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +000010054 }
10055 }
glennrp0e8ea192010-12-24 18:00:33 +000010056
glennrp3bd2e412010-08-10 13:34:52 +000010057 png_warning(ping,
cristy5d6fc9c2011-12-27 03:10:42 +000010058 "Cannot write image with defined png:bit-depth or png:color-type.");
cristy3ed852e2009-09-05 21:47:34 +000010059 }
10060
glennrp58e01762011-01-07 15:28:54 +000010061 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010062 {
10063 /* Add an opaque matte channel */
10064 image->matte = MagickTrue;
cristy16ea1392012-03-21 20:38:41 +000010065 (void) SetImageAlpha(image,OpaqueAlpha,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010066
glennrpb4a13412010-05-05 12:47:19 +000010067 if (logging != MagickFalse)
10068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10069 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +000010070 }
10071
glennrp0e319732011-01-25 21:53:13 +000010072 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +000010073 {
glennrp991d11d2010-11-12 21:55:28 +000010074 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +000010075 {
glennrp991d11d2010-11-12 21:55:28 +000010076 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +000010077 if (logging != MagickFalse)
10078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10079 " Setting ping_have_tRNS=MagickTrue.");
10080 }
glennrpe9c26dc2010-05-30 01:56:35 +000010081 }
10082
cristy3ed852e2009-09-05 21:47:34 +000010083 if (logging != MagickFalse)
10084 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10085 " Writing PNG header chunks");
10086
glennrp5af765f2010-03-30 11:12:18 +000010087 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10088 ping_bit_depth,ping_color_type,
10089 ping_interlace_method,ping_compression_method,
10090 ping_filter_method);
10091
glennrp39992b42010-11-14 00:03:43 +000010092 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10093 {
glennrpf09bded2011-01-08 01:15:59 +000010094 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +000010095
glennrp3b51f0e2010-11-27 18:14:08 +000010096 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +000010097 {
glennrp8640fb52010-11-23 15:48:26 +000010098 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +000010099 {
glennrpd6bf1612010-12-17 17:28:54 +000010100 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +000010101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010102 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10103 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010104 (int) palette[i].red,
10105 (int) palette[i].green,
10106 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +000010107 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +000010108 (int) ping_trans_alpha[i]);
10109 else
10110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010111 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010112 (int) i,
10113 (int) palette[i].red,
10114 (int) palette[i].green,
10115 (int) palette[i].blue);
10116 }
glennrp39992b42010-11-14 00:03:43 +000010117 }
glennrp39992b42010-11-14 00:03:43 +000010118 }
10119
glennrp26f37912010-12-23 16:22:42 +000010120 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010121 {
glennrp26f37912010-12-23 16:22:42 +000010122 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +000010123 {
glennrp26f37912010-12-23 16:22:42 +000010124 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +000010125 if (logging)
10126 {
10127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10128 " Setting up bKGD chunk");
10129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10130 " background color = (%d,%d,%d)",
10131 (int) ping_background.red,
10132 (int) ping_background.green,
10133 (int) ping_background.blue);
10134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10135 " index = %d, gray=%d",
10136 (int) ping_background.index,
10137 (int) ping_background.gray);
10138 }
10139 }
glennrp26f37912010-12-23 16:22:42 +000010140 }
10141
10142 if (ping_exclude_pHYs == MagickFalse)
10143 {
10144 if (ping_have_pHYs != MagickFalse)
10145 {
10146 png_set_pHYs(ping,ping_info,
10147 ping_pHYs_x_resolution,
10148 ping_pHYs_y_resolution,
10149 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010150
10151 if (logging)
10152 {
10153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10154 " Setting up pHYs chunk");
10155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10156 " x_resolution=%lu",
10157 (unsigned long) ping_pHYs_x_resolution);
10158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10159 " y_resolution=%lu",
10160 (unsigned long) ping_pHYs_y_resolution);
10161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10162 " unit_type=%lu",
10163 (unsigned long) ping_pHYs_unit_type);
10164 }
glennrp26f37912010-12-23 16:22:42 +000010165 }
glennrpdfd70802010-11-14 01:23:35 +000010166 }
10167
10168#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010169 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010170 {
glennrp26f37912010-12-23 16:22:42 +000010171 if (image->page.x || image->page.y)
10172 {
10173 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10174 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010175
glennrp26f37912010-12-23 16:22:42 +000010176 if (logging != MagickFalse)
10177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10178 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10179 (int) image->page.x, (int) image->page.y);
10180 }
glennrpdfd70802010-11-14 01:23:35 +000010181 }
10182#endif
10183
glennrpda8f3a72011-02-27 23:54:12 +000010184 if (mng_info->need_blob != MagickFalse)
10185 {
cristy16ea1392012-03-21 20:38:41 +000010186 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
glennrpda8f3a72011-02-27 23:54:12 +000010187 MagickFalse)
10188 png_error(ping,"WriteBlob Failed");
10189
10190 ping_have_blob=MagickTrue;
10191 }
10192
cristy3ed852e2009-09-05 21:47:34 +000010193 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010194
glennrp39992b42010-11-14 00:03:43 +000010195 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010196 {
glennrp3b51f0e2010-11-27 18:14:08 +000010197 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010198 {
10199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10200 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10201 }
10202
10203 if (ping_color_type == 3)
10204 (void) png_set_tRNS(ping, ping_info,
10205 ping_trans_alpha,
10206 ping_num_trans,
10207 NULL);
10208
10209 else
10210 {
10211 (void) png_set_tRNS(ping, ping_info,
10212 NULL,
10213 0,
10214 &ping_trans_color);
10215
glennrp3b51f0e2010-11-27 18:14:08 +000010216 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010217 {
10218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010219 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010220 (int) ping_trans_color.red,
10221 (int) ping_trans_color.green,
10222 (int) ping_trans_color.blue);
10223 }
10224 }
glennrp991d11d2010-11-12 21:55:28 +000010225 }
10226
cristy3ed852e2009-09-05 21:47:34 +000010227 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010228 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010229
cristy3ed852e2009-09-05 21:47:34 +000010230 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010231
cristy3ed852e2009-09-05 21:47:34 +000010232 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010233 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010234
glennrp26f37912010-12-23 16:22:42 +000010235 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010236 {
glennrp4f25bd02011-01-01 18:51:28 +000010237 if ((image->page.width != 0 && image->page.width != image->columns) ||
10238 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010239 {
10240 unsigned char
10241 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010242
glennrp26f37912010-12-23 16:22:42 +000010243 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10244 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010245 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010246 PNGLong(chunk+4,(png_uint_32) image->page.width);
10247 PNGLong(chunk+8,(png_uint_32) image->page.height);
10248 chunk[12]=0; /* unit = pixels */
10249 (void) WriteBlob(image,13,chunk);
10250 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10251 }
cristy3ed852e2009-09-05 21:47:34 +000010252 }
10253
10254#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010255 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010256#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010257 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010258#undef PNG_HAVE_IDAT
10259#endif
10260
10261 png_set_packing(ping);
10262 /*
10263 Allocate memory.
10264 */
10265 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010266 if (image_depth > 8)
10267 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010268 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010269 {
glennrpb4a13412010-05-05 12:47:19 +000010270 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010271 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010272 break;
glennrp0fe50b42010-11-16 03:52:51 +000010273
glennrpb4a13412010-05-05 12:47:19 +000010274 case PNG_COLOR_TYPE_GRAY_ALPHA:
10275 rowbytes*=2;
10276 break;
glennrp0fe50b42010-11-16 03:52:51 +000010277
glennrpb4a13412010-05-05 12:47:19 +000010278 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010279 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010280 break;
glennrp0fe50b42010-11-16 03:52:51 +000010281
glennrpb4a13412010-05-05 12:47:19 +000010282 default:
10283 break;
cristy3ed852e2009-09-05 21:47:34 +000010284 }
glennrp3b51f0e2010-11-27 18:14:08 +000010285
10286 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010287 {
10288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10289 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010290
glennrpb4a13412010-05-05 12:47:19 +000010291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010292 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010293 }
glennrpcf002022011-01-30 02:38:15 +000010294 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10295 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010296
glennrpcf002022011-01-30 02:38:15 +000010297 if (ping_pixels == (unsigned char *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010298 png_error(ping,"Allocation of memory for pixels failed");
glennrp0fe50b42010-11-16 03:52:51 +000010299
cristy3ed852e2009-09-05 21:47:34 +000010300 /*
10301 Initialize image scanlines.
10302 */
cristyed552522009-10-16 14:04:35 +000010303 quantum_info=AcquireQuantumInfo(image_info,image);
10304 if (quantum_info == (QuantumInfo *) NULL)
glennrpedaa0382012-04-12 14:16:21 +000010305 png_error(ping,"Memory allocation for quantum_info failed");
cristy3ed852e2009-09-05 21:47:34 +000010306 quantum_info->format=UndefinedQuantumFormat;
10307 quantum_info->depth=image_depth;
10308 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010309
cristy3ed852e2009-09-05 21:47:34 +000010310 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010311 !mng_info->write_png32) &&
10312 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010313 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010314 image_matte == MagickFalse &&
10315 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010316 {
glennrp8bb3a022010-12-13 20:40:04 +000010317 /* Palette, Bilevel, or Opaque Monochrome */
cristy16ea1392012-03-21 20:38:41 +000010318 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010319 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010320
cristy3ed852e2009-09-05 21:47:34 +000010321 quantum_info->depth=8;
10322 for (pass=0; pass < num_passes; pass++)
10323 {
10324 /*
10325 Convert PseudoClass image to a PNG monochrome image.
10326 */
cristybb503372010-05-27 20:51:26 +000010327 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010328 {
glennrpd71e86a2011-02-24 01:28:37 +000010329 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10331 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010332
cristy16ea1392012-03-21 20:38:41 +000010333 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010334
cristy16ea1392012-03-21 20:38:41 +000010335 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010336 break;
glennrp0fe50b42010-11-16 03:52:51 +000010337
cristy3ed852e2009-09-05 21:47:34 +000010338 if (mng_info->IsPalette)
10339 {
cristy16ea1392012-03-21 20:38:41 +000010340 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10341 quantum_info,GrayQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010342 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10343 mng_info->write_png_depth &&
10344 mng_info->write_png_depth != old_bit_depth)
10345 {
10346 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010347 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010348 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010349 >> (8-old_bit_depth));
10350 }
10351 }
glennrp0fe50b42010-11-16 03:52:51 +000010352
cristy3ed852e2009-09-05 21:47:34 +000010353 else
10354 {
cristy16ea1392012-03-21 20:38:41 +000010355 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10356 quantum_info,RedQuantum,ping_pixels,exception);
cristy3ed852e2009-09-05 21:47:34 +000010357 }
glennrp0fe50b42010-11-16 03:52:51 +000010358
cristy3ed852e2009-09-05 21:47:34 +000010359 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010360 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010361 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010362 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010363
glennrp3b51f0e2010-11-27 18:14:08 +000010364 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10366 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010367
glennrpcf002022011-01-30 02:38:15 +000010368 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010369 }
10370 if (image->previous == (Image *) NULL)
10371 {
10372 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10373 if (status == MagickFalse)
10374 break;
10375 }
10376 }
10377 }
glennrp0fe50b42010-11-16 03:52:51 +000010378
glennrp8bb3a022010-12-13 20:40:04 +000010379 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010380 {
glennrp0fe50b42010-11-16 03:52:51 +000010381 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010382 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010383 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010384 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010385 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010386 {
cristy16ea1392012-03-21 20:38:41 +000010387 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010388 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010389
glennrp8bb3a022010-12-13 20:40:04 +000010390 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010391 {
glennrp8bb3a022010-12-13 20:40:04 +000010392
cristybb503372010-05-27 20:51:26 +000010393 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010394 {
cristy16ea1392012-03-21 20:38:41 +000010395 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010396
cristy16ea1392012-03-21 20:38:41 +000010397 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010398 break;
glennrp2cc891a2010-12-24 13:44:32 +000010399
glennrp5af765f2010-03-30 11:12:18 +000010400 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010401 {
glennrp8bb3a022010-12-13 20:40:04 +000010402 if (mng_info->IsPalette)
cristy16ea1392012-03-21 20:38:41 +000010403 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10404 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010405
glennrp8bb3a022010-12-13 20:40:04 +000010406 else
cristy16ea1392012-03-21 20:38:41 +000010407 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10408 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010409
glennrp3b51f0e2010-11-27 18:14:08 +000010410 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010412 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010413 }
glennrp2cc891a2010-12-24 13:44:32 +000010414
glennrp8bb3a022010-12-13 20:40:04 +000010415 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10416 {
10417 if (logging != MagickFalse && y == 0)
10418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10419 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010420
cristy16ea1392012-03-21 20:38:41 +000010421 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10422 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010423 }
glennrp2cc891a2010-12-24 13:44:32 +000010424
glennrp3b51f0e2010-11-27 18:14:08 +000010425 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010427 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010428
glennrpcf002022011-01-30 02:38:15 +000010429 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010430 }
glennrp2cc891a2010-12-24 13:44:32 +000010431
glennrp8bb3a022010-12-13 20:40:04 +000010432 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010433 {
glennrp8bb3a022010-12-13 20:40:04 +000010434 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10435 if (status == MagickFalse)
10436 break;
cristy3ed852e2009-09-05 21:47:34 +000010437 }
cristy3ed852e2009-09-05 21:47:34 +000010438 }
10439 }
glennrp8bb3a022010-12-13 20:40:04 +000010440
10441 else
10442 {
cristy16ea1392012-03-21 20:38:41 +000010443 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010444 *p;
10445
10446 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010447 {
glennrp8bb3a022010-12-13 20:40:04 +000010448 if ((image_depth > 8) || (mng_info->write_png24 ||
10449 mng_info->write_png32 ||
10450 (!mng_info->write_png8 && !mng_info->IsPalette)))
10451 {
10452 for (y=0; y < (ssize_t) image->rows; y++)
10453 {
10454 p=GetVirtualPixels(image,0,y,image->columns,1,
cristy16ea1392012-03-21 20:38:41 +000010455 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010456
cristy16ea1392012-03-21 20:38:41 +000010457 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010458 break;
glennrp2cc891a2010-12-24 13:44:32 +000010459
glennrp8bb3a022010-12-13 20:40:04 +000010460 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10461 {
10462 if (image->storage_class == DirectClass)
cristy16ea1392012-03-21 20:38:41 +000010463 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10464 quantum_info,RedQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010465
glennrp8bb3a022010-12-13 20:40:04 +000010466 else
cristy16ea1392012-03-21 20:38:41 +000010467 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10468 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp8bb3a022010-12-13 20:40:04 +000010469 }
glennrp2cc891a2010-12-24 13:44:32 +000010470
glennrp8bb3a022010-12-13 20:40:04 +000010471 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10472 {
cristy16ea1392012-03-21 20:38:41 +000010473 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010474 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000010475 exception);
glennrp2cc891a2010-12-24 13:44:32 +000010476
glennrp8bb3a022010-12-13 20:40:04 +000010477 if (logging != MagickFalse && y == 0)
10478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10479 " Writing GRAY_ALPHA PNG pixels (3)");
10480 }
glennrp2cc891a2010-12-24 13:44:32 +000010481
glennrp8bb3a022010-12-13 20:40:04 +000010482 else if (image_matte != MagickFalse)
cristy16ea1392012-03-21 20:38:41 +000010483 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10484 quantum_info,RGBAQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010485
glennrp8bb3a022010-12-13 20:40:04 +000010486 else
cristy16ea1392012-03-21 20:38:41 +000010487 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10488 quantum_info,RGBQuantum,ping_pixels,exception);
glennrp2cc891a2010-12-24 13:44:32 +000010489
glennrp8bb3a022010-12-13 20:40:04 +000010490 if (logging != MagickFalse && y == 0)
10491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10492 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010493
glennrpcf002022011-01-30 02:38:15 +000010494 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010495 }
10496 }
glennrp2cc891a2010-12-24 13:44:32 +000010497
glennrp8bb3a022010-12-13 20:40:04 +000010498 else
10499 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10500 mng_info->write_png32 ||
10501 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10502 {
10503 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10504 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10505 {
10506 if (logging != MagickFalse)
10507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10508 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010509
glennrp8bb3a022010-12-13 20:40:04 +000010510 quantum_info->depth=8;
10511 image_depth=8;
10512 }
glennrp2cc891a2010-12-24 13:44:32 +000010513
glennrp8bb3a022010-12-13 20:40:04 +000010514 for (y=0; y < (ssize_t) image->rows; y++)
10515 {
10516 if (logging != MagickFalse && y == 0)
10517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10518 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010519
cristy16ea1392012-03-21 20:38:41 +000010520 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
glennrp2cc891a2010-12-24 13:44:32 +000010521
cristy16ea1392012-03-21 20:38:41 +000010522 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010523 break;
glennrp2cc891a2010-12-24 13:44:32 +000010524
glennrp8bb3a022010-12-13 20:40:04 +000010525 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010526 {
glennrp4bf89732011-03-21 13:48:28 +000010527 quantum_info->depth=image->depth;
10528
cristy16ea1392012-03-21 20:38:41 +000010529 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10530 quantum_info,GrayQuantum,ping_pixels,exception);
glennrp44757ab2011-03-17 12:57:03 +000010531 }
glennrp2cc891a2010-12-24 13:44:32 +000010532
glennrp8bb3a022010-12-13 20:40:04 +000010533 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10534 {
10535 if (logging != MagickFalse && y == 0)
10536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10537 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010538
cristy16ea1392012-03-21 20:38:41 +000010539 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010540 quantum_info,GrayAlphaQuantum,ping_pixels,
cristy16ea1392012-03-21 20:38:41 +000010541 exception);
glennrp8bb3a022010-12-13 20:40:04 +000010542 }
glennrp2cc891a2010-12-24 13:44:32 +000010543
glennrp8bb3a022010-12-13 20:40:04 +000010544 else
glennrp8bb3a022010-12-13 20:40:04 +000010545 {
cristy16ea1392012-03-21 20:38:41 +000010546 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10547 quantum_info,IndexQuantum,ping_pixels,exception);
glennrp5eae7602011-02-22 15:21:32 +000010548
10549 if (logging != MagickFalse && y <= 2)
10550 {
10551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010552 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010553
10554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10555 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10556 (int)ping_pixels[0],(int)ping_pixels[1]);
10557 }
glennrp8bb3a022010-12-13 20:40:04 +000010558 }
glennrpcf002022011-01-30 02:38:15 +000010559 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010560 }
10561 }
glennrp2cc891a2010-12-24 13:44:32 +000010562
glennrp8bb3a022010-12-13 20:40:04 +000010563 if (image->previous == (Image *) NULL)
10564 {
10565 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10566 if (status == MagickFalse)
10567 break;
10568 }
cristy3ed852e2009-09-05 21:47:34 +000010569 }
glennrp8bb3a022010-12-13 20:40:04 +000010570 }
10571 }
10572
cristyb32b90a2009-09-07 21:45:48 +000010573 if (quantum_info != (QuantumInfo *) NULL)
10574 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010575
10576 if (logging != MagickFalse)
10577 {
10578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010579 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010580
cristy3ed852e2009-09-05 21:47:34 +000010581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010582 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010583
cristy3ed852e2009-09-05 21:47:34 +000010584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010585 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010586
cristy3ed852e2009-09-05 21:47:34 +000010587 if (mng_info->write_png_depth)
10588 {
10589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010590 " Defined png:bit-depth: %d",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010591 }
glennrp0fe50b42010-11-16 03:52:51 +000010592
cristy3ed852e2009-09-05 21:47:34 +000010593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010594 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010595
cristy3ed852e2009-09-05 21:47:34 +000010596 if (mng_info->write_png_colortype)
10597 {
10598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000010599 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010600 }
glennrp0fe50b42010-11-16 03:52:51 +000010601
cristy3ed852e2009-09-05 21:47:34 +000010602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010603 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010604
cristy3ed852e2009-09-05 21:47:34 +000010605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010606 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010607 }
10608 /*
glennrpa0ed0092011-04-18 16:36:29 +000010609 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010610 */
glennrp823b55c2011-03-14 18:46:46 +000010611 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010612 {
glennrp26f37912010-12-23 16:22:42 +000010613 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010614 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010615 while (property != (const char *) NULL)
10616 {
10617 png_textp
10618 text;
glennrp2cc891a2010-12-24 13:44:32 +000010619
cristy16ea1392012-03-21 20:38:41 +000010620 value=GetImageProperty(image,property,exception);
glennrpa0ed0092011-04-18 16:36:29 +000010621
10622 /* Don't write any "png:" properties; those are just for "identify" */
10623 if (LocaleNCompare(property,"png:",4) != 0 &&
10624
10625 /* Suppress density and units if we wrote a pHYs chunk */
10626 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010627 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010628 LocaleCompare(property,"units") != 0) &&
10629
10630 /* Suppress the IM-generated Date:create and Date:modify */
10631 (ping_exclude_date == MagickFalse ||
10632 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010633 {
glennrpc70af4a2011-03-07 00:08:23 +000010634 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010635 {
glennrpc70af4a2011-03-07 00:08:23 +000010636 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10637 text[0].key=(char *) property;
10638 text[0].text=(char *) value;
10639 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010640
glennrpc70af4a2011-03-07 00:08:23 +000010641 if (ping_exclude_tEXt != MagickFalse)
10642 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10643
10644 else if (ping_exclude_zTXt != MagickFalse)
10645 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10646
10647 else
glennrp26f37912010-12-23 16:22:42 +000010648 {
glennrpc70af4a2011-03-07 00:08:23 +000010649 text[0].compression=image_info->compression == NoCompression ||
10650 (image_info->compression == UndefinedCompression &&
10651 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10652 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010653 }
glennrp2cc891a2010-12-24 13:44:32 +000010654
glennrpc70af4a2011-03-07 00:08:23 +000010655 if (logging != MagickFalse)
10656 {
10657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10658 " Setting up text chunk");
10659
10660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10661 " keyword: %s",text[0].key);
10662 }
10663
10664 png_set_text(ping,ping_info,text,1);
10665 png_free(ping,text);
10666 }
glennrp26f37912010-12-23 16:22:42 +000010667 }
10668 property=GetNextImageProperty(image);
10669 }
cristy3ed852e2009-09-05 21:47:34 +000010670 }
10671
10672 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010673 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010674
10675 if (logging != MagickFalse)
10676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10677 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010678
cristy3ed852e2009-09-05 21:47:34 +000010679 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010680
cristy3ed852e2009-09-05 21:47:34 +000010681 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10682 {
10683 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010684 (ping_width != mng_info->page.width) ||
10685 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010686 {
10687 unsigned char
10688 chunk[32];
10689
10690 /*
10691 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10692 */
10693 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10694 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010695 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010696 chunk[4]=4;
10697 chunk[5]=0; /* frame name separator (no name) */
10698 chunk[6]=1; /* flag for changing delay, for next frame only */
10699 chunk[7]=0; /* flag for changing frame timeout */
10700 chunk[8]=1; /* flag for changing frame clipping for next frame */
10701 chunk[9]=0; /* flag for changing frame sync_id */
10702 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10703 chunk[14]=0; /* clipping boundaries delta type */
10704 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10705 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010706 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010707 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10708 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010709 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010710 (void) WriteBlob(image,31,chunk);
10711 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10712 mng_info->old_framing_mode=4;
10713 mng_info->framing_mode=1;
10714 }
glennrp0fe50b42010-11-16 03:52:51 +000010715
cristy3ed852e2009-09-05 21:47:34 +000010716 else
10717 mng_info->framing_mode=3;
10718 }
10719 if (mng_info->write_mng && !mng_info->need_fram &&
10720 ((int) image->dispose == 3))
glennrpedaa0382012-04-12 14:16:21 +000010721 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
glennrp0fe50b42010-11-16 03:52:51 +000010722
cristy3ed852e2009-09-05 21:47:34 +000010723 /*
10724 Free PNG resources.
10725 */
glennrp5af765f2010-03-30 11:12:18 +000010726
cristy3ed852e2009-09-05 21:47:34 +000010727 png_destroy_write_struct(&ping,&ping_info);
10728
glennrpcf002022011-01-30 02:38:15 +000010729 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010730
cristy16ea1392012-03-21 20:38:41 +000010731 if (ping_have_blob != MagickFalse)
10732 (void) CloseBlob(image);
10733
10734 image_info=DestroyImageInfo(image_info);
10735 image=DestroyImage(image);
10736
glennrpb9cfe272010-12-21 15:08:06 +000010737 /* Store bit depth actually written */
10738 s[0]=(char) ping_bit_depth;
10739 s[1]='\0';
10740
cristy16ea1392012-03-21 20:38:41 +000010741 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
glennrpb9cfe272010-12-21 15:08:06 +000010742
cristy3ed852e2009-09-05 21:47:34 +000010743 if (logging != MagickFalse)
10744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10745 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010746
glennrpedaa0382012-04-12 14:16:21 +000010747#ifdef PNG_SETJMP_NOT_THREAD_SAFE
10748 UnlockSemaphoreInfo(ping_semaphore);
10749#endif
10750
10751 /* } for navigation to beginning of SETJMP-protected block. Revert to
10752 * Throwing an Exception when an error occurs.
10753 */
10754
cristy3ed852e2009-09-05 21:47:34 +000010755 return(MagickTrue);
10756/* End write one PNG image */
glennrpedaa0382012-04-12 14:16:21 +000010757
cristy3ed852e2009-09-05 21:47:34 +000010758}
10759
10760/*
10761%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10762% %
10763% %
10764% %
10765% W r i t e P N G I m a g e %
10766% %
10767% %
10768% %
10769%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10770%
10771% WritePNGImage() writes a Portable Network Graphics (PNG) or
10772% Multiple-image Network Graphics (MNG) image file.
10773%
10774% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10775%
10776% The format of the WritePNGImage method is:
10777%
cristy16ea1392012-03-21 20:38:41 +000010778% MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10779% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010780%
10781% A description of each parameter follows:
10782%
10783% o image_info: the image info.
10784%
10785% o image: The image.
10786%
cristy16ea1392012-03-21 20:38:41 +000010787% o exception: return any errors or warnings in this structure.
10788%
cristy3ed852e2009-09-05 21:47:34 +000010789% Returns MagickTrue on success, MagickFalse on failure.
10790%
10791% Communicating with the PNG encoder:
10792%
10793% While the datastream written is always in PNG format and normally would
10794% be given the "png" file extension, this method also writes the following
cristy5d6fc9c2011-12-27 03:10:42 +000010795% pseudo-formats which are subsets of png:
cristy3ed852e2009-09-05 21:47:34 +000010796%
glennrp5a39f372011-02-25 04:52:16 +000010797% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10798% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010799% is present, the tRNS chunk must only have values 0 and 255
10800% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010801% transparent). If other values are present they will be
10802% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010803% colors are present, they will be quantized to the 4-4-4-1,
glennrp130fc452011-08-20 03:43:18 +000010804% 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
10805% of any resulting fully-transparent pixels is changed to
10806% the image's background color.
glennrpe9637cb2011-03-24 16:34:37 +000010807%
10808% If you want better quantization or dithering of the colors
10809% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010810% PNG encoder. The pixels contain 8-bit indices even if
10811% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010812% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010813% PNG grayscale type might be slightly more efficient. Please
10814% note that writing to the PNG8 format may result in loss
10815% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010816%
10817% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10818% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010819% one of the colors as transparent. The only loss incurred
10820% is reduction of sample depth to 8. If the image has more
10821% than one transparent color, has semitransparent pixels, or
10822% has an opaque pixel with the same RGB components as the
10823% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010824%
10825% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10826% transparency is permitted, i.e., the alpha sample for
10827% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010828% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010829% The only loss in data is the reduction of the sample depth
10830% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010831%
10832% o -define: For more precise control of the PNG output, you can use the
10833% Image options "png:bit-depth" and "png:color-type". These
10834% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010835% from the application programming interfaces. The options
10836% are case-independent and are converted to lowercase before
10837% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010838%
10839% png:color-type can be 0, 2, 3, 4, or 6.
10840%
10841% When png:color-type is 0 (Grayscale), png:bit-depth can
10842% be 1, 2, 4, 8, or 16.
10843%
10844% When png:color-type is 2 (RGB), png:bit-depth can
10845% be 8 or 16.
10846%
10847% When png:color-type is 3 (Indexed), png:bit-depth can
10848% be 1, 2, 4, or 8. This refers to the number of bits
10849% used to store the index. The color samples always have
10850% bit-depth 8 in indexed PNG files.
10851%
10852% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10853% png:bit-depth can be 8 or 16.
10854%
glennrp5a39f372011-02-25 04:52:16 +000010855% If the image cannot be written without loss with the requested bit-depth
10856% and color-type, a PNG file will not be written, and the encoder will
10857% return MagickFalse.
10858%
cristy3ed852e2009-09-05 21:47:34 +000010859% Since image encoders should not be responsible for the "heavy lifting",
10860% the user should make sure that ImageMagick has already reduced the
10861% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010862% transparency prior to attempting to write the image with depth, color,
cristy16ea1392012-03-21 20:38:41 +000010863% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010864%
cristy3ed852e2009-09-05 21:47:34 +000010865% Note that another definition, "png:bit-depth-written" exists, but it
10866% is not intended for external use. It is only used internally by the
10867% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10868%
10869% It is possible to request that the PNG encoder write previously-formatted
10870% ancillary chunks in the output PNG file, using the "-profile" commandline
10871% option as shown below or by setting the profile via a programming
10872% interface:
10873%
10874% -profile PNG-chunk-x:<file>
10875%
10876% where x is a location flag and <file> is a file containing the chunk
10877% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010878% This encoder will compute the chunk length and CRC, so those must not
10879% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010880%
10881% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10882% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10883% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010884% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010885%
glennrpbb8a7332010-11-13 15:17:35 +000010886% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010887%
glennrp3241bd02010-12-12 04:36:28 +000010888% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010889%
glennrpd6afd542010-11-19 01:53:05 +000010890% o 32-bit depth is reduced to 16.
10891% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10892% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010893% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010894% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010895% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010896% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10897% this can be done without loss and a larger bit depth N was not
cristy5d6fc9c2011-12-27 03:10:42 +000010898% requested via the "-define png:bit-depth=N" option.
glennrpd6afd542010-11-19 01:53:05 +000010899% o If matte channel is present but only one transparent color is
10900% present, RGB+tRNS is written instead of RGBA
10901% o Opaque matte channel is removed (or added, if color-type 4 or 6
10902% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010903%
cristy3ed852e2009-09-05 21:47:34 +000010904%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10905*/
10906static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
cristy16ea1392012-03-21 20:38:41 +000010907 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000010908{
10909 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010910 excluding,
10911 logging,
10912 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010913 status;
10914
10915 MngInfo
10916 *mng_info;
10917
10918 const char
10919 *value;
10920
10921 int
glennrp21f0e622011-01-07 16:20:57 +000010922 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010923 source;
10924
cristy3ed852e2009-09-05 21:47:34 +000010925 /*
10926 Open image file.
10927 */
10928 assert(image_info != (const ImageInfo *) NULL);
10929 assert(image_info->signature == MagickSignature);
10930 assert(image != (Image *) NULL);
10931 assert(image->signature == MagickSignature);
10932 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010933 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010934 /*
10935 Allocate a MngInfo structure.
10936 */
10937 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010938 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010939
cristy3ed852e2009-09-05 21:47:34 +000010940 if (mng_info == (MngInfo *) NULL)
10941 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010942
cristy3ed852e2009-09-05 21:47:34 +000010943 /*
10944 Initialize members of the MngInfo structure.
10945 */
10946 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10947 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010948 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010949 have_mng_structure=MagickTrue;
10950
10951 /* See if user has requested a specific PNG subformat */
10952
10953 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10954 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10955 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10956
glennrpb381a262012-02-11 17:49:49 +000010957 value=GetImageOption(image_info,"png:format");
10958
10959 if (value != (char *) NULL)
10960 {
10961 if (LocaleCompare(value,"png8") == 0)
10962 {
10963 mng_info->write_png8 = MagickTrue;
10964 mng_info->write_png24 = MagickFalse;
10965 mng_info->write_png32 = MagickFalse;
10966 }
10967
10968 else if (LocaleCompare(value,"png24") == 0)
10969 {
10970 mng_info->write_png8 = MagickFalse;
10971 mng_info->write_png24 = MagickTrue;
10972 mng_info->write_png32 = MagickFalse;
10973 }
10974
10975 else if (LocaleCompare(value,"png32") == 0)
10976 {
10977 mng_info->write_png8 = MagickFalse;
10978 mng_info->write_png24 = MagickFalse;
10979 mng_info->write_png32 = MagickTrue;
10980 }
10981 }
cristy3ed852e2009-09-05 21:47:34 +000010982 if (mng_info->write_png8)
10983 {
glennrp9c1eb072010-06-06 22:19:15 +000010984 mng_info->write_png_colortype = /* 3 */ 4;
10985 mng_info->write_png_depth = 8;
10986 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010987 }
10988
10989 if (mng_info->write_png24)
10990 {
glennrp9c1eb072010-06-06 22:19:15 +000010991 mng_info->write_png_colortype = /* 2 */ 3;
10992 mng_info->write_png_depth = 8;
10993 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010994
glennrp9c1eb072010-06-06 22:19:15 +000010995 if (image->matte == MagickTrue)
cristy16ea1392012-03-21 20:38:41 +000010996 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000010997
glennrp9c1eb072010-06-06 22:19:15 +000010998 else
cristy16ea1392012-03-21 20:38:41 +000010999 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011000
cristy16ea1392012-03-21 20:38:41 +000011001 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011002 }
11003
11004 if (mng_info->write_png32)
11005 {
glennrp9c1eb072010-06-06 22:19:15 +000011006 mng_info->write_png_colortype = /* 6 */ 7;
11007 mng_info->write_png_depth = 8;
11008 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011009
glennrp9c1eb072010-06-06 22:19:15 +000011010 if (image->matte == MagickTrue)
cristy16ea1392012-03-21 20:38:41 +000011011 (void) SetImageType(image,TrueColorMatteType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011012
glennrp9c1eb072010-06-06 22:19:15 +000011013 else
cristy16ea1392012-03-21 20:38:41 +000011014 (void) SetImageType(image,TrueColorType,exception);
glennrp0fe50b42010-11-16 03:52:51 +000011015
cristy16ea1392012-03-21 20:38:41 +000011016 (void) SyncImage(image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011017 }
11018
11019 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000011020
cristy3ed852e2009-09-05 21:47:34 +000011021 if (value != (char *) NULL)
11022 {
11023 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011024 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011025
cristy3ed852e2009-09-05 21:47:34 +000011026 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011027 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000011028
cristy3ed852e2009-09-05 21:47:34 +000011029 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011030 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011031
cristy3ed852e2009-09-05 21:47:34 +000011032 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011033 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000011034
cristy3ed852e2009-09-05 21:47:34 +000011035 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011036 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000011037
glennrpbb8a7332010-11-13 15:17:35 +000011038 else
cristy16ea1392012-03-21 20:38:41 +000011039 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011040 GetMagickModule(),CoderWarning,
11041 "ignoring invalid defined png:bit-depth",
11042 "=%s",value);
11043
cristy3ed852e2009-09-05 21:47:34 +000011044 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011045 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000011046 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000011047 }
glennrp0fe50b42010-11-16 03:52:51 +000011048
cristy3ed852e2009-09-05 21:47:34 +000011049 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000011050
cristy3ed852e2009-09-05 21:47:34 +000011051 if (value != (char *) NULL)
11052 {
11053 /* We must store colortype+1 because 0 is a valid colortype */
11054 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011055 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000011056
cristy16ea1392012-03-21 20:38:41 +000011057 else if (LocaleCompare(value,"1") == 0)
11058 mng_info->write_png_colortype = 2;
11059
cristy3ed852e2009-09-05 21:47:34 +000011060 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011061 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000011062
cristy3ed852e2009-09-05 21:47:34 +000011063 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011064 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000011065
cristy3ed852e2009-09-05 21:47:34 +000011066 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011067 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000011068
cristy3ed852e2009-09-05 21:47:34 +000011069 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000011070 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000011071
glennrpbb8a7332010-11-13 15:17:35 +000011072 else
cristy16ea1392012-03-21 20:38:41 +000011073 (void) ThrowMagickException(exception,
glennrpbb8a7332010-11-13 15:17:35 +000011074 GetMagickModule(),CoderWarning,
11075 "ignoring invalid defined png:color-type",
11076 "=%s",value);
11077
cristy3ed852e2009-09-05 21:47:34 +000011078 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000011079 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000011080 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000011081 }
11082
glennrp0e8ea192010-12-24 18:00:33 +000011083 /* Check for chunks to be excluded:
11084 *
glennrp0dff56c2011-01-29 19:10:02 +000011085 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000011086 * listed in the "unused_chunks" array, above.
11087 *
cristy5d6fc9c2011-12-27 03:10:42 +000011088 * Chunks can be listed for exclusion via a "png:exclude-chunk"
glennrp0e8ea192010-12-24 18:00:33 +000011089 * define (in the image properties or in the image artifacts)
11090 * or via a mng_info member. For convenience, in addition
11091 * to or instead of a comma-separated list of chunks, the
11092 * "exclude-chunk" string can be simply "all" or "none".
11093 *
11094 * The exclude-chunk define takes priority over the mng_info.
11095 *
cristy5d6fc9c2011-12-27 03:10:42 +000011096 * A "png:include-chunk" define takes priority over both the
11097 * mng_info and the "png:exclude-chunk" define. Like the
glennrp0e8ea192010-12-24 18:00:33 +000011098 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000011099 * well as a comma-separated list. Chunks that are unknown to
11100 * ImageMagick are always excluded, regardless of their "copy-safe"
11101 * status according to the PNG specification, and even if they
glennrpaa192b12012-01-17 21:35:21 +000011102 * appear in the "include-chunk" list. Such defines appearing among
11103 * the image options take priority over those found among the image
11104 * artifacts.
glennrp0e8ea192010-12-24 18:00:33 +000011105 *
11106 * Finally, all chunks listed in the "unused_chunks" array are
11107 * automatically excluded, regardless of the other instructions
11108 * or lack thereof.
11109 *
11110 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11111 * will not be written and the gAMA chunk will only be written if it
11112 * is not between .45 and .46, or approximately (1.0/2.2).
11113 *
11114 * If you exclude tRNS and the image has transparency, the colortype
11115 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11116 *
11117 * The -strip option causes StripImage() to set the png:include-chunk
glennrp104f2062011-08-20 05:08:53 +000011118 * artifact to "none,trns,gama".
glennrp0e8ea192010-12-24 18:00:33 +000011119 */
11120
glennrp26f37912010-12-23 16:22:42 +000011121 mng_info->ping_exclude_bKGD=MagickFalse;
11122 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011123 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011124 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11125 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011126 mng_info->ping_exclude_iCCP=MagickFalse;
11127 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11128 mng_info->ping_exclude_oFFs=MagickFalse;
11129 mng_info->ping_exclude_pHYs=MagickFalse;
11130 mng_info->ping_exclude_sRGB=MagickFalse;
11131 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011132 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000011133 mng_info->ping_exclude_vpAg=MagickFalse;
11134 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11135 mng_info->ping_exclude_zTXt=MagickFalse;
11136
glennrp8d3d6e52011-04-19 04:39:51 +000011137 mng_info->ping_preserve_colormap=MagickFalse;
11138
11139 value=GetImageArtifact(image,"png:preserve-colormap");
11140 if (value == NULL)
11141 value=GetImageOption(image_info,"png:preserve-colormap");
11142 if (value != NULL)
11143 mng_info->ping_preserve_colormap=MagickTrue;
11144
glennrp18682582011-06-30 18:11:47 +000011145 /* Thes compression-level, compression-strategy, and compression-filter
11146 * defines take precedence over values from the -quality option.
11147 */
11148 value=GetImageArtifact(image,"png:compression-level");
11149 if (value == NULL)
11150 value=GetImageOption(image_info,"png:compression-level");
11151 if (value != NULL)
11152 {
glennrp18682582011-06-30 18:11:47 +000011153 /* We have to add 1 to everything because 0 is a valid input,
11154 * and we want to use 0 (the default) to mean undefined.
11155 */
11156 if (LocaleCompare(value,"0") == 0)
11157 mng_info->write_png_compression_level = 1;
11158
glennrp0ffb95c2012-01-30 21:16:22 +000011159 else if (LocaleCompare(value,"1") == 0)
glennrp18682582011-06-30 18:11:47 +000011160 mng_info->write_png_compression_level = 2;
11161
11162 else if (LocaleCompare(value,"2") == 0)
11163 mng_info->write_png_compression_level = 3;
11164
11165 else if (LocaleCompare(value,"3") == 0)
11166 mng_info->write_png_compression_level = 4;
11167
11168 else if (LocaleCompare(value,"4") == 0)
11169 mng_info->write_png_compression_level = 5;
11170
11171 else if (LocaleCompare(value,"5") == 0)
11172 mng_info->write_png_compression_level = 6;
11173
11174 else if (LocaleCompare(value,"6") == 0)
11175 mng_info->write_png_compression_level = 7;
11176
11177 else if (LocaleCompare(value,"7") == 0)
11178 mng_info->write_png_compression_level = 8;
11179
11180 else if (LocaleCompare(value,"8") == 0)
11181 mng_info->write_png_compression_level = 9;
11182
11183 else if (LocaleCompare(value,"9") == 0)
11184 mng_info->write_png_compression_level = 10;
11185
11186 else
cristy16ea1392012-03-21 20:38:41 +000011187 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011188 GetMagickModule(),CoderWarning,
11189 "ignoring invalid defined png:compression-level",
11190 "=%s",value);
11191 }
11192
11193 value=GetImageArtifact(image,"png:compression-strategy");
11194 if (value == NULL)
11195 value=GetImageOption(image_info,"png:compression-strategy");
11196 if (value != NULL)
11197 {
11198
11199 if (LocaleCompare(value,"0") == 0)
11200 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11201
11202 else if (LocaleCompare(value,"1") == 0)
11203 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11204
11205 else if (LocaleCompare(value,"2") == 0)
11206 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11207
11208 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011209#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011210 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011211#else
11212 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11213#endif
glennrp18682582011-06-30 18:11:47 +000011214
11215 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011216#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011217 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011218#else
11219 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11220#endif
glennrp18682582011-06-30 18:11:47 +000011221
11222 else
cristy16ea1392012-03-21 20:38:41 +000011223 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011224 GetMagickModule(),CoderWarning,
11225 "ignoring invalid defined png:compression-strategy",
11226 "=%s",value);
11227 }
11228
11229 value=GetImageArtifact(image,"png:compression-filter");
11230 if (value == NULL)
11231 value=GetImageOption(image_info,"png:compression-filter");
11232 if (value != NULL)
11233 {
11234
11235 /* To do: combinations of filters allowed by libpng
11236 * masks 0x08 through 0xf8
11237 *
11238 * Implement this as a comma-separated list of 0,1,2,3,4,5
11239 * where 5 is a special case meaning PNG_ALL_FILTERS.
11240 */
11241
11242 if (LocaleCompare(value,"0") == 0)
11243 mng_info->write_png_compression_filter = 1;
11244
11245 if (LocaleCompare(value,"1") == 0)
11246 mng_info->write_png_compression_filter = 2;
11247
11248 else if (LocaleCompare(value,"2") == 0)
11249 mng_info->write_png_compression_filter = 3;
11250
11251 else if (LocaleCompare(value,"3") == 0)
11252 mng_info->write_png_compression_filter = 4;
11253
11254 else if (LocaleCompare(value,"4") == 0)
11255 mng_info->write_png_compression_filter = 5;
11256
11257 else if (LocaleCompare(value,"5") == 0)
11258 mng_info->write_png_compression_filter = 6;
11259
glennrp18682582011-06-30 18:11:47 +000011260 else
cristy16ea1392012-03-21 20:38:41 +000011261 (void) ThrowMagickException(exception,
glennrp18682582011-06-30 18:11:47 +000011262 GetMagickModule(),CoderWarning,
11263 "ignoring invalid defined png:compression-filter",
11264 "=%s",value);
11265 }
11266
glennrp03812ae2010-12-24 01:31:34 +000011267 excluding=MagickFalse;
11268
glennrp5c7cf4e2010-12-24 00:30:00 +000011269 for (source=0; source<1; source++)
11270 {
11271 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011272 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011273 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011274
11275 if (value == NULL)
11276 value=GetImageArtifact(image,"png:exclude-chunks");
11277 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011278 else
glennrpacba0042010-12-24 14:27:26 +000011279 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011280 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011281
glennrpacba0042010-12-24 14:27:26 +000011282 if (value == NULL)
11283 value=GetImageOption(image_info,"png:exclude-chunks");
11284 }
11285
glennrp03812ae2010-12-24 01:31:34 +000011286 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011287 {
glennrp03812ae2010-12-24 01:31:34 +000011288
11289 size_t
11290 last;
11291
11292 excluding=MagickTrue;
11293
11294 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011295 {
11296 if (source == 0)
11297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11298 " png:exclude-chunk=%s found in image artifacts.\n", value);
11299 else
11300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11301 " png:exclude-chunk=%s found in image properties.\n", value);
11302 }
glennrp03812ae2010-12-24 01:31:34 +000011303
11304 last=strlen(value);
11305
11306 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011307 {
glennrp03812ae2010-12-24 01:31:34 +000011308
11309 if (LocaleNCompare(value+i,"all",3) == 0)
11310 {
11311 mng_info->ping_exclude_bKGD=MagickTrue;
11312 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011313 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011314 mng_info->ping_exclude_EXIF=MagickTrue;
11315 mng_info->ping_exclude_gAMA=MagickTrue;
11316 mng_info->ping_exclude_iCCP=MagickTrue;
11317 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11318 mng_info->ping_exclude_oFFs=MagickTrue;
11319 mng_info->ping_exclude_pHYs=MagickTrue;
11320 mng_info->ping_exclude_sRGB=MagickTrue;
11321 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011322 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011323 mng_info->ping_exclude_vpAg=MagickTrue;
11324 mng_info->ping_exclude_zCCP=MagickTrue;
11325 mng_info->ping_exclude_zTXt=MagickTrue;
11326 i--;
11327 }
glennrp2cc891a2010-12-24 13:44:32 +000011328
glennrp03812ae2010-12-24 01:31:34 +000011329 if (LocaleNCompare(value+i,"none",4) == 0)
11330 {
11331 mng_info->ping_exclude_bKGD=MagickFalse;
11332 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011333 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011334 mng_info->ping_exclude_EXIF=MagickFalse;
11335 mng_info->ping_exclude_gAMA=MagickFalse;
11336 mng_info->ping_exclude_iCCP=MagickFalse;
11337 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11338 mng_info->ping_exclude_oFFs=MagickFalse;
11339 mng_info->ping_exclude_pHYs=MagickFalse;
11340 mng_info->ping_exclude_sRGB=MagickFalse;
11341 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011342 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011343 mng_info->ping_exclude_vpAg=MagickFalse;
11344 mng_info->ping_exclude_zCCP=MagickFalse;
11345 mng_info->ping_exclude_zTXt=MagickFalse;
11346 }
glennrp2cc891a2010-12-24 13:44:32 +000011347
glennrp03812ae2010-12-24 01:31:34 +000011348 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11349 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011350
glennrp03812ae2010-12-24 01:31:34 +000011351 if (LocaleNCompare(value+i,"chrm",4) == 0)
11352 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011353
glennrpa0ed0092011-04-18 16:36:29 +000011354 if (LocaleNCompare(value+i,"date",4) == 0)
11355 mng_info->ping_exclude_date=MagickTrue;
11356
glennrp03812ae2010-12-24 01:31:34 +000011357 if (LocaleNCompare(value+i,"exif",4) == 0)
11358 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011359
glennrp03812ae2010-12-24 01:31:34 +000011360 if (LocaleNCompare(value+i,"gama",4) == 0)
11361 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011362
glennrp03812ae2010-12-24 01:31:34 +000011363 if (LocaleNCompare(value+i,"iccp",4) == 0)
11364 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011365
glennrp03812ae2010-12-24 01:31:34 +000011366 /*
11367 if (LocaleNCompare(value+i,"itxt",4) == 0)
11368 mng_info->ping_exclude_iTXt=MagickTrue;
11369 */
glennrp2cc891a2010-12-24 13:44:32 +000011370
glennrp03812ae2010-12-24 01:31:34 +000011371 if (LocaleNCompare(value+i,"gama",4) == 0)
11372 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011373
glennrp03812ae2010-12-24 01:31:34 +000011374 if (LocaleNCompare(value+i,"offs",4) == 0)
11375 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011376
glennrp03812ae2010-12-24 01:31:34 +000011377 if (LocaleNCompare(value+i,"phys",4) == 0)
11378 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011379
glennrpa1e3b7b2010-12-24 16:37:33 +000011380 if (LocaleNCompare(value+i,"srgb",4) == 0)
11381 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011382
glennrp03812ae2010-12-24 01:31:34 +000011383 if (LocaleNCompare(value+i,"text",4) == 0)
11384 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011385
glennrpa1e3b7b2010-12-24 16:37:33 +000011386 if (LocaleNCompare(value+i,"trns",4) == 0)
11387 mng_info->ping_exclude_tRNS=MagickTrue;
11388
glennrp03812ae2010-12-24 01:31:34 +000011389 if (LocaleNCompare(value+i,"vpag",4) == 0)
11390 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011391
glennrp03812ae2010-12-24 01:31:34 +000011392 if (LocaleNCompare(value+i,"zccp",4) == 0)
11393 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011394
glennrp03812ae2010-12-24 01:31:34 +000011395 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11396 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011397
glennrp03812ae2010-12-24 01:31:34 +000011398 }
glennrpce91ed52010-12-23 22:37:49 +000011399 }
glennrp26f37912010-12-23 16:22:42 +000011400 }
11401
glennrp5c7cf4e2010-12-24 00:30:00 +000011402 for (source=0; source<1; source++)
11403 {
11404 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011405 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011406 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011407
11408 if (value == NULL)
11409 value=GetImageArtifact(image,"png:include-chunks");
11410 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011411 else
glennrpacba0042010-12-24 14:27:26 +000011412 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011413 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011414
glennrpacba0042010-12-24 14:27:26 +000011415 if (value == NULL)
11416 value=GetImageOption(image_info,"png:include-chunks");
11417 }
11418
glennrp03812ae2010-12-24 01:31:34 +000011419 if (value != NULL)
11420 {
11421 size_t
11422 last;
glennrp26f37912010-12-23 16:22:42 +000011423
glennrp03812ae2010-12-24 01:31:34 +000011424 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011425
glennrp03812ae2010-12-24 01:31:34 +000011426 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011427 {
11428 if (source == 0)
11429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11430 " png:include-chunk=%s found in image artifacts.\n", value);
11431 else
11432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11433 " png:include-chunk=%s found in image properties.\n", value);
11434 }
glennrp03812ae2010-12-24 01:31:34 +000011435
11436 last=strlen(value);
11437
11438 for (i=0; i<(int) last; i+=5)
11439 {
11440 if (LocaleNCompare(value+i,"all",3) == 0)
11441 {
11442 mng_info->ping_exclude_bKGD=MagickFalse;
11443 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011444 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011445 mng_info->ping_exclude_EXIF=MagickFalse;
11446 mng_info->ping_exclude_gAMA=MagickFalse;
11447 mng_info->ping_exclude_iCCP=MagickFalse;
11448 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11449 mng_info->ping_exclude_oFFs=MagickFalse;
11450 mng_info->ping_exclude_pHYs=MagickFalse;
11451 mng_info->ping_exclude_sRGB=MagickFalse;
11452 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011453 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011454 mng_info->ping_exclude_vpAg=MagickFalse;
11455 mng_info->ping_exclude_zCCP=MagickFalse;
11456 mng_info->ping_exclude_zTXt=MagickFalse;
11457 i--;
11458 }
glennrp2cc891a2010-12-24 13:44:32 +000011459
glennrp03812ae2010-12-24 01:31:34 +000011460 if (LocaleNCompare(value+i,"none",4) == 0)
11461 {
11462 mng_info->ping_exclude_bKGD=MagickTrue;
11463 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011464 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011465 mng_info->ping_exclude_EXIF=MagickTrue;
11466 mng_info->ping_exclude_gAMA=MagickTrue;
11467 mng_info->ping_exclude_iCCP=MagickTrue;
11468 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11469 mng_info->ping_exclude_oFFs=MagickTrue;
11470 mng_info->ping_exclude_pHYs=MagickTrue;
11471 mng_info->ping_exclude_sRGB=MagickTrue;
11472 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011473 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011474 mng_info->ping_exclude_vpAg=MagickTrue;
11475 mng_info->ping_exclude_zCCP=MagickTrue;
11476 mng_info->ping_exclude_zTXt=MagickTrue;
11477 }
glennrp2cc891a2010-12-24 13:44:32 +000011478
glennrp03812ae2010-12-24 01:31:34 +000011479 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11480 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011481
glennrp03812ae2010-12-24 01:31:34 +000011482 if (LocaleNCompare(value+i,"chrm",4) == 0)
11483 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011484
glennrpa0ed0092011-04-18 16:36:29 +000011485 if (LocaleNCompare(value+i,"date",4) == 0)
11486 mng_info->ping_exclude_date=MagickFalse;
11487
glennrp03812ae2010-12-24 01:31:34 +000011488 if (LocaleNCompare(value+i,"exif",4) == 0)
11489 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011490
glennrp03812ae2010-12-24 01:31:34 +000011491 if (LocaleNCompare(value+i,"gama",4) == 0)
11492 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011493
glennrp03812ae2010-12-24 01:31:34 +000011494 if (LocaleNCompare(value+i,"iccp",4) == 0)
11495 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011496
glennrp03812ae2010-12-24 01:31:34 +000011497 /*
11498 if (LocaleNCompare(value+i,"itxt",4) == 0)
11499 mng_info->ping_exclude_iTXt=MagickFalse;
11500 */
glennrp2cc891a2010-12-24 13:44:32 +000011501
glennrp03812ae2010-12-24 01:31:34 +000011502 if (LocaleNCompare(value+i,"gama",4) == 0)
11503 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011504
glennrp03812ae2010-12-24 01:31:34 +000011505 if (LocaleNCompare(value+i,"offs",4) == 0)
11506 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011507
glennrp03812ae2010-12-24 01:31:34 +000011508 if (LocaleNCompare(value+i,"phys",4) == 0)
11509 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011510
glennrpa1e3b7b2010-12-24 16:37:33 +000011511 if (LocaleNCompare(value+i,"srgb",4) == 0)
11512 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011513
glennrp03812ae2010-12-24 01:31:34 +000011514 if (LocaleNCompare(value+i,"text",4) == 0)
11515 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011516
glennrpa1e3b7b2010-12-24 16:37:33 +000011517 if (LocaleNCompare(value+i,"trns",4) == 0)
11518 mng_info->ping_exclude_tRNS=MagickFalse;
11519
glennrp03812ae2010-12-24 01:31:34 +000011520 if (LocaleNCompare(value+i,"vpag",4) == 0)
11521 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011522
glennrp03812ae2010-12-24 01:31:34 +000011523 if (LocaleNCompare(value+i,"zccp",4) == 0)
11524 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011525
glennrp03812ae2010-12-24 01:31:34 +000011526 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11527 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011528
glennrp03812ae2010-12-24 01:31:34 +000011529 }
glennrpce91ed52010-12-23 22:37:49 +000011530 }
glennrp26f37912010-12-23 16:22:42 +000011531 }
11532
glennrp03812ae2010-12-24 01:31:34 +000011533 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011534 {
11535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy5d6fc9c2011-12-27 03:10:42 +000011536 " Chunks to be excluded from the output png:");
glennrp26f37912010-12-23 16:22:42 +000011537 if (mng_info->ping_exclude_bKGD != MagickFalse)
11538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11539 " bKGD");
11540 if (mng_info->ping_exclude_cHRM != MagickFalse)
11541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11542 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011543 if (mng_info->ping_exclude_date != MagickFalse)
11544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11545 " date");
glennrp26f37912010-12-23 16:22:42 +000011546 if (mng_info->ping_exclude_EXIF != MagickFalse)
11547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11548 " EXIF");
11549 if (mng_info->ping_exclude_gAMA != MagickFalse)
11550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11551 " gAMA");
11552 if (mng_info->ping_exclude_iCCP != MagickFalse)
11553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11554 " iCCP");
11555/*
11556 if (mng_info->ping_exclude_iTXt != MagickFalse)
11557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11558 " iTXt");
11559*/
11560 if (mng_info->ping_exclude_oFFs != MagickFalse)
11561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11562 " oFFs");
11563 if (mng_info->ping_exclude_pHYs != MagickFalse)
11564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11565 " pHYs");
11566 if (mng_info->ping_exclude_sRGB != MagickFalse)
11567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11568 " sRGB");
11569 if (mng_info->ping_exclude_tEXt != MagickFalse)
11570 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11571 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011572 if (mng_info->ping_exclude_tRNS != MagickFalse)
11573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11574 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011575 if (mng_info->ping_exclude_vpAg != MagickFalse)
11576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11577 " vpAg");
11578 if (mng_info->ping_exclude_zCCP != MagickFalse)
11579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11580 " zCCP");
11581 if (mng_info->ping_exclude_zTXt != MagickFalse)
11582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11583 " zTXt");
11584 }
11585
glennrpb9cfe272010-12-21 15:08:06 +000011586 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011587
cristy16ea1392012-03-21 20:38:41 +000011588 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000011589
11590 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011591
cristy3ed852e2009-09-05 21:47:34 +000011592 if (logging != MagickFalse)
11593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011594
cristy3ed852e2009-09-05 21:47:34 +000011595 return(status);
11596}
11597
11598#if defined(JNG_SUPPORTED)
11599
11600/* Write one JNG image */
11601static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
cristy16ea1392012-03-21 20:38:41 +000011602 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000011603{
11604 Image
11605 *jpeg_image;
11606
11607 ImageInfo
11608 *jpeg_image_info;
11609
11610 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011611 logging,
cristy3ed852e2009-09-05 21:47:34 +000011612 status;
11613
11614 size_t
11615 length;
11616
11617 unsigned char
11618 *blob,
11619 chunk[80],
11620 *p;
11621
11622 unsigned int
11623 jng_alpha_compression_method,
11624 jng_alpha_sample_depth,
11625 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011626 transparent;
11627
cristybb503372010-05-27 20:51:26 +000011628 size_t
glennrp59575fa2011-12-31 21:31:39 +000011629 jng_alpha_quality,
cristy3ed852e2009-09-05 21:47:34 +000011630 jng_quality;
11631
11632 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011633 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011634
11635 blob=(unsigned char *) NULL;
11636 jpeg_image=(Image *) NULL;
11637 jpeg_image_info=(ImageInfo *) NULL;
11638
11639 status=MagickTrue;
11640 transparent=image_info->type==GrayscaleMatteType ||
glennrp59575fa2011-12-31 21:31:39 +000011641 image_info->type==TrueColorMatteType || image->matte != MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +000011642
glennrp59575fa2011-12-31 21:31:39 +000011643 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
11644
11645 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
11646
11647 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
11648 image_info->quality;
11649
11650 if (jng_alpha_quality >= 1000)
11651 jng_alpha_quality /= 1000;
cristy3ed852e2009-09-05 21:47:34 +000011652
11653 if (transparent)
11654 {
11655 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011656
cristy3ed852e2009-09-05 21:47:34 +000011657 /* Create JPEG blob, image, and image_info */
11658 if (logging != MagickFalse)
11659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy16ea1392012-03-21 20:38:41 +000011660 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011661
cristy3ed852e2009-09-05 21:47:34 +000011662 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011663
cristy3ed852e2009-09-05 21:47:34 +000011664 if (jpeg_image_info == (ImageInfo *) NULL)
11665 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011666
cristy3ed852e2009-09-05 21:47:34 +000011667 if (logging != MagickFalse)
11668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11669 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011670
cristy16ea1392012-03-21 20:38:41 +000011671 jpeg_image=SeparateImage(image,AlphaChannel,exception);
cristy3ed852e2009-09-05 21:47:34 +000011672 if (jpeg_image == (Image *) NULL)
11673 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11674 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +000011675 jpeg_image->matte=MagickFalse;
glennrp8f77fdc2012-03-21 15:16:37 +000011676 jpeg_image->quality=jng_alpha_quality;
cristy16ea1392012-03-21 20:38:41 +000011677 jpeg_image_info->type=GrayscaleType;
11678 (void) SetImageType(jpeg_image,GrayscaleType,exception);
cristy3ed852e2009-09-05 21:47:34 +000011679 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011680 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011681 "%s",jpeg_image->filename);
11682 }
glennrp59575fa2011-12-31 21:31:39 +000011683 else
11684 {
11685 jng_alpha_compression_method=0;
11686 jng_color_type=10;
11687 jng_alpha_sample_depth=0;
11688 }
cristy3ed852e2009-09-05 21:47:34 +000011689
11690 /* To do: check bit depth of PNG alpha channel */
11691
11692 /* Check if image is grayscale. */
11693 if (image_info->type != TrueColorMatteType && image_info->type !=
cristy16ea1392012-03-21 20:38:41 +000011694 TrueColorType && ImageIsGray(image,exception))
cristy3ed852e2009-09-05 21:47:34 +000011695 jng_color_type-=2;
11696
glennrp59575fa2011-12-31 21:31:39 +000011697 if (logging != MagickFalse)
11698 {
11699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11700 " JNG Quality = %d",(int) jng_quality);
11701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11702 " JNG Color Type = %d",jng_color_type);
11703 if (transparent)
11704 {
11705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11706 " JNG Alpha Compression = %d",jng_alpha_compression_method);
11707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11708 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
11709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11710 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
11711 }
11712 }
11713
cristy3ed852e2009-09-05 21:47:34 +000011714 if (transparent)
11715 {
11716 if (jng_alpha_compression_method==0)
11717 {
11718 const char
11719 *value;
11720
cristy16ea1392012-03-21 20:38:41 +000011721 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011722 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000011723 exception);
cristy3ed852e2009-09-05 21:47:34 +000011724 if (logging != MagickFalse)
11725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11726 " Creating PNG blob.");
11727 length=0;
11728
11729 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11730 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11731 jpeg_image_info->interlace=NoInterlace;
11732
glennrpcc5d45b2012-01-06 04:06:10 +000011733 /* Exclude all ancillary chunks */
11734 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
11735
cristy3ed852e2009-09-05 21:47:34 +000011736 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000011737 exception);
cristy3ed852e2009-09-05 21:47:34 +000011738
11739 /* Retrieve sample depth used */
cristy16ea1392012-03-21 20:38:41 +000011740 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
cristy3ed852e2009-09-05 21:47:34 +000011741 if (value != (char *) NULL)
11742 jng_alpha_sample_depth= (unsigned int) value[0];
11743 }
11744 else
11745 {
cristy16ea1392012-03-21 20:38:41 +000011746 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011747
11748 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000011749 exception);
cristy3ed852e2009-09-05 21:47:34 +000011750
11751 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11752 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11753 jpeg_image_info->interlace=NoInterlace;
11754 if (logging != MagickFalse)
11755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11756 " Creating blob.");
11757 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristy16ea1392012-03-21 20:38:41 +000011758 exception);
cristy3ed852e2009-09-05 21:47:34 +000011759 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011760
cristy3ed852e2009-09-05 21:47:34 +000011761 if (logging != MagickFalse)
11762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011763 " Successfully read jpeg_image into a blob, length=%.20g.",
11764 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011765
11766 }
11767 /* Destroy JPEG image and image_info */
11768 jpeg_image=DestroyImage(jpeg_image);
11769 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11770 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11771 }
11772
11773 /* Write JHDR chunk */
11774 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11775 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011776 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011777 PNGLong(chunk+4,(png_uint_32) image->columns);
11778 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011779 chunk[12]=jng_color_type;
11780 chunk[13]=8; /* sample depth */
11781 chunk[14]=8; /*jng_image_compression_method */
11782 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11783 chunk[16]=jng_alpha_sample_depth;
11784 chunk[17]=jng_alpha_compression_method;
11785 chunk[18]=0; /*jng_alpha_filter_method */
11786 chunk[19]=0; /*jng_alpha_interlace_method */
11787 (void) WriteBlob(image,20,chunk);
11788 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11789 if (logging != MagickFalse)
11790 {
11791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011792 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011793
cristy3ed852e2009-09-05 21:47:34 +000011794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011795 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011796
cristy3ed852e2009-09-05 21:47:34 +000011797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11798 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011799
cristy3ed852e2009-09-05 21:47:34 +000011800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11801 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011802
cristy3ed852e2009-09-05 21:47:34 +000011803 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11804 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011805
cristy3ed852e2009-09-05 21:47:34 +000011806 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11807 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011808
cristy3ed852e2009-09-05 21:47:34 +000011809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11810 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011811
cristy3ed852e2009-09-05 21:47:34 +000011812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11813 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011814
cristy3ed852e2009-09-05 21:47:34 +000011815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11816 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011817
cristy3ed852e2009-09-05 21:47:34 +000011818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11819 " JNG alpha interlace:%5d",0);
11820 }
11821
glennrp0fe50b42010-11-16 03:52:51 +000011822 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011823 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011824
11825 /*
11826 Write leading ancillary chunks
11827 */
11828
11829 if (transparent)
11830 {
11831 /*
11832 Write JNG bKGD chunk
11833 */
11834
11835 unsigned char
11836 blue,
11837 green,
11838 red;
11839
cristybb503372010-05-27 20:51:26 +000011840 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011841 num_bytes;
11842
11843 if (jng_color_type == 8 || jng_color_type == 12)
11844 num_bytes=6L;
11845 else
11846 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011847 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011848 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011849 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011850 red=ScaleQuantumToChar(image->background_color.red);
11851 green=ScaleQuantumToChar(image->background_color.green);
11852 blue=ScaleQuantumToChar(image->background_color.blue);
11853 *(chunk+4)=0;
11854 *(chunk+5)=red;
11855 *(chunk+6)=0;
11856 *(chunk+7)=green;
11857 *(chunk+8)=0;
11858 *(chunk+9)=blue;
11859 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11860 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11861 }
11862
11863 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11864 {
11865 /*
11866 Write JNG sRGB chunk
11867 */
11868 (void) WriteBlobMSBULong(image,1L);
11869 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011870 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011871
cristy3ed852e2009-09-05 21:47:34 +000011872 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011873 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011874 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011875 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011876
cristy3ed852e2009-09-05 21:47:34 +000011877 else
glennrpe610a072010-08-05 17:08:46 +000011878 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011879 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011880 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011881
cristy3ed852e2009-09-05 21:47:34 +000011882 (void) WriteBlob(image,5,chunk);
11883 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11884 }
11885 else
11886 {
11887 if (image->gamma != 0.0)
11888 {
11889 /*
11890 Write JNG gAMA chunk
11891 */
11892 (void) WriteBlobMSBULong(image,4L);
11893 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011894 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011895 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011896 (void) WriteBlob(image,8,chunk);
11897 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11898 }
glennrp0fe50b42010-11-16 03:52:51 +000011899
cristy3ed852e2009-09-05 21:47:34 +000011900 if ((mng_info->equal_chrms == MagickFalse) &&
11901 (image->chromaticity.red_primary.x != 0.0))
11902 {
11903 PrimaryInfo
11904 primary;
11905
11906 /*
11907 Write JNG cHRM chunk
11908 */
11909 (void) WriteBlobMSBULong(image,32L);
11910 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011911 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011912 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011913 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11914 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011915 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011916 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11917 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011918 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011919 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11920 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011921 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011922 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11923 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011924 (void) WriteBlob(image,36,chunk);
11925 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11926 }
11927 }
glennrp0fe50b42010-11-16 03:52:51 +000011928
cristy16ea1392012-03-21 20:38:41 +000011929 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000011930 {
11931 /*
11932 Write JNG pHYs chunk
11933 */
11934 (void) WriteBlobMSBULong(image,9L);
11935 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011936 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011937 if (image->units == PixelsPerInchResolution)
11938 {
cristy35ef8242010-06-03 16:24:13 +000011939 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000011940 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011941
cristy35ef8242010-06-03 16:24:13 +000011942 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000011943 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011944
cristy3ed852e2009-09-05 21:47:34 +000011945 chunk[12]=1;
11946 }
glennrp0fe50b42010-11-16 03:52:51 +000011947
cristy3ed852e2009-09-05 21:47:34 +000011948 else
11949 {
11950 if (image->units == PixelsPerCentimeterResolution)
11951 {
cristy35ef8242010-06-03 16:24:13 +000011952 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000011953 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011954
cristy35ef8242010-06-03 16:24:13 +000011955 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000011956 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011957
cristy3ed852e2009-09-05 21:47:34 +000011958 chunk[12]=1;
11959 }
glennrp0fe50b42010-11-16 03:52:51 +000011960
cristy3ed852e2009-09-05 21:47:34 +000011961 else
11962 {
cristy16ea1392012-03-21 20:38:41 +000011963 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
11964 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011965 chunk[12]=0;
11966 }
11967 }
11968 (void) WriteBlob(image,13,chunk);
11969 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11970 }
11971
11972 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11973 {
11974 /*
11975 Write JNG oFFs chunk
11976 */
11977 (void) WriteBlobMSBULong(image,9L);
11978 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011979 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011980 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11981 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011982 chunk[12]=0;
11983 (void) WriteBlob(image,13,chunk);
11984 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11985 }
11986 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11987 {
11988 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11989 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011990 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011991 PNGLong(chunk+4,(png_uint_32) image->page.width);
11992 PNGLong(chunk+8,(png_uint_32) image->page.height);
11993 chunk[12]=0; /* unit = pixels */
11994 (void) WriteBlob(image,13,chunk);
11995 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11996 }
11997
11998
11999 if (transparent)
12000 {
12001 if (jng_alpha_compression_method==0)
12002 {
cristybb503372010-05-27 20:51:26 +000012003 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012004 i;
12005
cristybb503372010-05-27 20:51:26 +000012006 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012007 len;
12008
12009 /* Write IDAT chunk header */
12010 if (logging != MagickFalse)
12011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012012 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000012013 length);
cristy3ed852e2009-09-05 21:47:34 +000012014
12015 /* Copy IDAT chunks */
12016 len=0;
12017 p=blob+8;
cristybb503372010-05-27 20:51:26 +000012018 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000012019 {
12020 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12021 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000012022
cristy3ed852e2009-09-05 21:47:34 +000012023 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12024 {
12025 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000012026 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000012027 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000012028 (void) WriteBlob(image,(size_t) len+4,p);
12029 (void) WriteBlobMSBULong(image,
12030 crc32(0,p,(uInt) len+4));
12031 }
glennrp0fe50b42010-11-16 03:52:51 +000012032
cristy3ed852e2009-09-05 21:47:34 +000012033 else
12034 {
12035 if (logging != MagickFalse)
12036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012037 " Skipping %c%c%c%c chunk, length=%.20g.",
12038 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000012039 }
12040 p+=(8+len);
12041 }
12042 }
12043 else
12044 {
12045 /* Write JDAA chunk header */
12046 if (logging != MagickFalse)
12047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012048 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000012049 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012050 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000012051 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000012052 /* Write JDAT chunk(s) data */
12053 (void) WriteBlob(image,4,chunk);
12054 (void) WriteBlob(image,length,blob);
12055 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12056 (uInt) length));
12057 }
12058 blob=(unsigned char *) RelinquishMagickMemory(blob);
12059 }
12060
12061 /* Encode image as a JPEG blob */
12062 if (logging != MagickFalse)
12063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12064 " Creating jpeg_image_info.");
12065 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12066 if (jpeg_image_info == (ImageInfo *) NULL)
12067 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12068
12069 if (logging != MagickFalse)
12070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12071 " Creating jpeg_image.");
12072
cristy16ea1392012-03-21 20:38:41 +000012073 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
cristy3ed852e2009-09-05 21:47:34 +000012074 if (jpeg_image == (Image *) NULL)
12075 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12076 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12077
12078 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000012079 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000012080 jpeg_image->filename);
12081
12082 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
cristy16ea1392012-03-21 20:38:41 +000012083 exception);
cristy3ed852e2009-09-05 21:47:34 +000012084
12085 if (logging != MagickFalse)
12086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012087 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12088 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000012089
12090 if (jng_color_type == 8 || jng_color_type == 12)
12091 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000012092
glennrp59575fa2011-12-31 21:31:39 +000012093 jpeg_image_info->quality=jng_quality;
12094 jpeg_image->quality=jng_quality;
cristy3ed852e2009-09-05 21:47:34 +000012095 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12096 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000012097
cristy3ed852e2009-09-05 21:47:34 +000012098 if (logging != MagickFalse)
12099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12100 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000012101
cristy16ea1392012-03-21 20:38:41 +000012102 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
glennrp0fe50b42010-11-16 03:52:51 +000012103
cristy3ed852e2009-09-05 21:47:34 +000012104 if (logging != MagickFalse)
12105 {
12106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012107 " Successfully read jpeg_image into a blob, length=%.20g.",
12108 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000012109
12110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012111 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000012112 }
glennrp0fe50b42010-11-16 03:52:51 +000012113
cristy3ed852e2009-09-05 21:47:34 +000012114 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000012115 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012116 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000012117 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000012118 (void) WriteBlob(image,4,chunk);
12119 (void) WriteBlob(image,length,blob);
12120 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12121
12122 jpeg_image=DestroyImage(jpeg_image);
12123 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12124 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12125 blob=(unsigned char *) RelinquishMagickMemory(blob);
12126
12127 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000012128 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000012129
12130 /* Write IEND chunk */
12131 (void) WriteBlobMSBULong(image,0L);
12132 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000012133 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000012134 (void) WriteBlob(image,4,chunk);
12135 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12136
12137 if (logging != MagickFalse)
12138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12139 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012140
cristy3ed852e2009-09-05 21:47:34 +000012141 return(status);
12142}
12143
12144
12145/*
12146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12147% %
12148% %
12149% %
12150% W r i t e J N G I m a g e %
12151% %
12152% %
12153% %
12154%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12155%
12156% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12157%
12158% JNG support written by Glenn Randers-Pehrson, glennrp@image...
12159%
12160% The format of the WriteJNGImage method is:
12161%
cristy16ea1392012-03-21 20:38:41 +000012162% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12163% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012164%
12165% A description of each parameter follows:
12166%
12167% o image_info: the image info.
12168%
12169% o image: The image.
12170%
cristy16ea1392012-03-21 20:38:41 +000012171% o exception: return any errors or warnings in this structure.
12172%
cristy3ed852e2009-09-05 21:47:34 +000012173%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12174*/
cristy16ea1392012-03-21 20:38:41 +000012175static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12176 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012177{
12178 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012179 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012180 logging,
cristy3ed852e2009-09-05 21:47:34 +000012181 status;
12182
12183 MngInfo
12184 *mng_info;
12185
cristy3ed852e2009-09-05 21:47:34 +000012186 /*
12187 Open image file.
12188 */
12189 assert(image_info != (const ImageInfo *) NULL);
12190 assert(image_info->signature == MagickSignature);
12191 assert(image != (Image *) NULL);
12192 assert(image->signature == MagickSignature);
12193 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012194 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012195 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012196 if (status == MagickFalse)
12197 return(status);
12198
12199 /*
12200 Allocate a MngInfo structure.
12201 */
12202 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012203 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012204 if (mng_info == (MngInfo *) NULL)
12205 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12206 /*
12207 Initialize members of the MngInfo structure.
12208 */
12209 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12210 mng_info->image=image;
12211 have_mng_structure=MagickTrue;
12212
12213 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12214
cristy16ea1392012-03-21 20:38:41 +000012215 status=WriteOneJNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000012216 (void) CloseBlob(image);
12217
12218 (void) CatchImageException(image);
12219 MngInfoFreeStruct(mng_info,&have_mng_structure);
12220 if (logging != MagickFalse)
12221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12222 return(status);
12223}
12224#endif
12225
cristy16ea1392012-03-21 20:38:41 +000012226static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12227 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +000012228{
12229 const char
12230 *option;
12231
12232 Image
12233 *next_image;
12234
12235 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012236 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012237 status;
12238
glennrp03812ae2010-12-24 01:31:34 +000012239 volatile MagickBooleanType
12240 logging;
12241
cristy3ed852e2009-09-05 21:47:34 +000012242 MngInfo
12243 *mng_info;
12244
12245 int
cristy3ed852e2009-09-05 21:47:34 +000012246 image_count,
12247 need_iterations,
12248 need_matte;
12249
12250 volatile int
12251#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12252 defined(PNG_MNG_FEATURES_SUPPORTED)
12253 need_local_plte,
12254#endif
12255 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012256 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012257 use_global_plte;
12258
cristybb503372010-05-27 20:51:26 +000012259 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012260 i;
12261
12262 unsigned char
12263 chunk[800];
12264
12265 volatile unsigned int
12266 write_jng,
12267 write_mng;
12268
cristybb503372010-05-27 20:51:26 +000012269 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012270 scene;
12271
cristybb503372010-05-27 20:51:26 +000012272 size_t
cristy3ed852e2009-09-05 21:47:34 +000012273 final_delay=0,
12274 initial_delay;
12275
glennrpd5045b42010-03-24 12:40:35 +000012276#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012277 if (image_info->verbose)
12278 printf("Your PNG library (libpng-%s) is rather old.\n",
12279 PNG_LIBPNG_VER_STRING);
12280#endif
12281
12282 /*
12283 Open image file.
12284 */
12285 assert(image_info != (const ImageInfo *) NULL);
12286 assert(image_info->signature == MagickSignature);
12287 assert(image != (Image *) NULL);
12288 assert(image->signature == MagickSignature);
12289 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012290 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy16ea1392012-03-21 20:38:41 +000012291 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +000012292 if (status == MagickFalse)
12293 return(status);
12294
12295 /*
12296 Allocate a MngInfo structure.
12297 */
12298 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012299 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012300 if (mng_info == (MngInfo *) NULL)
12301 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12302 /*
12303 Initialize members of the MngInfo structure.
12304 */
12305 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12306 mng_info->image=image;
12307 have_mng_structure=MagickTrue;
12308 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12309
12310 /*
12311 * See if user has requested a specific PNG subformat to be used
12312 * for all of the PNGs in the MNG being written, e.g.,
12313 *
12314 * convert *.png png8:animation.mng
12315 *
12316 * To do: check -define png:bit_depth and png:color_type as well,
12317 * or perhaps use mng:bit_depth and mng:color_type instead for
12318 * global settings.
12319 */
12320
12321 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12322 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12323 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12324
12325 write_jng=MagickFalse;
12326 if (image_info->compression == JPEGCompression)
12327 write_jng=MagickTrue;
12328
12329 mng_info->adjoin=image_info->adjoin &&
12330 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12331
cristy3ed852e2009-09-05 21:47:34 +000012332 if (logging != MagickFalse)
12333 {
12334 /* Log some info about the input */
12335 Image
12336 *p;
12337
12338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12339 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012340
cristy3ed852e2009-09-05 21:47:34 +000012341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012342 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012343
cristy3ed852e2009-09-05 21:47:34 +000012344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12345 " Type: %d",image_info->type);
12346
12347 scene=0;
12348 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12349 {
12350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012351 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012352
cristy3ed852e2009-09-05 21:47:34 +000012353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012354 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012355
cristy3ed852e2009-09-05 21:47:34 +000012356 if (p->matte)
12357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12358 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012359
cristy3ed852e2009-09-05 21:47:34 +000012360 else
12361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12362 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012363
cristy3ed852e2009-09-05 21:47:34 +000012364 if (p->storage_class == PseudoClass)
12365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12366 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012367
cristy3ed852e2009-09-05 21:47:34 +000012368 else
12369 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12370 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012371
cristy3ed852e2009-09-05 21:47:34 +000012372 if (p->colors)
12373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012374 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012375
cristy3ed852e2009-09-05 21:47:34 +000012376 else
12377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12378 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012379
cristy3ed852e2009-09-05 21:47:34 +000012380 if (mng_info->adjoin == MagickFalse)
12381 break;
12382 }
12383 }
12384
cristy3ed852e2009-09-05 21:47:34 +000012385 use_global_plte=MagickFalse;
12386 all_images_are_gray=MagickFalse;
12387#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12388 need_local_plte=MagickTrue;
12389#endif
12390 need_defi=MagickFalse;
12391 need_matte=MagickFalse;
12392 mng_info->framing_mode=1;
12393 mng_info->old_framing_mode=1;
12394
12395 if (write_mng)
12396 if (image_info->page != (char *) NULL)
12397 {
12398 /*
12399 Determine image bounding box.
12400 */
12401 SetGeometry(image,&mng_info->page);
12402 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12403 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12404 }
12405 if (write_mng)
12406 {
12407 unsigned int
12408 need_geom;
12409
12410 unsigned short
12411 red,
12412 green,
12413 blue;
12414
12415 mng_info->page=image->page;
12416 need_geom=MagickTrue;
12417 if (mng_info->page.width || mng_info->page.height)
12418 need_geom=MagickFalse;
12419 /*
12420 Check all the scenes.
12421 */
12422 initial_delay=image->delay;
12423 need_iterations=MagickFalse;
12424 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12425 mng_info->equal_physs=MagickTrue,
12426 mng_info->equal_gammas=MagickTrue;
12427 mng_info->equal_srgbs=MagickTrue;
12428 mng_info->equal_backgrounds=MagickTrue;
12429 image_count=0;
12430#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12431 defined(PNG_MNG_FEATURES_SUPPORTED)
12432 all_images_are_gray=MagickTrue;
12433 mng_info->equal_palettes=MagickFalse;
12434 need_local_plte=MagickFalse;
12435#endif
12436 for (next_image=image; next_image != (Image *) NULL; )
12437 {
12438 if (need_geom)
12439 {
12440 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12441 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012442
cristy3ed852e2009-09-05 21:47:34 +000012443 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12444 mng_info->page.height=next_image->rows+next_image->page.y;
12445 }
glennrp0fe50b42010-11-16 03:52:51 +000012446
cristy3ed852e2009-09-05 21:47:34 +000012447 if (next_image->page.x || next_image->page.y)
12448 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012449
cristy3ed852e2009-09-05 21:47:34 +000012450 if (next_image->matte)
12451 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012452
cristy3ed852e2009-09-05 21:47:34 +000012453 if ((int) next_image->dispose >= BackgroundDispose)
12454 if (next_image->matte || next_image->page.x || next_image->page.y ||
12455 ((next_image->columns < mng_info->page.width) &&
12456 (next_image->rows < mng_info->page.height)))
12457 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012458
cristy3ed852e2009-09-05 21:47:34 +000012459 if (next_image->iterations)
12460 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012461
cristy3ed852e2009-09-05 21:47:34 +000012462 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012463
cristy3ed852e2009-09-05 21:47:34 +000012464 if (final_delay != initial_delay || final_delay > 1UL*
12465 next_image->ticks_per_second)
12466 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012467
cristy3ed852e2009-09-05 21:47:34 +000012468#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12469 defined(PNG_MNG_FEATURES_SUPPORTED)
12470 /*
12471 check for global palette possibility.
12472 */
12473 if (image->matte != MagickFalse)
12474 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012475
cristy3ed852e2009-09-05 21:47:34 +000012476 if (need_local_plte == 0)
12477 {
cristy16ea1392012-03-21 20:38:41 +000012478 if (ImageIsGray(image,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000012479 all_images_are_gray=MagickFalse;
12480 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12481 if (use_global_plte == 0)
12482 use_global_plte=mng_info->equal_palettes;
12483 need_local_plte=!mng_info->equal_palettes;
12484 }
12485#endif
12486 if (GetNextImageInList(next_image) != (Image *) NULL)
12487 {
12488 if (next_image->background_color.red !=
12489 next_image->next->background_color.red ||
12490 next_image->background_color.green !=
12491 next_image->next->background_color.green ||
12492 next_image->background_color.blue !=
12493 next_image->next->background_color.blue)
12494 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012495
cristy3ed852e2009-09-05 21:47:34 +000012496 if (next_image->gamma != next_image->next->gamma)
12497 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012498
cristy3ed852e2009-09-05 21:47:34 +000012499 if (next_image->rendering_intent !=
12500 next_image->next->rendering_intent)
12501 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012502
cristy3ed852e2009-09-05 21:47:34 +000012503 if ((next_image->units != next_image->next->units) ||
cristy16ea1392012-03-21 20:38:41 +000012504 (next_image->resolution.x != next_image->next->resolution.x) ||
12505 (next_image->resolution.y != next_image->next->resolution.y))
cristy3ed852e2009-09-05 21:47:34 +000012506 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012507
cristy3ed852e2009-09-05 21:47:34 +000012508 if (mng_info->equal_chrms)
12509 {
12510 if (next_image->chromaticity.red_primary.x !=
12511 next_image->next->chromaticity.red_primary.x ||
12512 next_image->chromaticity.red_primary.y !=
12513 next_image->next->chromaticity.red_primary.y ||
12514 next_image->chromaticity.green_primary.x !=
12515 next_image->next->chromaticity.green_primary.x ||
12516 next_image->chromaticity.green_primary.y !=
12517 next_image->next->chromaticity.green_primary.y ||
12518 next_image->chromaticity.blue_primary.x !=
12519 next_image->next->chromaticity.blue_primary.x ||
12520 next_image->chromaticity.blue_primary.y !=
12521 next_image->next->chromaticity.blue_primary.y ||
12522 next_image->chromaticity.white_point.x !=
12523 next_image->next->chromaticity.white_point.x ||
12524 next_image->chromaticity.white_point.y !=
12525 next_image->next->chromaticity.white_point.y)
12526 mng_info->equal_chrms=MagickFalse;
12527 }
12528 }
12529 image_count++;
12530 next_image=GetNextImageInList(next_image);
12531 }
12532 if (image_count < 2)
12533 {
12534 mng_info->equal_backgrounds=MagickFalse;
12535 mng_info->equal_chrms=MagickFalse;
12536 mng_info->equal_gammas=MagickFalse;
12537 mng_info->equal_srgbs=MagickFalse;
12538 mng_info->equal_physs=MagickFalse;
12539 use_global_plte=MagickFalse;
12540#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12541 need_local_plte=MagickTrue;
12542#endif
12543 need_iterations=MagickFalse;
12544 }
glennrp0fe50b42010-11-16 03:52:51 +000012545
cristy3ed852e2009-09-05 21:47:34 +000012546 if (mng_info->need_fram == MagickFalse)
12547 {
12548 /*
12549 Only certain framing rates 100/n are exactly representable without
12550 the FRAM chunk but we'll allow some slop in VLC files
12551 */
12552 if (final_delay == 0)
12553 {
12554 if (need_iterations != MagickFalse)
12555 {
12556 /*
12557 It's probably a GIF with loop; don't run it *too* fast.
12558 */
glennrp02617122010-07-28 13:07:35 +000012559 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012560 {
12561 final_delay=10;
cristy16ea1392012-03-21 20:38:41 +000012562 (void) ThrowMagickException(exception,GetMagickModule(),
12563 CoderWarning,
glennrpd908de42010-07-28 13:28:27 +000012564 "input has zero delay between all frames; assuming",
12565 " 10 cs `%s'","");
12566 }
cristy3ed852e2009-09-05 21:47:34 +000012567 }
12568 else
12569 mng_info->ticks_per_second=0;
12570 }
12571 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012572 mng_info->ticks_per_second=(png_uint_32)
12573 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012574 if (final_delay > 50)
12575 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012576
cristy3ed852e2009-09-05 21:47:34 +000012577 if (final_delay > 75)
12578 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012579
cristy3ed852e2009-09-05 21:47:34 +000012580 if (final_delay > 125)
12581 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012582
cristy3ed852e2009-09-05 21:47:34 +000012583 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12584 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12585 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12586 1UL*image->ticks_per_second))
12587 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12588 }
glennrp0fe50b42010-11-16 03:52:51 +000012589
cristy3ed852e2009-09-05 21:47:34 +000012590 if (mng_info->need_fram != MagickFalse)
12591 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12592 /*
12593 If pseudocolor, we should also check to see if all the
12594 palettes are identical and write a global PLTE if they are.
12595 ../glennrp Feb 99.
12596 */
12597 /*
12598 Write the MNG version 1.0 signature and MHDR chunk.
12599 */
12600 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12601 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12602 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012603 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012604 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12605 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012606 PNGLong(chunk+12,mng_info->ticks_per_second);
12607 PNGLong(chunk+16,0L); /* layer count=unknown */
12608 PNGLong(chunk+20,0L); /* frame count=unknown */
12609 PNGLong(chunk+24,0L); /* play time=unknown */
12610 if (write_jng)
12611 {
12612 if (need_matte)
12613 {
12614 if (need_defi || mng_info->need_fram || use_global_plte)
12615 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012616
cristy3ed852e2009-09-05 21:47:34 +000012617 else
12618 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12619 }
glennrp0fe50b42010-11-16 03:52:51 +000012620
cristy3ed852e2009-09-05 21:47:34 +000012621 else
12622 {
12623 if (need_defi || mng_info->need_fram || use_global_plte)
12624 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012625
cristy3ed852e2009-09-05 21:47:34 +000012626 else
12627 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12628 }
12629 }
glennrp0fe50b42010-11-16 03:52:51 +000012630
cristy3ed852e2009-09-05 21:47:34 +000012631 else
12632 {
12633 if (need_matte)
12634 {
12635 if (need_defi || mng_info->need_fram || use_global_plte)
12636 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012637
cristy3ed852e2009-09-05 21:47:34 +000012638 else
12639 PNGLong(chunk+28,9L); /* simplicity=VLC */
12640 }
glennrp0fe50b42010-11-16 03:52:51 +000012641
cristy3ed852e2009-09-05 21:47:34 +000012642 else
12643 {
12644 if (need_defi || mng_info->need_fram || use_global_plte)
12645 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012646
cristy3ed852e2009-09-05 21:47:34 +000012647 else
12648 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12649 }
12650 }
12651 (void) WriteBlob(image,32,chunk);
12652 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12653 option=GetImageOption(image_info,"mng:need-cacheoff");
12654 if (option != (const char *) NULL)
12655 {
12656 size_t
12657 length;
12658
12659 /*
12660 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12661 */
12662 PNGType(chunk,mng_nEED);
12663 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012664 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012665 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012666 length+=4;
12667 (void) WriteBlob(image,length,chunk);
12668 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12669 }
12670 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12671 (GetNextImageInList(image) != (Image *) NULL) &&
12672 (image->iterations != 1))
12673 {
12674 /*
12675 Write MNG TERM chunk
12676 */
12677 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12678 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012679 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012680 chunk[4]=3; /* repeat animation */
12681 chunk[5]=0; /* show last frame when done */
12682 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12683 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012684
cristy3ed852e2009-09-05 21:47:34 +000012685 if (image->iterations == 0)
12686 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012687
cristy3ed852e2009-09-05 21:47:34 +000012688 else
12689 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012690
cristy3ed852e2009-09-05 21:47:34 +000012691 if (logging != MagickFalse)
12692 {
12693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012694 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12695 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012696
cristy3ed852e2009-09-05 21:47:34 +000012697 if (image->iterations == 0)
12698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012699 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012700
cristy3ed852e2009-09-05 21:47:34 +000012701 else
12702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012703 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012704 }
12705 (void) WriteBlob(image,14,chunk);
12706 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12707 }
12708 /*
12709 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12710 */
12711 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12712 mng_info->equal_srgbs)
12713 {
12714 /*
12715 Write MNG sRGB chunk
12716 */
12717 (void) WriteBlobMSBULong(image,1L);
12718 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012719 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012720
cristy3ed852e2009-09-05 21:47:34 +000012721 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012722 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012723 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012724 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012725
cristy3ed852e2009-09-05 21:47:34 +000012726 else
glennrpe610a072010-08-05 17:08:46 +000012727 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012728 Magick_RenderingIntent_to_PNG_RenderingIntent(
12729 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012730
cristy3ed852e2009-09-05 21:47:34 +000012731 (void) WriteBlob(image,5,chunk);
12732 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12733 mng_info->have_write_global_srgb=MagickTrue;
12734 }
glennrp0fe50b42010-11-16 03:52:51 +000012735
cristy3ed852e2009-09-05 21:47:34 +000012736 else
12737 {
12738 if (image->gamma && mng_info->equal_gammas)
12739 {
12740 /*
12741 Write MNG gAMA chunk
12742 */
12743 (void) WriteBlobMSBULong(image,4L);
12744 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012745 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012746 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012747 (void) WriteBlob(image,8,chunk);
12748 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12749 mng_info->have_write_global_gama=MagickTrue;
12750 }
12751 if (mng_info->equal_chrms)
12752 {
12753 PrimaryInfo
12754 primary;
12755
12756 /*
12757 Write MNG cHRM chunk
12758 */
12759 (void) WriteBlobMSBULong(image,32L);
12760 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012761 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012762 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012763 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12764 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012765 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012766 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12767 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012768 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012769 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12770 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012771 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012772 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12773 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012774 (void) WriteBlob(image,36,chunk);
12775 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12776 mng_info->have_write_global_chrm=MagickTrue;
12777 }
12778 }
cristy16ea1392012-03-21 20:38:41 +000012779 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
cristy3ed852e2009-09-05 21:47:34 +000012780 {
12781 /*
12782 Write MNG pHYs chunk
12783 */
12784 (void) WriteBlobMSBULong(image,9L);
12785 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012786 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012787
cristy3ed852e2009-09-05 21:47:34 +000012788 if (image->units == PixelsPerInchResolution)
12789 {
cristy35ef8242010-06-03 16:24:13 +000012790 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012791 (image->resolution.x*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012792
cristy35ef8242010-06-03 16:24:13 +000012793 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012794 (image->resolution.y*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012795
cristy3ed852e2009-09-05 21:47:34 +000012796 chunk[12]=1;
12797 }
glennrp0fe50b42010-11-16 03:52:51 +000012798
cristy3ed852e2009-09-05 21:47:34 +000012799 else
12800 {
12801 if (image->units == PixelsPerCentimeterResolution)
12802 {
cristy35ef8242010-06-03 16:24:13 +000012803 PNGLong(chunk+4,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012804 (image->resolution.x*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012805
cristy35ef8242010-06-03 16:24:13 +000012806 PNGLong(chunk+8,(png_uint_32)
cristy16ea1392012-03-21 20:38:41 +000012807 (image->resolution.y*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012808
cristy3ed852e2009-09-05 21:47:34 +000012809 chunk[12]=1;
12810 }
glennrp0fe50b42010-11-16 03:52:51 +000012811
cristy3ed852e2009-09-05 21:47:34 +000012812 else
12813 {
cristy16ea1392012-03-21 20:38:41 +000012814 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12815 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012816 chunk[12]=0;
12817 }
12818 }
12819 (void) WriteBlob(image,13,chunk);
12820 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12821 }
12822 /*
12823 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12824 or does not cover the entire frame.
12825 */
12826 if (write_mng && (image->matte || image->page.x > 0 ||
12827 image->page.y > 0 || (image->page.width &&
12828 (image->page.width+image->page.x < mng_info->page.width))
12829 || (image->page.height && (image->page.height+image->page.y
12830 < mng_info->page.height))))
12831 {
12832 (void) WriteBlobMSBULong(image,6L);
12833 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012834 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012835 red=ScaleQuantumToShort(image->background_color.red);
12836 green=ScaleQuantumToShort(image->background_color.green);
12837 blue=ScaleQuantumToShort(image->background_color.blue);
12838 PNGShort(chunk+4,red);
12839 PNGShort(chunk+6,green);
12840 PNGShort(chunk+8,blue);
12841 (void) WriteBlob(image,10,chunk);
12842 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12843 if (mng_info->equal_backgrounds)
12844 {
12845 (void) WriteBlobMSBULong(image,6L);
12846 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012847 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012848 (void) WriteBlob(image,10,chunk);
12849 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12850 }
12851 }
12852
12853#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12854 if ((need_local_plte == MagickFalse) &&
12855 (image->storage_class == PseudoClass) &&
12856 (all_images_are_gray == MagickFalse))
12857 {
cristybb503372010-05-27 20:51:26 +000012858 size_t
cristy3ed852e2009-09-05 21:47:34 +000012859 data_length;
12860
12861 /*
12862 Write MNG PLTE chunk
12863 */
12864 data_length=3*image->colors;
12865 (void) WriteBlobMSBULong(image,data_length);
12866 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012867 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012868
cristybb503372010-05-27 20:51:26 +000012869 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012870 {
cristy16ea1392012-03-21 20:38:41 +000012871 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
12872 image->colormap[i].red) & 0xff);
12873 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
12874 image->colormap[i].green) & 0xff);
12875 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
12876 image->colormap[i].blue) & 0xff);
cristy3ed852e2009-09-05 21:47:34 +000012877 }
glennrp0fe50b42010-11-16 03:52:51 +000012878
cristy3ed852e2009-09-05 21:47:34 +000012879 (void) WriteBlob(image,data_length+4,chunk);
12880 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12881 mng_info->have_write_global_plte=MagickTrue;
12882 }
12883#endif
12884 }
12885 scene=0;
12886 mng_info->delay=0;
12887#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12888 defined(PNG_MNG_FEATURES_SUPPORTED)
12889 mng_info->equal_palettes=MagickFalse;
12890#endif
12891 do
12892 {
12893 if (mng_info->adjoin)
12894 {
12895#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12896 defined(PNG_MNG_FEATURES_SUPPORTED)
12897 /*
12898 If we aren't using a global palette for the entire MNG, check to
12899 see if we can use one for two or more consecutive images.
12900 */
12901 if (need_local_plte && use_global_plte && !all_images_are_gray)
12902 {
12903 if (mng_info->IsPalette)
12904 {
12905 /*
12906 When equal_palettes is true, this image has the same palette
12907 as the previous PseudoClass image
12908 */
12909 mng_info->have_write_global_plte=mng_info->equal_palettes;
12910 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12911 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12912 {
12913 /*
12914 Write MNG PLTE chunk
12915 */
cristybb503372010-05-27 20:51:26 +000012916 size_t
cristy3ed852e2009-09-05 21:47:34 +000012917 data_length;
12918
12919 data_length=3*image->colors;
12920 (void) WriteBlobMSBULong(image,data_length);
12921 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012922 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012923
cristybb503372010-05-27 20:51:26 +000012924 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012925 {
12926 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12927 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12928 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12929 }
glennrp0fe50b42010-11-16 03:52:51 +000012930
cristy3ed852e2009-09-05 21:47:34 +000012931 (void) WriteBlob(image,data_length+4,chunk);
12932 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12933 (uInt) (data_length+4)));
12934 mng_info->have_write_global_plte=MagickTrue;
12935 }
12936 }
12937 else
12938 mng_info->have_write_global_plte=MagickFalse;
12939 }
12940#endif
12941 if (need_defi)
12942 {
cristybb503372010-05-27 20:51:26 +000012943 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012944 previous_x,
12945 previous_y;
12946
12947 if (scene)
12948 {
12949 previous_x=mng_info->page.x;
12950 previous_y=mng_info->page.y;
12951 }
12952 else
12953 {
12954 previous_x=0;
12955 previous_y=0;
12956 }
12957 mng_info->page=image->page;
12958 if ((mng_info->page.x != previous_x) ||
12959 (mng_info->page.y != previous_y))
12960 {
12961 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12962 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012963 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012964 chunk[4]=0; /* object 0 MSB */
12965 chunk[5]=0; /* object 0 LSB */
12966 chunk[6]=0; /* visible */
12967 chunk[7]=0; /* abstract */
12968 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12969 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12970 (void) WriteBlob(image,16,chunk);
12971 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12972 }
12973 }
12974 }
12975
12976 mng_info->write_mng=write_mng;
12977
12978 if ((int) image->dispose >= 3)
12979 mng_info->framing_mode=3;
12980
12981 if (mng_info->need_fram && mng_info->adjoin &&
12982 ((image->delay != mng_info->delay) ||
12983 (mng_info->framing_mode != mng_info->old_framing_mode)))
12984 {
12985 if (image->delay == mng_info->delay)
12986 {
12987 /*
12988 Write a MNG FRAM chunk with the new framing mode.
12989 */
12990 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12991 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012992 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012993 chunk[4]=(unsigned char) mng_info->framing_mode;
12994 (void) WriteBlob(image,5,chunk);
12995 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12996 }
12997 else
12998 {
12999 /*
13000 Write a MNG FRAM chunk with the delay.
13001 */
13002 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13003 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000013004 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000013005 chunk[4]=(unsigned char) mng_info->framing_mode;
13006 chunk[5]=0; /* frame name separator (no name) */
13007 chunk[6]=2; /* flag for changing default delay */
13008 chunk[7]=0; /* flag for changing frame timeout */
13009 chunk[8]=0; /* flag for changing frame clipping */
13010 chunk[9]=0; /* flag for changing frame sync_id */
13011 PNGLong(chunk+10,(png_uint_32)
13012 ((mng_info->ticks_per_second*
13013 image->delay)/MagickMax(image->ticks_per_second,1)));
13014 (void) WriteBlob(image,14,chunk);
13015 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000013016 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000013017 }
13018 mng_info->old_framing_mode=mng_info->framing_mode;
13019 }
13020
13021#if defined(JNG_SUPPORTED)
13022 if (image_info->compression == JPEGCompression)
13023 {
13024 ImageInfo
13025 *write_info;
13026
13027 if (logging != MagickFalse)
13028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13029 " Writing JNG object.");
13030 /* To do: specify the desired alpha compression method. */
13031 write_info=CloneImageInfo(image_info);
13032 write_info->compression=UndefinedCompression;
cristy16ea1392012-03-21 20:38:41 +000013033 status=WriteOneJNGImage(mng_info,write_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013034 write_info=DestroyImageInfo(write_info);
13035 }
13036 else
13037#endif
13038 {
13039 if (logging != MagickFalse)
13040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13041 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000013042
glennrpb9cfe272010-12-21 15:08:06 +000013043 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000013044 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000013045
13046 /* We don't want any ancillary chunks written */
13047 mng_info->ping_exclude_bKGD=MagickTrue;
13048 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000013049 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013050 mng_info->ping_exclude_EXIF=MagickTrue;
13051 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013052 mng_info->ping_exclude_iCCP=MagickTrue;
13053 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13054 mng_info->ping_exclude_oFFs=MagickTrue;
13055 mng_info->ping_exclude_pHYs=MagickTrue;
13056 mng_info->ping_exclude_sRGB=MagickTrue;
13057 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000013058 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000013059 mng_info->ping_exclude_vpAg=MagickTrue;
13060 mng_info->ping_exclude_zCCP=MagickTrue;
13061 mng_info->ping_exclude_zTXt=MagickTrue;
13062
cristy16ea1392012-03-21 20:38:41 +000013063 status=WriteOnePNGImage(mng_info,image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +000013064 }
13065
13066 if (status == MagickFalse)
13067 {
13068 MngInfoFreeStruct(mng_info,&have_mng_structure);
13069 (void) CloseBlob(image);
13070 return(MagickFalse);
13071 }
13072 (void) CatchImageException(image);
13073 if (GetNextImageInList(image) == (Image *) NULL)
13074 break;
13075 image=SyncNextImageInList(image);
13076 status=SetImageProgress(image,SaveImagesTag,scene++,
13077 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000013078
cristy3ed852e2009-09-05 21:47:34 +000013079 if (status == MagickFalse)
13080 break;
glennrp0fe50b42010-11-16 03:52:51 +000013081
cristy3ed852e2009-09-05 21:47:34 +000013082 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000013083
cristy3ed852e2009-09-05 21:47:34 +000013084 if (write_mng)
13085 {
13086 while (GetPreviousImageInList(image) != (Image *) NULL)
13087 image=GetPreviousImageInList(image);
13088 /*
13089 Write the MEND chunk.
13090 */
13091 (void) WriteBlobMSBULong(image,0x00000000L);
13092 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000013093 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000013094 (void) WriteBlob(image,4,chunk);
13095 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13096 }
13097 /*
13098 Relinquish resources.
13099 */
13100 (void) CloseBlob(image);
13101 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000013102
cristy3ed852e2009-09-05 21:47:34 +000013103 if (logging != MagickFalse)
13104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000013105
cristy3ed852e2009-09-05 21:47:34 +000013106 return(MagickTrue);
13107}
glennrpd5045b42010-03-24 12:40:35 +000013108#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000013109
cristy3ed852e2009-09-05 21:47:34 +000013110static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13111{
glennrp3bd393f2011-12-21 18:54:53 +000013112 (void) image;
cristy3ed852e2009-09-05 21:47:34 +000013113 printf("Your PNG library is too old: You have libpng-%s\n",
13114 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000013115
cristy3ed852e2009-09-05 21:47:34 +000013116 ThrowBinaryException(CoderError,"PNG library is too old",
13117 image_info->filename);
13118}
glennrp39992b42010-11-14 00:03:43 +000013119
cristy3ed852e2009-09-05 21:47:34 +000013120static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13121{
13122 return(WritePNGImage(image_info,image));
13123}
glennrpd5045b42010-03-24 12:40:35 +000013124#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000013125#endif