blob: 7d188f05906d088764b547f9d300f658df132eeb [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% %
cristy7e41fe82010-12-04 23:12:08 +000021% Copyright 1999-2011 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*/
cristy4c08aed2011-07-01 19:47:50 +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"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colormap.h"
53#include "MagickCore/colorspace.h"
54#include "MagickCore/constitute.h"
55#include "MagickCore/enhance.h"
56#include "MagickCore/exception.h"
57#include "MagickCore/exception-private.h"
58#include "MagickCore/geometry.h"
59#include "MagickCore/histogram.h"
60#include "MagickCore/image.h"
61#include "MagickCore/image-private.h"
62#include "MagickCore/layer.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/MagickCore.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/module.h"
68#include "MagickCore/monitor.h"
69#include "MagickCore/monitor-private.h"
70#include "MagickCore/option.h"
71#include "MagickCore/pixel.h"
72#include "MagickCore/pixel-accessor.h"
73#include "MagickCore/profile.h"
74#include "MagickCore/property.h"
75#include "MagickCore/quantum-private.h"
76#include "MagickCore/resource_.h"
77#include "MagickCore/semaphore.h"
78#include "MagickCore/quantum-private.h"
79#include "MagickCore/static.h"
80#include "MagickCore/statistic.h"
81#include "MagickCore/string_.h"
82#include "MagickCore/string-private.h"
83#include "MagickCore/transform.h"
84#include "MagickCore/utility.h"
cristy3ed852e2009-09-05 21:47:34 +000085#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp286a6352009-11-09 02:58:50 +000086
glennrp7ef138c2009-11-10 13:50:20 +000087/* Suppress libpng pedantic warnings that were added in
88 * libpng-1.2.41 and libpng-1.4.0. If you are working on
glennrpfaa852b2010-03-30 12:17:00 +000089 * migration to libpng-1.5, remove these defines and then
glennrp7ef138c2009-11-10 13:50:20 +000090 * fix any code that generates warnings.
91 */
glennrp991e92a2010-01-28 03:09:00 +000092/* #define PNG_DEPRECATED Use of this function is deprecated */
glennrpfaa852b2010-03-30 12:17:00 +000093/* #define PNG_USE_RESULT The result of this function must be checked */
94/* #define PNG_NORETURN This function does not return */
95/* #define PNG_ALLOCATED The result of the function is new memory */
glennrp8371ecc2011-02-19 19:15:38 +000096/* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
glennrp5b927342011-04-14 14:31:48 +000097
98/* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
cristy75cfe702011-04-14 14:27:17 +000099#define PNG_PTR_NORETURN
glennrp286a6352009-11-09 02:58:50 +0000100
cristy3ed852e2009-09-05 21:47:34 +0000101#include "png.h"
102#include "zlib.h"
103
104/* ImageMagick differences */
105#define first_scene scene
106
glennrpd5045b42010-03-24 12:40:35 +0000107#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +0000108/*
109 Optional declarations. Define or undefine them as you like.
110*/
111/* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
112
113/*
114 Features under construction. Define these to work on them.
115*/
116#undef MNG_OBJECT_BUFFERS
117#undef MNG_BASI_SUPPORTED
118#define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
119#define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
cristy3ed852e2009-09-05 21:47:34 +0000120#if defined(MAGICKCORE_JPEG_DELEGATE)
121# define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
122#endif
123#if !defined(RGBColorMatchExact)
124#define IsPNGColorEqual(color,target) \
glennrp8e045c82011-04-27 16:40:27 +0000125 (((color).red == (target).red) && \
126 ((color).green == (target).green) && \
127 ((color).blue == (target).blue))
cristy3ed852e2009-09-05 21:47:34 +0000128#endif
129
glennrp8e58efd2011-05-20 12:16:29 +0000130/* Macros for left-bit-replication to ensure that pixels
131 * and PixelPackets all have the image->depth, and for use
132 * in PNG8 quantization.
133 */
134
135
136/* LBR01: Replicate top bit */
137
cristy4c08aed2011-07-01 19:47:50 +0000138#define LBR01PixelPacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000139 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
140 0 : QuantumRange);
141
glennrp91d99252011-06-25 14:30:13 +0000142#define LBR01PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000143 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
144 0 : QuantumRange);
145
glennrp91d99252011-06-25 14:30:13 +0000146#define LBR01PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000147 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
148 0 : QuantumRange);
149
cristy4c08aed2011-07-01 19:47:50 +0000150#define LBR01PacketAlpha(pixelpacket) \
151 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000152 0 : QuantumRange);
153
glennrp91d99252011-06-25 14:30:13 +0000154#define LBR01PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000155 { \
cristy4c08aed2011-07-01 19:47:50 +0000156 LBR01PixelPacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000157 LBR01PacketGreen((pixelpacket)); \
158 LBR01PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000159 }
glennrp8e58efd2011-05-20 12:16:29 +0000160
glennrp91d99252011-06-25 14:30:13 +0000161#define LBR01PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000162 { \
glennrp91d99252011-06-25 14:30:13 +0000163 LBR01PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000164 LBR01PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000165 }
glennrp8e58efd2011-05-20 12:16:29 +0000166
cristyef618312011-06-25 12:26:44 +0000167#define LBR01PixelRed(pixel) \
cristy4c08aed2011-07-01 19:47:50 +0000168 (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000169 0 : QuantumRange);
170
cristy4c08aed2011-07-01 19:47:50 +0000171#define LBR01Green(pixel) \
172 (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000173 0 : QuantumRange);
174
cristy4c08aed2011-07-01 19:47:50 +0000175#define LBR01Blue(pixel) \
176 (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000177 0 : QuantumRange);
178
cristy4c08aed2011-07-01 19:47:50 +0000179#define LBR01Alpha(pixel) \
180 (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
glennrp8e58efd2011-05-20 12:16:29 +0000181 0 : QuantumRange);
182
cristy4c08aed2011-07-01 19:47:50 +0000183#define LBR01RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000184 { \
cristyef618312011-06-25 12:26:44 +0000185 LBR01PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000186 LBR01Green((pixel)); \
187 LBR01Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000188 }
glennrp8e58efd2011-05-20 12:16:29 +0000189
cristy4c08aed2011-07-01 19:47:50 +0000190#define LBR01RGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000191 { \
cristy4c08aed2011-07-01 19:47:50 +0000192 LBR01RGB((pixel)); \
193 LBR01Alpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000194 }
glennrp8e58efd2011-05-20 12:16:29 +0000195
196/* LBR02: Replicate top 2 bits */
197
cristy4c08aed2011-07-01 19:47:50 +0000198#define LBR02PixelPacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000199 { \
200 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
201 (pixelpacket).red=ScaleCharToQuantum( \
202 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
203 }
glennrp91d99252011-06-25 14:30:13 +0000204#define LBR02PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000205 { \
206 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
207 (pixelpacket).green=ScaleCharToQuantum( \
208 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
209 }
glennrp91d99252011-06-25 14:30:13 +0000210#define LBR02PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000211 { \
212 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
213 (pixelpacket).blue=ScaleCharToQuantum( \
214 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
215 }
cristy4c08aed2011-07-01 19:47:50 +0000216#define LBR02PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000217 { \
cristy4c08aed2011-07-01 19:47:50 +0000218 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
219 (pixelpacket).alpha=ScaleCharToQuantum( \
glennrp8e58efd2011-05-20 12:16:29 +0000220 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
221 }
222
glennrp91d99252011-06-25 14:30:13 +0000223#define LBR02PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000224 { \
cristy4c08aed2011-07-01 19:47:50 +0000225 LBR02PixelPacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000226 LBR02PacketGreen((pixelpacket)); \
227 LBR02PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000228 }
glennrp8e58efd2011-05-20 12:16:29 +0000229
glennrp91d99252011-06-25 14:30:13 +0000230#define LBR02PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000231 { \
glennrp91d99252011-06-25 14:30:13 +0000232 LBR02PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000233 LBR02PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000234 }
glennrp8e58efd2011-05-20 12:16:29 +0000235
cristyef618312011-06-25 12:26:44 +0000236#define LBR02PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000237 { \
cristy4c08aed2011-07-01 19:47:50 +0000238 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000239 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000240 SetPixelRed(image, ScaleCharToQuantum( \
241 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000242 }
cristy4c08aed2011-07-01 19:47:50 +0000243#define LBR02Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000244 { \
cristy4c08aed2011-07-01 19:47:50 +0000245 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000246 & 0xc0; \
cristy4c08aed2011-07-01 19:47:50 +0000247 SetPixelGreen(image, ScaleCharToQuantum( \
248 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000249 }
cristy4c08aed2011-07-01 19:47:50 +0000250#define LBR02Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000251 { \
252 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000253 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
254 SetPixelBlue(image, ScaleCharToQuantum( \
255 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000256 }
cristy4c08aed2011-07-01 19:47:50 +0000257#define LBR02Alpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000258 { \
259 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000260 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
261 SetPixelAlpha(image, ScaleCharToQuantum( \
262 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), (pixel) ); \
glennrp8e58efd2011-05-20 12:16:29 +0000263 }
264
cristy4c08aed2011-07-01 19:47:50 +0000265#define LBR02RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000266 { \
cristyef618312011-06-25 12:26:44 +0000267 LBR02PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000268 LBR02Green((pixel)); \
269 LBR02Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000270 }
glennrp8e58efd2011-05-20 12:16:29 +0000271
cristy4c08aed2011-07-01 19:47:50 +0000272#define LBR02RGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000273 { \
cristy4c08aed2011-07-01 19:47:50 +0000274 LBR02RGB((pixel)); \
275 LBR02Alpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000276 }
glennrp8e58efd2011-05-20 12:16:29 +0000277
278/* LBR03: Replicate top 3 bits (only used with opaque pixels during
279 PNG8 quantization) */
280
cristy4c08aed2011-07-01 19:47:50 +0000281#define LBR03PixelPacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000282 { \
283 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
284 (pixelpacket).red=ScaleCharToQuantum( \
285 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
286 }
glennrp91d99252011-06-25 14:30:13 +0000287#define LBR03PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000288 { \
289 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
290 (pixelpacket).green=ScaleCharToQuantum( \
291 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
292 }
glennrp91d99252011-06-25 14:30:13 +0000293#define LBR03PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000294 { \
295 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
296 (pixelpacket).blue=ScaleCharToQuantum( \
297 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
298 }
299
glennrp91d99252011-06-25 14:30:13 +0000300#define LBR03PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000301 { \
cristy4c08aed2011-07-01 19:47:50 +0000302 LBR03PixelPacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000303 LBR03PacketGreen((pixelpacket)); \
304 LBR03PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000305 }
glennrp8e58efd2011-05-20 12:16:29 +0000306
cristyef618312011-06-25 12:26:44 +0000307#define LBR03PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000308 { \
cristy4c08aed2011-07-01 19:47:50 +0000309 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000310 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000311 SetPixelRed(image, ScaleCharToQuantum( \
312 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000313 }
cristy4c08aed2011-07-01 19:47:50 +0000314#define LBR03Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000315 { \
cristy4c08aed2011-07-01 19:47:50 +0000316 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000317 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000318 SetPixelGreen(image, ScaleCharToQuantum( \
319 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000320 }
cristy4c08aed2011-07-01 19:47:50 +0000321#define LBR03Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000322 { \
cristy4c08aed2011-07-01 19:47:50 +0000323 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000324 & 0xe0; \
cristy4c08aed2011-07-01 19:47:50 +0000325 SetPixelBlue(image, ScaleCharToQuantum( \
326 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000327 }
328
cristy4c08aed2011-07-01 19:47:50 +0000329#define LBR03RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000330 { \
cristyef618312011-06-25 12:26:44 +0000331 LBR03PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000332 LBR03Green((pixel)); \
333 LBR03Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000334 }
glennrp8e58efd2011-05-20 12:16:29 +0000335
336/* LBR04: Replicate top 4 bits */
337
cristy4c08aed2011-07-01 19:47:50 +0000338#define LBR04PixelPacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000339 { \
340 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
341 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
342 }
glennrp91d99252011-06-25 14:30:13 +0000343#define LBR04PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000344 { \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
346 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
347 }
glennrp91d99252011-06-25 14:30:13 +0000348#define LBR04PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000349 { \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
351 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
352 }
cristy4c08aed2011-07-01 19:47:50 +0000353#define LBR04PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000354 { \
cristy4c08aed2011-07-01 19:47:50 +0000355 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
356 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
glennrp8e58efd2011-05-20 12:16:29 +0000357 }
358
glennrp91d99252011-06-25 14:30:13 +0000359#define LBR04PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000360 { \
cristy4c08aed2011-07-01 19:47:50 +0000361 LBR04PixelPacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000362 LBR04PacketGreen((pixelpacket)); \
363 LBR04PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000364 }
glennrp8e58efd2011-05-20 12:16:29 +0000365
glennrp91d99252011-06-25 14:30:13 +0000366#define LBR04PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000367 { \
glennrp91d99252011-06-25 14:30:13 +0000368 LBR04PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000369 LBR04PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000370 }
glennrp8e58efd2011-05-20 12:16:29 +0000371
cristyef618312011-06-25 12:26:44 +0000372#define LBR04PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000373 { \
cristy4c08aed2011-07-01 19:47:50 +0000374 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
glennrp8e58efd2011-05-20 12:16:29 +0000375 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000376 SetPixelRed(image,\
377 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000378 }
cristy4c08aed2011-07-01 19:47:50 +0000379#define LBR04Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000380 { \
cristy4c08aed2011-07-01 19:47:50 +0000381 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
glennrp8e58efd2011-05-20 12:16:29 +0000382 & 0xf0; \
cristy4c08aed2011-07-01 19:47:50 +0000383 SetPixelGreen(image,\
384 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000385 }
cristy4c08aed2011-07-01 19:47:50 +0000386#define LBR04Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000387 { \
388 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000389 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
390 SetPixelBlue(image,\
391 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000392 }
cristy4c08aed2011-07-01 19:47:50 +0000393#define LBR04Alpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000394 { \
395 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000396 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
397 SetPixelAlpha(image,\
398 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000399 }
400
cristy4c08aed2011-07-01 19:47:50 +0000401#define LBR04RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000402 { \
cristyef618312011-06-25 12:26:44 +0000403 LBR04PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000404 LBR04Green((pixel)); \
405 LBR04Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000406 }
glennrp8e58efd2011-05-20 12:16:29 +0000407
cristy4c08aed2011-07-01 19:47:50 +0000408#define LBR04RGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000409 { \
cristy4c08aed2011-07-01 19:47:50 +0000410 LBR04RGB((pixel)); \
411 LBR04Alpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000412 }
glennrp8e58efd2011-05-20 12:16:29 +0000413
414
415/* LBR08: Replicate top 8 bits */
416
cristy4c08aed2011-07-01 19:47:50 +0000417#define LBR08PixelPacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000418 { \
419 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red); \
420 (pixelpacket).red=ScaleCharToQuantum((lbr_bits)); \
421 }
glennrp91d99252011-06-25 14:30:13 +0000422#define LBR08PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000423 { \
424 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green); \
425 (pixelpacket).green=ScaleCharToQuantum((lbr_bits)); \
426 }
glennrp91d99252011-06-25 14:30:13 +0000427#define LBR08PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000428 { \
429 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue); \
430 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits)); \
431 }
cristy4c08aed2011-07-01 19:47:50 +0000432#define LBR08PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000433 { \
cristy4c08aed2011-07-01 19:47:50 +0000434 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha); \
435 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000436 }
437
glennrp91d99252011-06-25 14:30:13 +0000438#define LBR08PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000439 { \
cristy4c08aed2011-07-01 19:47:50 +0000440 LBR08PixelPacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000441 LBR08PacketGreen((pixelpacket)); \
442 LBR08PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000443 }
glennrp8e58efd2011-05-20 12:16:29 +0000444
glennrp91d99252011-06-25 14:30:13 +0000445#define LBR08PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000446 { \
glennrp91d99252011-06-25 14:30:13 +0000447 LBR08PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000448 LBR08PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000449 }
glennrp8e58efd2011-05-20 12:16:29 +0000450
cristyef618312011-06-25 12:26:44 +0000451#define LBR08PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000452 { \
453 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000454 ScaleQuantumToChar(GetPixelRed(image,(pixel))); \
455 SetPixelRed(image,\
456 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000457 }
cristy4c08aed2011-07-01 19:47:50 +0000458#define LBR08Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000459 { \
460 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000461 ScaleQuantumToChar(GetPixelGreen(image,(pixel))); \
462 SetPixelGreen(image,\
463 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000464 }
cristy4c08aed2011-07-01 19:47:50 +0000465#define LBR08Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000466 { \
467 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000468 ScaleQuantumToChar(GetPixelBlue(image,(pixel))); \
469 SetPixelBlue(image,\
470 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000471 }
cristy4c08aed2011-07-01 19:47:50 +0000472#define LBR08Alpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000473 { \
474 unsigned char lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000475 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))); \
476 SetPixelAlpha(image,\
477 ScaleCharToQuantum((lbr_bits)), (pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000478 }
479
cristy4c08aed2011-07-01 19:47:50 +0000480#define LBR08RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000481 { \
cristyef618312011-06-25 12:26:44 +0000482 LBR08PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000483 LBR08Green((pixel)); \
484 LBR08Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000485 }
glennrp8e58efd2011-05-20 12:16:29 +0000486
cristy4c08aed2011-07-01 19:47:50 +0000487#define LBR08RGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000488 { \
cristy4c08aed2011-07-01 19:47:50 +0000489 LBR08RGB((pixel)); \
490 LBR08Alpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000491 }
glennrp8e58efd2011-05-20 12:16:29 +0000492
493
494/* LBR16: Replicate top 16 bits */
495
cristy4c08aed2011-07-01 19:47:50 +0000496#define LBR16PixelPacketRed(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000497 { \
498 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).red); \
499 (pixelpacket).red=ScaleShortToQuantum((lbr_bits)); \
500 }
glennrp91d99252011-06-25 14:30:13 +0000501#define LBR16PacketGreen(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000502 { \
503 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).green); \
504 (pixelpacket).green=ScaleShortToQuantum((lbr_bits)); \
505 }
glennrp91d99252011-06-25 14:30:13 +0000506#define LBR16PacketBlue(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000507 { \
508 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).blue); \
509 (pixelpacket).blue=ScaleShortToQuantum((lbr_bits)); \
510 }
cristy4c08aed2011-07-01 19:47:50 +0000511#define LBR16PacketAlpha(pixelpacket) \
glennrp8e58efd2011-05-20 12:16:29 +0000512 { \
cristy4c08aed2011-07-01 19:47:50 +0000513 unsigned short lbr_bits=ScaleQuantumToShort((pixelpacket).alpha); \
514 (pixelpacket).alpha=ScaleShortToQuantum((lbr_bits)); \
glennrp8e58efd2011-05-20 12:16:29 +0000515 }
516
glennrp91d99252011-06-25 14:30:13 +0000517#define LBR16PacketRGB(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000518 { \
cristy4c08aed2011-07-01 19:47:50 +0000519 LBR16PixelPacketRed((pixelpacket)); \
glennrp91d99252011-06-25 14:30:13 +0000520 LBR16PacketGreen((pixelpacket)); \
521 LBR16PacketBlue((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000522 }
glennrp8e58efd2011-05-20 12:16:29 +0000523
glennrp91d99252011-06-25 14:30:13 +0000524#define LBR16PacketRGBO(pixelpacket) \
glennrpbb4f99d2011-05-22 11:13:17 +0000525 { \
glennrp91d99252011-06-25 14:30:13 +0000526 LBR16PacketRGB((pixelpacket)); \
cristy4c08aed2011-07-01 19:47:50 +0000527 LBR16PacketAlpha((pixelpacket)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000528 }
glennrp8e58efd2011-05-20 12:16:29 +0000529
cristyef618312011-06-25 12:26:44 +0000530#define LBR16PixelRed(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000531 { \
532 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000533 ScaleQuantumToShort(GetPixelRed(image,(pixel))); \
534 SetPixelRed(image,\
535 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000536 }
cristy4c08aed2011-07-01 19:47:50 +0000537#define LBR16Green(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000538 { \
539 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000540 ScaleQuantumToShort(GetPixelGreen(image,(pixel))); \
541 SetPixelGreen(image,\
542 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000543 }
cristy4c08aed2011-07-01 19:47:50 +0000544#define LBR16Blue(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000545 { \
546 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000547 ScaleQuantumToShort(GetPixelBlue(image,(pixel))); \
548 SetPixelBlue(image,\
549 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000550 }
cristy4c08aed2011-07-01 19:47:50 +0000551#define LBR16Alpha(pixel) \
glennrp8e58efd2011-05-20 12:16:29 +0000552 { \
553 unsigned short lbr_bits= \
cristy4c08aed2011-07-01 19:47:50 +0000554 ScaleQuantumToShort(GetPixelAlpha(image,(pixel))); \
555 SetPixelAlpha(image,\
556 ScaleShortToQuantum((lbr_bits)),(pixel)); \
glennrp8e58efd2011-05-20 12:16:29 +0000557 }
558
cristy4c08aed2011-07-01 19:47:50 +0000559#define LBR16RGB(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000560 { \
cristyef618312011-06-25 12:26:44 +0000561 LBR16PixelRed((pixel)); \
cristy4c08aed2011-07-01 19:47:50 +0000562 LBR16Green((pixel)); \
563 LBR16Blue((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000564 }
glennrp8e58efd2011-05-20 12:16:29 +0000565
cristy4c08aed2011-07-01 19:47:50 +0000566#define LBR16RGBA(pixel) \
glennrpbb4f99d2011-05-22 11:13:17 +0000567 { \
cristy4c08aed2011-07-01 19:47:50 +0000568 LBR16RGB((pixel)); \
569 LBR16Alpha((pixel)); \
glennrpbb4f99d2011-05-22 11:13:17 +0000570 }
glennrp8e58efd2011-05-20 12:16:29 +0000571
cristy3ed852e2009-09-05 21:47:34 +0000572/*
573 Establish thread safety.
574 setjmp/longjmp is claimed to be safe on these platforms:
575 setjmp/longjmp is alleged to be unsafe on these platforms:
576*/
577#ifndef SETJMP_IS_THREAD_SAFE
578#define PNG_SETJMP_NOT_THREAD_SAFE
579#endif
580
581#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
582static SemaphoreInfo
glennrpcf002022011-01-30 02:38:15 +0000583 *ping_semaphore = (SemaphoreInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000584#endif
585
586/*
587 This temporary until I set up malloc'ed object attributes array.
588 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
589 waste more memory.
590*/
591#define MNG_MAX_OBJECTS 256
592
593/*
594 If this not defined, spec is interpreted strictly. If it is
595 defined, an attempt will be made to recover from some errors,
596 including
597 o global PLTE too short
598*/
599#undef MNG_LOOSE
600
601/*
602 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
603 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
604 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
605 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
606 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
607 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
608 will be enabled by default in libpng-1.2.0.
609*/
cristy3ed852e2009-09-05 21:47:34 +0000610#ifdef PNG_MNG_FEATURES_SUPPORTED
611# ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
612# define PNG_READ_EMPTY_PLTE_SUPPORTED
613# endif
614# ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
615# define PNG_WRITE_EMPTY_PLTE_SUPPORTED
616# endif
617#endif
618
619/*
cristybb503372010-05-27 20:51:26 +0000620 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
cristy3ed852e2009-09-05 21:47:34 +0000621 This macro is only defined in libpng-1.0.3 and later.
622 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
623*/
624#ifndef PNG_UINT_31_MAX
625#define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
626#endif
627
628/*
629 Constant strings for known chunk types. If you need to add a chunk,
630 add a string holding the name here. To make the code more
631 portable, we use ASCII numbers like this, not characters.
632*/
633
634static png_byte FARDATA mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
635static png_byte FARDATA mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
636static png_byte FARDATA mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
637static png_byte FARDATA mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
638static png_byte FARDATA mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
639static png_byte FARDATA mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
640static png_byte FARDATA mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
641static png_byte FARDATA mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
642static png_byte FARDATA mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
643static png_byte FARDATA mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
644static png_byte FARDATA mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
645static png_byte FARDATA mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
646static png_byte FARDATA mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
647static png_byte FARDATA mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
648static png_byte FARDATA mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
649static png_byte FARDATA mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
650static png_byte FARDATA mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
651static png_byte FARDATA mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
652static png_byte FARDATA mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
653static png_byte FARDATA mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
654static png_byte FARDATA mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
655static png_byte FARDATA mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
656static png_byte FARDATA mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
657static png_byte FARDATA mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
658static png_byte FARDATA mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
659static png_byte FARDATA mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
660static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
661static png_byte FARDATA mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
662static png_byte FARDATA mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
663static png_byte FARDATA mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
664static png_byte FARDATA mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
665static png_byte FARDATA mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
666static png_byte FARDATA mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
667static png_byte FARDATA mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
668
669#if defined(JNG_SUPPORTED)
670static png_byte FARDATA mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
671static png_byte FARDATA mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
672static png_byte FARDATA mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
673static png_byte FARDATA mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
674static png_byte FARDATA mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
675static png_byte FARDATA mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
676#endif
677
678/*
679Other known chunks that are not yet supported by ImageMagick:
680static png_byte FARDATA mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
681static png_byte FARDATA mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
682static png_byte FARDATA mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
683static png_byte FARDATA mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
684static png_byte FARDATA mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
685static png_byte FARDATA mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
686static png_byte FARDATA mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
687static png_byte FARDATA mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
688*/
689
690typedef struct _MngBox
691{
cristy8182b072010-05-30 20:10:53 +0000692 long
cristy3ed852e2009-09-05 21:47:34 +0000693 left,
694 right,
695 top,
696 bottom;
697} MngBox;
698
699typedef struct _MngPair
700{
cristy8182b072010-05-30 20:10:53 +0000701 volatile long
cristy3ed852e2009-09-05 21:47:34 +0000702 a,
703 b;
704} MngPair;
705
706#ifdef MNG_OBJECT_BUFFERS
707typedef struct _MngBuffer
708{
709
cristybb503372010-05-27 20:51:26 +0000710 size_t
cristy3ed852e2009-09-05 21:47:34 +0000711 height,
712 width;
713
714 Image
715 *image;
716
717 png_color
718 plte[256];
719
720 int
721 reference_count;
722
723 unsigned char
724 alpha_sample_depth,
725 compression_method,
726 color_type,
727 concrete,
728 filter_method,
729 frozen,
730 image_type,
731 interlace_method,
732 pixel_sample_depth,
733 plte_length,
734 sample_depth,
735 viewable;
736} MngBuffer;
737#endif
738
739typedef struct _MngInfo
740{
741
742#ifdef MNG_OBJECT_BUFFERS
743 MngBuffer
744 *ob[MNG_MAX_OBJECTS];
745#endif
746
747 Image *
748 image;
749
750 RectangleInfo
751 page;
752
753 int
754 adjoin,
755#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
756 bytes_in_read_buffer,
757 found_empty_plte,
758#endif
759 equal_backgrounds,
760 equal_chrms,
761 equal_gammas,
762#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
763 defined(PNG_MNG_FEATURES_SUPPORTED)
764 equal_palettes,
765#endif
766 equal_physs,
767 equal_srgbs,
768 framing_mode,
769 have_global_bkgd,
770 have_global_chrm,
771 have_global_gama,
772 have_global_phys,
773 have_global_sbit,
774 have_global_srgb,
775 have_saved_bkgd_index,
776 have_write_global_chrm,
777 have_write_global_gama,
778 have_write_global_plte,
779 have_write_global_srgb,
780 need_fram,
781 object_id,
782 old_framing_mode,
cristy3ed852e2009-09-05 21:47:34 +0000783 saved_bkgd_index;
784
785 int
786 new_number_colors;
787
cristybb503372010-05-27 20:51:26 +0000788 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000789 image_found,
790 loop_count[256],
791 loop_iteration[256],
792 scenes_found,
793 x_off[MNG_MAX_OBJECTS],
794 y_off[MNG_MAX_OBJECTS];
795
796 MngBox
797 clip,
798 frame,
799 image_box,
800 object_clip[MNG_MAX_OBJECTS];
801
802 unsigned char
803 /* These flags could be combined into one byte */
804 exists[MNG_MAX_OBJECTS],
805 frozen[MNG_MAX_OBJECTS],
806 loop_active[256],
807 invisible[MNG_MAX_OBJECTS],
808 viewable[MNG_MAX_OBJECTS];
809
810 MagickOffsetType
811 loop_jump[256];
812
813 png_colorp
814 global_plte;
815
816 png_color_8
817 global_sbit;
818
819 png_byte
820#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
821 read_buffer[8],
822#endif
823 global_trns[256];
824
825 float
826 global_gamma;
827
828 ChromaticityInfo
829 global_chrm;
830
831 RenderingIntent
832 global_srgb_intent;
833
cristy35ef8242010-06-03 16:24:13 +0000834 unsigned int
cristy3ed852e2009-09-05 21:47:34 +0000835 delay,
836 global_plte_length,
837 global_trns_length,
838 global_x_pixels_per_unit,
839 global_y_pixels_per_unit,
840 mng_width,
841 mng_height,
842 ticks_per_second;
843
glennrpb9cfe272010-12-21 15:08:06 +0000844 MagickBooleanType
845 need_blob;
846
cristy3ed852e2009-09-05 21:47:34 +0000847 unsigned int
848 IsPalette,
849 global_phys_unit_type,
850 basi_warning,
851 clon_warning,
852 dhdr_warning,
853 jhdr_warning,
854 magn_warning,
855 past_warning,
856 phyg_warning,
857 phys_warning,
858 sbit_warning,
859 show_warning,
860 mng_type,
861 write_mng,
862 write_png_colortype,
863 write_png_depth,
glennrp18682582011-06-30 18:11:47 +0000864 write_png_compression_level,
865 write_png_compression_strategy,
866 write_png_compression_filter,
cristy3ed852e2009-09-05 21:47:34 +0000867 write_png8,
868 write_png24,
869 write_png32;
870
871#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +0000872 size_t
cristy3ed852e2009-09-05 21:47:34 +0000873 basi_width,
874 basi_height;
875
876 unsigned int
877 basi_depth,
878 basi_color_type,
879 basi_compression_method,
880 basi_filter_type,
881 basi_interlace_method,
882 basi_red,
883 basi_green,
884 basi_blue,
885 basi_alpha,
886 basi_viewable;
887#endif
888
889 png_uint_16
890 magn_first,
891 magn_last,
892 magn_mb,
893 magn_ml,
894 magn_mr,
895 magn_mt,
896 magn_mx,
897 magn_my,
898 magn_methx,
899 magn_methy;
900
901 PixelPacket
902 mng_global_bkgd;
903
glennrp26f37912010-12-23 16:22:42 +0000904 /* Added at version 6.6.6-7 */
905 MagickBooleanType
906 ping_exclude_bKGD,
907 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +0000908 ping_exclude_date,
glennrp26f37912010-12-23 16:22:42 +0000909 ping_exclude_EXIF,
910 ping_exclude_gAMA,
911 ping_exclude_iCCP,
912 /* ping_exclude_iTXt, */
913 ping_exclude_oFFs,
914 ping_exclude_pHYs,
915 ping_exclude_sRGB,
916 ping_exclude_tEXt,
glennrpa1e3b7b2010-12-24 16:37:33 +0000917 ping_exclude_tRNS,
glennrp26f37912010-12-23 16:22:42 +0000918 ping_exclude_vpAg,
919 ping_exclude_zCCP, /* hex-encoded iCCP */
glennrp8d3d6e52011-04-19 04:39:51 +0000920 ping_exclude_zTXt,
921 ping_preserve_colormap;
glennrp26f37912010-12-23 16:22:42 +0000922
cristy3ed852e2009-09-05 21:47:34 +0000923} MngInfo;
924#endif /* VER */
925
926/*
927 Forward declarations.
928*/
929static MagickBooleanType
930 WritePNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000931
cristy3ed852e2009-09-05 21:47:34 +0000932static MagickBooleanType
933 WriteMNGImage(const ImageInfo *,Image *);
glennrp0c3e06b2010-11-19 13:45:02 +0000934
cristy3ed852e2009-09-05 21:47:34 +0000935#if defined(JNG_SUPPORTED)
936static MagickBooleanType
937 WriteJNGImage(const ImageInfo *,Image *);
938#endif
939
glennrp0c3e06b2010-11-19 13:45:02 +0000940#if PNG_LIBPNG_VER > 10011
941
glennrpfd05d622011-02-25 04:10:33 +0000942
glennrp0c3e06b2010-11-19 13:45:02 +0000943#if (MAGICKCORE_QUANTUM_DEPTH >= 16)
944static MagickBooleanType
glennrpfd05d622011-02-25 04:10:33 +0000945LosslessReduceDepthOK(Image *image)
glennrp0c3e06b2010-11-19 13:45:02 +0000946{
glennrp67b9c1a2011-04-22 18:47:36 +0000947 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
948 *
949 * This is true if the high byte and the next highest byte of
950 * each sample of the image, the colormap, and the background color
glennrp3faa9a32011-04-23 14:00:25 +0000951 * are equal to each other. We check this by seeing if the samples
952 * are unchanged when we scale them down to 8 and back up to Quantum.
glennrp67b9c1a2011-04-22 18:47:36 +0000953 *
954 * We don't use the method GetImageDepth() because it doesn't check
glennrp3faa9a32011-04-23 14:00:25 +0000955 * background and doesn't handle PseudoClass specially.
glennrp67b9c1a2011-04-22 18:47:36 +0000956 */
957
glennrp3faa9a32011-04-23 14:00:25 +0000958#define QuantumToCharToQuantumEqQuantum(quantum) \
959 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
960
glennrp0c3e06b2010-11-19 13:45:02 +0000961 MagickBooleanType
962 ok_to_reduce=MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000963
glennrp03e11f62011-04-22 13:30:16 +0000964 if (image->depth >= 16)
glennrp0c3e06b2010-11-19 13:45:02 +0000965 {
966
cristy4c08aed2011-07-01 19:47:50 +0000967 const Quantum
glennrp0c3e06b2010-11-19 13:45:02 +0000968 *p;
969
970 ok_to_reduce=
glennrp3faa9a32011-04-23 14:00:25 +0000971 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
972 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
973 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
974 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +0000975
976 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
977 {
978 int indx;
979
980 for (indx=0; indx < (ssize_t) image->colors; indx++)
981 {
glennrp3faa9a32011-04-23 14:00:25 +0000982 ok_to_reduce=(
983 QuantumToCharToQuantumEqQuantum(
984 image->colormap[indx].red) &&
985 QuantumToCharToQuantumEqQuantum(
986 image->colormap[indx].green) &&
987 QuantumToCharToQuantumEqQuantum(
988 image->colormap[indx].blue)) ?
989 MagickTrue : MagickFalse;
990
glennrp0c3e06b2010-11-19 13:45:02 +0000991 if (ok_to_reduce == MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +0000992 break;
glennrp0c3e06b2010-11-19 13:45:02 +0000993 }
994 }
995
996 if ((ok_to_reduce != MagickFalse) &&
997 (image->storage_class != PseudoClass))
998 {
999 ssize_t
1000 y;
1001
1002 register ssize_t
1003 x;
1004
1005 for (y=0; y < (ssize_t) image->rows; y++)
1006 {
1007 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1008
cristy4c08aed2011-07-01 19:47:50 +00001009 if (p == (const Quantum *) NULL)
glennrp0c3e06b2010-11-19 13:45:02 +00001010 {
1011 ok_to_reduce = MagickFalse;
1012 break;
1013 }
1014
1015 for (x=(ssize_t) image->columns-1; x >= 0; x--)
1016 {
glennrp3faa9a32011-04-23 14:00:25 +00001017 ok_to_reduce=
cristy4c08aed2011-07-01 19:47:50 +00001018 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
1019 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
1020 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
glennrp3faa9a32011-04-23 14:00:25 +00001021 MagickTrue : MagickFalse;
glennrp0c3e06b2010-11-19 13:45:02 +00001022
1023 if (ok_to_reduce == MagickFalse)
1024 break;
1025
cristy4c08aed2011-07-01 19:47:50 +00001026 p+=GetPixelChannels(image);
glennrp0c3e06b2010-11-19 13:45:02 +00001027 }
glennrp8640fb52010-11-23 15:48:26 +00001028 if (x >= 0)
glennrp0c3e06b2010-11-19 13:45:02 +00001029 break;
1030 }
1031 }
1032
1033 if (ok_to_reduce != MagickFalse)
1034 {
glennrp0c3e06b2010-11-19 13:45:02 +00001035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001036 " OK to reduce PNG bit depth to 8 without loss of info");
glennrp0c3e06b2010-11-19 13:45:02 +00001037 }
glennrpa6a06632011-01-19 15:15:34 +00001038 else
1039 {
1040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00001041 " Not OK to reduce PNG bit depth to 8 without loss of info");
glennrpa6a06632011-01-19 15:15:34 +00001042 }
glennrp0c3e06b2010-11-19 13:45:02 +00001043 }
1044
1045 return ok_to_reduce;
1046}
1047#endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
1048
glennrpe610a072010-08-05 17:08:46 +00001049static int
glennrpcf002022011-01-30 02:38:15 +00001050Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
glennrp0fe50b42010-11-16 03:52:51 +00001051{
glennrpe610a072010-08-05 17:08:46 +00001052 switch (intent)
1053 {
1054 case PerceptualIntent:
1055 return 0;
glennrp0fe50b42010-11-16 03:52:51 +00001056
glennrpe610a072010-08-05 17:08:46 +00001057 case RelativeIntent:
1058 return 1;
glennrp0fe50b42010-11-16 03:52:51 +00001059
glennrpe610a072010-08-05 17:08:46 +00001060 case SaturationIntent:
1061 return 2;
glennrp0fe50b42010-11-16 03:52:51 +00001062
glennrpe610a072010-08-05 17:08:46 +00001063 case AbsoluteIntent:
1064 return 3;
glennrp0fe50b42010-11-16 03:52:51 +00001065
glennrpe610a072010-08-05 17:08:46 +00001066 default:
1067 return -1;
1068 }
1069}
1070
1071static RenderingIntent
glennrpcf002022011-01-30 02:38:15 +00001072Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
glennrp0fe50b42010-11-16 03:52:51 +00001073{
glennrpcf002022011-01-30 02:38:15 +00001074 switch (ping_intent)
glennrpe610a072010-08-05 17:08:46 +00001075 {
1076 case 0:
1077 return PerceptualIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001078
glennrpe610a072010-08-05 17:08:46 +00001079 case 1:
1080 return RelativeIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001081
glennrpe610a072010-08-05 17:08:46 +00001082 case 2:
1083 return SaturationIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001084
glennrpe610a072010-08-05 17:08:46 +00001085 case 3:
1086 return AbsoluteIntent;
glennrp0fe50b42010-11-16 03:52:51 +00001087
glennrpe610a072010-08-05 17:08:46 +00001088 default:
1089 return UndefinedIntent;
1090 }
1091}
1092
cristybb503372010-05-27 20:51:26 +00001093static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001094{
1095 if (x > y)
1096 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001097
cristy3ed852e2009-09-05 21:47:34 +00001098 return(y);
1099}
glennrp0c3e06b2010-11-19 13:45:02 +00001100
cristybb503372010-05-27 20:51:26 +00001101static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
cristy3ed852e2009-09-05 21:47:34 +00001102{
1103 if (x < y)
1104 return(x);
glennrp0fe50b42010-11-16 03:52:51 +00001105
cristy3ed852e2009-09-05 21:47:34 +00001106 return(y);
1107}
glennrp0c3e06b2010-11-19 13:45:02 +00001108
cristy3ed852e2009-09-05 21:47:34 +00001109
1110/*
1111%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1112% %
1113% %
1114% %
1115% I m a g e I s G r a y %
1116% %
1117% %
1118% %
1119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1120% %
cristy4c08aed2011-07-01 19:47:50 +00001121% Like IsImageGray except does not change DirectClass to PseudoClass %
cristy3ed852e2009-09-05 21:47:34 +00001122% %
1123%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1124*/
1125static MagickBooleanType ImageIsGray(Image *image)
1126{
cristy4c08aed2011-07-01 19:47:50 +00001127 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00001128 *p;
1129
cristybb503372010-05-27 20:51:26 +00001130 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001131 i,
1132 x,
1133 y;
1134
1135 assert(image != (Image *) NULL);
1136 assert(image->signature == MagickSignature);
1137 if (image->debug != MagickFalse)
1138 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1139
1140 if (image->storage_class == PseudoClass)
1141 {
cristybb503372010-05-27 20:51:26 +00001142 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00001143 if (IsPixelPacketGray(image->colormap+i) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001144 return(MagickFalse);
1145 return(MagickTrue);
1146 }
cristybb503372010-05-27 20:51:26 +00001147 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001148 {
1149 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
cristy4c08aed2011-07-01 19:47:50 +00001150 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001151 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +00001152 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001153 {
cristy4c08aed2011-07-01 19:47:50 +00001154 if (IsPixelGray(image,p) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001155 return(MagickFalse);
cristy4c08aed2011-07-01 19:47:50 +00001156 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001157 }
1158 }
1159 return(MagickTrue);
1160}
glennrpd5045b42010-03-24 12:40:35 +00001161#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001162#endif /* MAGICKCORE_PNG_DELEGATE */
1163
1164/*
1165%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1166% %
1167% %
1168% %
1169% I s M N G %
1170% %
1171% %
1172% %
1173%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1174%
1175% IsMNG() returns MagickTrue if the image format type, identified by the
1176% magick string, is MNG.
1177%
1178% The format of the IsMNG method is:
1179%
1180% MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1181%
1182% A description of each parameter follows:
1183%
1184% o magick: compare image format pattern against these bytes.
1185%
1186% o length: Specifies the length of the magick string.
1187%
1188%
1189*/
1190static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1191{
1192 if (length < 8)
1193 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001194
cristy3ed852e2009-09-05 21:47:34 +00001195 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1196 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001197
cristy3ed852e2009-09-05 21:47:34 +00001198 return(MagickFalse);
1199}
1200
1201/*
1202%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1203% %
1204% %
1205% %
1206% I s J N G %
1207% %
1208% %
1209% %
1210%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1211%
1212% IsJNG() returns MagickTrue if the image format type, identified by the
1213% magick string, is JNG.
1214%
1215% The format of the IsJNG method is:
1216%
1217% MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1218%
1219% A description of each parameter follows:
1220%
1221% o magick: compare image format pattern against these bytes.
1222%
1223% o length: Specifies the length of the magick string.
1224%
1225%
1226*/
1227static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1228{
1229 if (length < 8)
1230 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001231
cristy3ed852e2009-09-05 21:47:34 +00001232 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1233 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001234
cristy3ed852e2009-09-05 21:47:34 +00001235 return(MagickFalse);
1236}
1237
1238/*
1239%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1240% %
1241% %
1242% %
1243% I s P N G %
1244% %
1245% %
1246% %
1247%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1248%
1249% IsPNG() returns MagickTrue if the image format type, identified by the
1250% magick string, is PNG.
1251%
1252% The format of the IsPNG method is:
1253%
1254% MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1255%
1256% A description of each parameter follows:
1257%
1258% o magick: compare image format pattern against these bytes.
1259%
1260% o length: Specifies the length of the magick string.
1261%
1262*/
1263static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1264{
1265 if (length < 8)
1266 return(MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001267
cristy3ed852e2009-09-05 21:47:34 +00001268 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1269 return(MagickTrue);
glennrp0fe50b42010-11-16 03:52:51 +00001270
cristy3ed852e2009-09-05 21:47:34 +00001271 return(MagickFalse);
1272}
1273
1274#if defined(MAGICKCORE_PNG_DELEGATE)
1275#if defined(__cplusplus) || defined(c_plusplus)
1276extern "C" {
1277#endif
1278
glennrpd5045b42010-03-24 12:40:35 +00001279#if (PNG_LIBPNG_VER > 10011)
cristybb503372010-05-27 20:51:26 +00001280static size_t WriteBlobMSBULong(Image *image,const size_t value)
cristy3ed852e2009-09-05 21:47:34 +00001281{
1282 unsigned char
1283 buffer[4];
1284
1285 assert(image != (Image *) NULL);
1286 assert(image->signature == MagickSignature);
1287 buffer[0]=(unsigned char) (value >> 24);
1288 buffer[1]=(unsigned char) (value >> 16);
1289 buffer[2]=(unsigned char) (value >> 8);
1290 buffer[3]=(unsigned char) value;
1291 return((size_t) WriteBlob(image,4,buffer));
1292}
1293
1294static void PNGLong(png_bytep p,png_uint_32 value)
1295{
1296 *p++=(png_byte) ((value >> 24) & 0xff);
1297 *p++=(png_byte) ((value >> 16) & 0xff);
1298 *p++=(png_byte) ((value >> 8) & 0xff);
1299 *p++=(png_byte) (value & 0xff);
1300}
1301
glennrpa521b2f2010-10-29 04:11:03 +00001302#if defined(JNG_SUPPORTED)
cristy3ed852e2009-09-05 21:47:34 +00001303static void PNGsLong(png_bytep p,png_int_32 value)
1304{
1305 *p++=(png_byte) ((value >> 24) & 0xff);
1306 *p++=(png_byte) ((value >> 16) & 0xff);
1307 *p++=(png_byte) ((value >> 8) & 0xff);
1308 *p++=(png_byte) (value & 0xff);
1309}
glennrpa521b2f2010-10-29 04:11:03 +00001310#endif
cristy3ed852e2009-09-05 21:47:34 +00001311
1312static void PNGShort(png_bytep p,png_uint_16 value)
1313{
1314 *p++=(png_byte) ((value >> 8) & 0xff);
1315 *p++=(png_byte) (value & 0xff);
1316}
1317
1318static void PNGType(png_bytep p,png_bytep type)
1319{
1320 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1321}
1322
glennrp03812ae2010-12-24 01:31:34 +00001323static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1324 size_t length)
cristy3ed852e2009-09-05 21:47:34 +00001325{
1326 if (logging != MagickFalse)
1327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00001328 " Writing %c%c%c%c chunk, length: %.20g",
1329 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00001330}
glennrpd5045b42010-03-24 12:40:35 +00001331#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00001332
1333#if defined(__cplusplus) || defined(c_plusplus)
1334}
1335#endif
1336
glennrpd5045b42010-03-24 12:40:35 +00001337#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00001338/*
1339%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1340% %
1341% %
1342% %
1343% R e a d P N G I m a g e %
1344% %
1345% %
1346% %
1347%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1348%
1349% ReadPNGImage() reads a Portable Network Graphics (PNG) or
1350% Multiple-image Network Graphics (MNG) image file and returns it. It
1351% allocates the memory necessary for the new Image structure and returns a
1352% pointer to the new image or set of images.
1353%
1354% MNG support written by Glenn Randers-Pehrson, glennrp@image...
1355%
1356% The format of the ReadPNGImage method is:
1357%
1358% Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1359%
1360% A description of each parameter follows:
1361%
1362% o image_info: the image info.
1363%
1364% o exception: return any errors or warnings in this structure.
1365%
1366% To do, more or less in chronological order (as of version 5.5.2,
1367% November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1368%
1369% Get 16-bit cheap transparency working.
1370%
1371% (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1372%
1373% Preserve all unknown and not-yet-handled known chunks found in input
1374% PNG file and copy them into output PNG files according to the PNG
1375% copying rules.
1376%
1377% (At this point, PNG encoding should be in full MNG compliance)
1378%
1379% Provide options for choice of background to use when the MNG BACK
1380% chunk is not present or is not mandatory (i.e., leave transparent,
1381% user specified, MNG BACK, PNG bKGD)
1382%
1383% Implement LOOP/ENDL [done, but could do discretionary loops more
1384% efficiently by linking in the duplicate frames.].
1385%
1386% Decode and act on the MHDR simplicity profile (offer option to reject
1387% files or attempt to process them anyway when the profile isn't LC or VLC).
1388%
1389% Upgrade to full MNG without Delta-PNG.
1390%
1391% o BACK [done a while ago except for background image ID]
1392% o MOVE [done 15 May 1999]
1393% o CLIP [done 15 May 1999]
1394% o DISC [done 19 May 1999]
1395% o SAVE [partially done 19 May 1999 (marks objects frozen)]
1396% o SEEK [partially done 19 May 1999 (discard function only)]
1397% o SHOW
1398% o PAST
1399% o BASI
1400% o MNG-level tEXt/iTXt/zTXt
1401% o pHYg
1402% o pHYs
1403% o sBIT
1404% o bKGD
1405% o iTXt (wait for libpng implementation).
1406%
1407% Use the scene signature to discover when an identical scene is
1408% being reused, and just point to the original image->exception instead
1409% of storing another set of pixels. This not specific to MNG
1410% but could be applied generally.
1411%
1412% Upgrade to full MNG with Delta-PNG.
1413%
1414% JNG tEXt/iTXt/zTXt
1415%
1416% We will not attempt to read files containing the CgBI chunk.
1417% They are really Xcode files meant for display on the iPhone.
1418% These are not valid PNG files and it is impossible to recover
glennrpf54e2952011-06-29 14:20:44 +00001419% the original PNG from files that have been converted to Xcode-PNG,
cristy3ed852e2009-09-05 21:47:34 +00001420% since irretrievable loss of color data has occurred due to the
1421% use of premultiplied alpha.
1422*/
1423
1424#if defined(__cplusplus) || defined(c_plusplus)
1425extern "C" {
1426#endif
1427
1428/*
1429 This the function that does the actual reading of data. It is
1430 the same as the one supplied in libpng, except that it receives the
1431 datastream from the ReadBlob() function instead of standard input.
1432*/
1433static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1434{
1435 Image
1436 *image;
1437
1438 image=(Image *) png_get_io_ptr(png_ptr);
1439 if (length)
1440 {
1441 png_size_t
1442 check;
1443
1444 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1445 if (check != length)
1446 {
1447 char
1448 msg[MaxTextExtent];
1449
cristy3b6fd2e2011-05-20 12:53:50 +00001450 (void) FormatLocaleString(msg,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001451 "Expected %.20g bytes; found %.20g bytes",(double) length,
1452 (double) check);
cristy3ed852e2009-09-05 21:47:34 +00001453 png_warning(png_ptr,msg);
1454 png_error(png_ptr,"Read Exception");
1455 }
1456 }
1457}
1458
1459#if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1460 !defined(PNG_MNG_FEATURES_SUPPORTED)
1461/* We use mng_get_data() instead of png_get_data() if we have a libpng
1462 * older than libpng-1.0.3a, which was the first to allow the empty
1463 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1464 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1465 * encountered after an empty PLTE, so we have to look ahead for bKGD
1466 * chunks and remove them from the datastream that is passed to libpng,
1467 * and store their contents for later use.
1468 */
1469static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1470{
1471 MngInfo
1472 *mng_info;
1473
1474 Image
1475 *image;
1476
1477 png_size_t
1478 check;
1479
cristybb503372010-05-27 20:51:26 +00001480 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001481 i;
1482
1483 i=0;
1484 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1485 image=(Image *) mng_info->image;
1486 while (mng_info->bytes_in_read_buffer && length)
1487 {
1488 data[i]=mng_info->read_buffer[i];
1489 mng_info->bytes_in_read_buffer--;
1490 length--;
1491 i++;
1492 }
1493 if (length)
1494 {
1495 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
glennrp0fe50b42010-11-16 03:52:51 +00001496
cristy3ed852e2009-09-05 21:47:34 +00001497 if (check != length)
1498 png_error(png_ptr,"Read Exception");
glennrp0fe50b42010-11-16 03:52:51 +00001499
cristy3ed852e2009-09-05 21:47:34 +00001500 if (length == 4)
1501 {
1502 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1503 (data[3] == 0))
1504 {
1505 check=(png_size_t) ReadBlob(image,(size_t) length,
1506 (char *) mng_info->read_buffer);
1507 mng_info->read_buffer[4]=0;
1508 mng_info->bytes_in_read_buffer=4;
1509 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1510 mng_info->found_empty_plte=MagickTrue;
1511 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1512 {
1513 mng_info->found_empty_plte=MagickFalse;
1514 mng_info->have_saved_bkgd_index=MagickFalse;
1515 }
1516 }
glennrp0fe50b42010-11-16 03:52:51 +00001517
cristy3ed852e2009-09-05 21:47:34 +00001518 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1519 (data[3] == 1))
1520 {
1521 check=(png_size_t) ReadBlob(image,(size_t) length,
1522 (char *) mng_info->read_buffer);
1523 mng_info->read_buffer[4]=0;
1524 mng_info->bytes_in_read_buffer=4;
1525 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1526 if (mng_info->found_empty_plte)
1527 {
1528 /*
1529 Skip the bKGD data byte and CRC.
1530 */
1531 check=(png_size_t)
1532 ReadBlob(image,5,(char *) mng_info->read_buffer);
1533 check=(png_size_t) ReadBlob(image,(size_t) length,
1534 (char *) mng_info->read_buffer);
1535 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1536 mng_info->have_saved_bkgd_index=MagickTrue;
1537 mng_info->bytes_in_read_buffer=0;
1538 }
1539 }
1540 }
1541 }
1542}
1543#endif
1544
1545static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1546{
1547 Image
1548 *image;
1549
1550 image=(Image *) png_get_io_ptr(png_ptr);
1551 if (length)
1552 {
1553 png_size_t
1554 check;
1555
cristybb503372010-05-27 20:51:26 +00001556 check=(png_size_t) WriteBlob(image,(size_t) length,data);
glennrp0fe50b42010-11-16 03:52:51 +00001557
cristy3ed852e2009-09-05 21:47:34 +00001558 if (check != length)
1559 png_error(png_ptr,"WriteBlob Failed");
1560 }
1561}
1562
1563static void png_flush_data(png_structp png_ptr)
1564{
1565 (void) png_ptr;
1566}
1567
1568#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1569static int PalettesAreEqual(Image *a,Image *b)
1570{
cristybb503372010-05-27 20:51:26 +00001571 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001572 i;
1573
1574 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1575 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001576
cristy3ed852e2009-09-05 21:47:34 +00001577 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1578 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001579
cristy3ed852e2009-09-05 21:47:34 +00001580 if (a->colors != b->colors)
1581 return((int) MagickFalse);
glennrp0fe50b42010-11-16 03:52:51 +00001582
cristybb503372010-05-27 20:51:26 +00001583 for (i=0; i < (ssize_t) a->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001584 {
1585 if ((a->colormap[i].red != b->colormap[i].red) ||
1586 (a->colormap[i].green != b->colormap[i].green) ||
1587 (a->colormap[i].blue != b->colormap[i].blue))
1588 return((int) MagickFalse);
1589 }
glennrp0fe50b42010-11-16 03:52:51 +00001590
cristy3ed852e2009-09-05 21:47:34 +00001591 return((int) MagickTrue);
1592}
1593#endif
1594
1595static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1596{
1597 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1598 mng_info->exists[i] && !mng_info->frozen[i])
1599 {
1600#ifdef MNG_OBJECT_BUFFERS
1601 if (mng_info->ob[i] != (MngBuffer *) NULL)
1602 {
1603 if (mng_info->ob[i]->reference_count > 0)
1604 mng_info->ob[i]->reference_count--;
glennrp0fe50b42010-11-16 03:52:51 +00001605
cristy3ed852e2009-09-05 21:47:34 +00001606 if (mng_info->ob[i]->reference_count == 0)
1607 {
1608 if (mng_info->ob[i]->image != (Image *) NULL)
1609 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
glennrp0fe50b42010-11-16 03:52:51 +00001610
cristy3ed852e2009-09-05 21:47:34 +00001611 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1612 }
1613 }
1614 mng_info->ob[i]=(MngBuffer *) NULL;
1615#endif
1616 mng_info->exists[i]=MagickFalse;
1617 mng_info->invisible[i]=MagickFalse;
1618 mng_info->viewable[i]=MagickFalse;
1619 mng_info->frozen[i]=MagickFalse;
1620 mng_info->x_off[i]=0;
1621 mng_info->y_off[i]=0;
1622 mng_info->object_clip[i].left=0;
cristybb503372010-05-27 20:51:26 +00001623 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001624 mng_info->object_clip[i].top=0;
cristybb503372010-05-27 20:51:26 +00001625 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00001626 }
1627}
1628
glennrp21f0e622011-01-07 16:20:57 +00001629static void MngInfoFreeStruct(MngInfo *mng_info,
1630 MagickBooleanType *have_mng_structure)
cristy3ed852e2009-09-05 21:47:34 +00001631{
glennrp21f0e622011-01-07 16:20:57 +00001632 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00001633 {
cristybb503372010-05-27 20:51:26 +00001634 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001635 i;
1636
1637 for (i=1; i < MNG_MAX_OBJECTS; i++)
1638 MngInfoDiscardObject(mng_info,i);
glennrp0fe50b42010-11-16 03:52:51 +00001639
cristy3ed852e2009-09-05 21:47:34 +00001640 if (mng_info->global_plte != (png_colorp) NULL)
1641 mng_info->global_plte=(png_colorp)
1642 RelinquishMagickMemory(mng_info->global_plte);
glennrp0fe50b42010-11-16 03:52:51 +00001643
cristy3ed852e2009-09-05 21:47:34 +00001644 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1645 *have_mng_structure=MagickFalse;
1646 }
1647}
1648
1649static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1650{
1651 MngBox
1652 box;
1653
1654 box=box1;
1655 if (box.left < box2.left)
1656 box.left=box2.left;
glennrp0fe50b42010-11-16 03:52:51 +00001657
cristy3ed852e2009-09-05 21:47:34 +00001658 if (box.top < box2.top)
1659 box.top=box2.top;
glennrp0fe50b42010-11-16 03:52:51 +00001660
cristy3ed852e2009-09-05 21:47:34 +00001661 if (box.right > box2.right)
1662 box.right=box2.right;
glennrp0fe50b42010-11-16 03:52:51 +00001663
cristy3ed852e2009-09-05 21:47:34 +00001664 if (box.bottom > box2.bottom)
1665 box.bottom=box2.bottom;
glennrp0fe50b42010-11-16 03:52:51 +00001666
cristy3ed852e2009-09-05 21:47:34 +00001667 return box;
1668}
1669
1670static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1671{
1672 MngBox
1673 box;
1674
1675 /*
1676 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1677 */
cristybb503372010-05-27 20:51:26 +00001678 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1679 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1680 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1681 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
cristy3ed852e2009-09-05 21:47:34 +00001682 if (delta_type != 0)
1683 {
1684 box.left+=previous_box.left;
1685 box.right+=previous_box.right;
1686 box.top+=previous_box.top;
1687 box.bottom+=previous_box.bottom;
1688 }
glennrp0fe50b42010-11-16 03:52:51 +00001689
cristy3ed852e2009-09-05 21:47:34 +00001690 return(box);
1691}
1692
1693static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1694 unsigned char *p)
1695{
1696 MngPair
1697 pair;
1698 /*
cristybb503372010-05-27 20:51:26 +00001699 Read two ssize_ts from CLON, MOVE or PAST chunk
cristy3ed852e2009-09-05 21:47:34 +00001700 */
cristy8182b072010-05-30 20:10:53 +00001701 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1702 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00001703
cristy3ed852e2009-09-05 21:47:34 +00001704 if (delta_type != 0)
1705 {
1706 pair.a+=previous_pair.a;
1707 pair.b+=previous_pair.b;
1708 }
glennrp0fe50b42010-11-16 03:52:51 +00001709
cristy3ed852e2009-09-05 21:47:34 +00001710 return(pair);
1711}
1712
cristy8182b072010-05-30 20:10:53 +00001713static long mng_get_long(unsigned char *p)
cristy3ed852e2009-09-05 21:47:34 +00001714{
cristy8182b072010-05-30 20:10:53 +00001715 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
cristy3ed852e2009-09-05 21:47:34 +00001716}
1717
glennrpcf002022011-01-30 02:38:15 +00001718static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001719{
1720 Image
1721 *image;
1722
1723 image=(Image *) png_get_error_ptr(ping);
glennrp0fe50b42010-11-16 03:52:51 +00001724
cristy3ed852e2009-09-05 21:47:34 +00001725 if (image->debug != MagickFalse)
1726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1727 " libpng-%s error: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001728
cristy3ed852e2009-09-05 21:47:34 +00001729 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderError,
1730 message,"`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00001731
glennrpe4017e32011-01-08 17:16:09 +00001732#if (PNG_LIBPNG_VER < 10500)
glennrp8371ecc2011-02-19 19:15:38 +00001733 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1734 * are building with libpng-1.4.x and can be ignored.
1735 */
cristy3ed852e2009-09-05 21:47:34 +00001736 longjmp(ping->jmpbuf,1);
glennrpfaa852b2010-03-30 12:17:00 +00001737#else
1738 png_longjmp(ping,1);
1739#endif
cristy3ed852e2009-09-05 21:47:34 +00001740}
1741
glennrpcf002022011-01-30 02:38:15 +00001742static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
cristy3ed852e2009-09-05 21:47:34 +00001743{
1744 Image
1745 *image;
1746
1747 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1748 png_error(ping, message);
glennrp0fe50b42010-11-16 03:52:51 +00001749
cristy3ed852e2009-09-05 21:47:34 +00001750 image=(Image *) png_get_error_ptr(ping);
1751 if (image->debug != MagickFalse)
1752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristycc23b9a2010-05-09 22:37:43 +00001753 " libpng-%s warning: %s", PNG_LIBPNG_VER_STRING,message);
glennrp0fe50b42010-11-16 03:52:51 +00001754
cristy3ed852e2009-09-05 21:47:34 +00001755 (void) ThrowMagickException(&image->exception,GetMagickModule(),CoderWarning,
1756 message,"`%s'",image->filename);
1757}
1758
1759#ifdef PNG_USER_MEM_SUPPORTED
glennrpcf002022011-01-30 02:38:15 +00001760static png_voidp Magick_png_malloc(png_structp png_ptr,png_uint_32 size)
cristy3ed852e2009-09-05 21:47:34 +00001761{
1762#if (PNG_LIBPNG_VER < 10011)
1763 png_voidp
1764 ret;
1765
1766 png_ptr=png_ptr;
1767 ret=((png_voidp) AcquireMagickMemory((size_t) size));
glennrp0fe50b42010-11-16 03:52:51 +00001768
cristy3ed852e2009-09-05 21:47:34 +00001769 if (ret == NULL)
1770 png_error("Insufficient memory.");
glennrp0fe50b42010-11-16 03:52:51 +00001771
cristy3ed852e2009-09-05 21:47:34 +00001772 return(ret);
1773#else
1774 png_ptr=png_ptr;
1775 return((png_voidp) AcquireMagickMemory((size_t) size));
1776#endif
1777}
1778
1779/*
1780 Free a pointer. It is removed from the list at the same time.
1781*/
glennrpcf002022011-01-30 02:38:15 +00001782static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
cristy3ed852e2009-09-05 21:47:34 +00001783{
1784 png_ptr=png_ptr;
1785 ptr=RelinquishMagickMemory(ptr);
1786 return((png_free_ptr) NULL);
1787}
1788#endif
1789
1790#if defined(__cplusplus) || defined(c_plusplus)
1791}
1792#endif
1793
1794static int
glennrpcf002022011-01-30 02:38:15 +00001795Magick_png_read_raw_profile(Image *image, const ImageInfo *image_info,
cristy3ed852e2009-09-05 21:47:34 +00001796 png_textp text,int ii)
1797{
cristybb503372010-05-27 20:51:26 +00001798 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001799 i;
1800
1801 register unsigned char
1802 *dp;
1803
1804 register png_charp
1805 sp;
1806
1807 png_uint_32
1808 length,
1809 nibbles;
1810
1811 StringInfo
1812 *profile;
1813
glennrp0c3e06b2010-11-19 13:45:02 +00001814 const unsigned char
cristy3ed852e2009-09-05 21:47:34 +00001815 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1816 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1817 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1818 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1819 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1820 13,14,15};
1821
1822 sp=text[ii].text+1;
1823 /* look for newline */
1824 while (*sp != '\n')
1825 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001826
cristy3ed852e2009-09-05 21:47:34 +00001827 /* look for length */
1828 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1829 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001830
cristyf2f27272009-12-17 14:48:46 +00001831 length=(png_uint_32) StringToLong(sp);
glennrp0fe50b42010-11-16 03:52:51 +00001832
glennrp97f90e22011-02-22 05:47:58 +00001833 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1834 " length: %lu",(unsigned long) length);
1835
cristy3ed852e2009-09-05 21:47:34 +00001836 while (*sp != ' ' && *sp != '\n')
1837 sp++;
glennrp0fe50b42010-11-16 03:52:51 +00001838
cristy3ed852e2009-09-05 21:47:34 +00001839 /* allocate space */
1840 if (length == 0)
1841 {
1842 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1843 CoderWarning,"UnableToCopyProfile","`%s'","invalid profile length");
1844 return(MagickFalse);
1845 }
glennrp0fe50b42010-11-16 03:52:51 +00001846
cristy3ed852e2009-09-05 21:47:34 +00001847 profile=AcquireStringInfo(length);
glennrp0fe50b42010-11-16 03:52:51 +00001848
cristy3ed852e2009-09-05 21:47:34 +00001849 if (profile == (StringInfo *) NULL)
1850 {
1851 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1852 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1853 "unable to copy profile");
1854 return(MagickFalse);
1855 }
glennrp0fe50b42010-11-16 03:52:51 +00001856
cristy3ed852e2009-09-05 21:47:34 +00001857 /* copy profile, skipping white space and column 1 "=" signs */
1858 dp=GetStringInfoDatum(profile);
1859 nibbles=length*2;
glennrp0fe50b42010-11-16 03:52:51 +00001860
cristybb503372010-05-27 20:51:26 +00001861 for (i=0; i < (ssize_t) nibbles; i++)
cristy3ed852e2009-09-05 21:47:34 +00001862 {
1863 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1864 {
1865 if (*sp == '\0')
1866 {
1867 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1868 CoderWarning,"UnableToCopyProfile","`%s'","ran out of data");
1869 profile=DestroyStringInfo(profile);
1870 return(MagickFalse);
1871 }
1872 sp++;
1873 }
glennrp0fe50b42010-11-16 03:52:51 +00001874
cristy3ed852e2009-09-05 21:47:34 +00001875 if (i%2 == 0)
1876 *dp=(unsigned char) (16*unhex[(int) *sp++]);
glennrp0fe50b42010-11-16 03:52:51 +00001877
cristy3ed852e2009-09-05 21:47:34 +00001878 else
1879 (*dp++)+=unhex[(int) *sp++];
1880 }
1881 /*
1882 We have already read "Raw profile type.
1883 */
1884 (void) SetImageProfile(image,&text[ii].key[17],profile);
1885 profile=DestroyStringInfo(profile);
glennrp0fe50b42010-11-16 03:52:51 +00001886
cristy3ed852e2009-09-05 21:47:34 +00001887 if (image_info->verbose)
1888 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
glennrp0fe50b42010-11-16 03:52:51 +00001889
cristy3ed852e2009-09-05 21:47:34 +00001890 return MagickTrue;
1891}
1892
1893#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1894static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1895{
1896 Image
1897 *image;
1898
1899
1900 /* The unknown chunk structure contains the chunk data:
1901 png_byte name[5];
1902 png_byte *data;
1903 png_size_t size;
1904
1905 Note that libpng has already taken care of the CRC handling.
1906 */
1907
1908
1909 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1910 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1911 return(0); /* Did not recognize */
1912
1913 /* recognized vpAg */
1914
1915 if (chunk->size != 9)
1916 return(-1); /* Error return */
1917
1918 if (chunk->data[8] != 0)
1919 return(0); /* ImageMagick requires pixel units */
1920
1921 image=(Image *) png_get_user_chunk_ptr(ping);
1922
cristybb503372010-05-27 20:51:26 +00001923 image->page.width=(size_t) ((chunk->data[0] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001924 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
glennrp0fe50b42010-11-16 03:52:51 +00001925
cristybb503372010-05-27 20:51:26 +00001926 image->page.height=(size_t) ((chunk->data[4] << 24) |
cristy3ed852e2009-09-05 21:47:34 +00001927 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1928
1929 /* Return one of the following: */
1930 /* return(-n); chunk had an error */
1931 /* return(0); did not recognize */
1932 /* return(n); success */
1933
1934 return(1);
1935
1936}
1937#endif
1938
1939/*
1940%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1941% %
1942% %
1943% %
1944% R e a d O n e P N G I m a g e %
1945% %
1946% %
1947% %
1948%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1949%
1950% ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1951% (minus the 8-byte signature) and returns it. It allocates the memory
1952% necessary for the new Image structure and returns a pointer to the new
1953% image.
1954%
1955% The format of the ReadOnePNGImage method is:
1956%
1957% Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1958% ExceptionInfo *exception)
1959%
1960% A description of each parameter follows:
1961%
1962% o mng_info: Specifies a pointer to a MngInfo structure.
1963%
1964% o image_info: the image info.
1965%
1966% o exception: return any errors or warnings in this structure.
1967%
1968*/
1969static Image *ReadOnePNGImage(MngInfo *mng_info,
1970 const ImageInfo *image_info, ExceptionInfo *exception)
1971{
1972 /* Read one PNG image */
1973
glennrpcc95c3f2011-04-18 16:46:48 +00001974 /* To do: Read the tIME chunk into the date:modify property */
1975 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1976
cristy3ed852e2009-09-05 21:47:34 +00001977 Image
1978 *image;
1979
1980 int
glennrp4eb39312011-03-30 21:34:55 +00001981 intent,
glennrpcb395ac2011-03-30 19:50:23 +00001982 num_raw_profiles,
cristy3ed852e2009-09-05 21:47:34 +00001983 num_text,
glennrp4eb39312011-03-30 21:34:55 +00001984 num_text_total,
cristy3ed852e2009-09-05 21:47:34 +00001985 num_passes,
glennrpfaa852b2010-03-30 12:17:00 +00001986 pass,
1987 ping_bit_depth,
1988 ping_color_type,
1989 ping_interlace_method,
1990 ping_compression_method,
1991 ping_filter_method,
glennrp4eb39312011-03-30 21:34:55 +00001992 ping_num_trans,
1993 unit_type;
1994
1995 double
1996 file_gamma;
cristy3ed852e2009-09-05 21:47:34 +00001997
glennrpa6a06632011-01-19 15:15:34 +00001998 LongPixelPacket
1999 transparent_color;
2000
cristy3ed852e2009-09-05 21:47:34 +00002001 MagickBooleanType
cristy4383ec82011-01-05 15:42:32 +00002002 logging,
cristy3ed852e2009-09-05 21:47:34 +00002003 status;
2004
glennrpfaa852b2010-03-30 12:17:00 +00002005 png_bytep
2006 ping_trans_alpha;
2007
2008 png_color_16p
2009 ping_background,
2010 ping_trans_color;
2011
cristy3ed852e2009-09-05 21:47:34 +00002012 png_info
2013 *end_info,
2014 *ping_info;
2015
2016 png_struct
2017 *ping;
2018
2019 png_textp
2020 text;
2021
glennrpfaa852b2010-03-30 12:17:00 +00002022 png_uint_32
2023 ping_height,
2024 ping_width,
glennrp4eb39312011-03-30 21:34:55 +00002025 ping_rowbytes,
2026 x_resolution,
2027 y_resolution;
glennrpfaa852b2010-03-30 12:17:00 +00002028
cristy3ed852e2009-09-05 21:47:34 +00002029 QuantumInfo
2030 *quantum_info;
2031
2032 unsigned char
glennrpcf002022011-01-30 02:38:15 +00002033 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00002034
cristybb503372010-05-27 20:51:26 +00002035 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002036 y;
2037
2038 register unsigned char
2039 *p;
2040
cristybb503372010-05-27 20:51:26 +00002041 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002042 i,
2043 x;
2044
cristy4c08aed2011-07-01 19:47:50 +00002045 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00002046 *q;
2047
2048 size_t
glennrp39992b42010-11-14 00:03:43 +00002049 length,
cristy3ed852e2009-09-05 21:47:34 +00002050 row_offset;
2051
cristyeb3b22a2011-03-31 20:16:11 +00002052 ssize_t
2053 j;
2054
cristy3ed852e2009-09-05 21:47:34 +00002055#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2056 png_byte unused_chunks[]=
2057 {
2058 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2059 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2060 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2061 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2062 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2063 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2064 };
2065#endif
2066
2067 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00002068 " Enter ReadOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00002069
2070#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002071 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002072#endif
2073
glennrp25c1e2b2010-03-25 01:39:56 +00002074#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +00002075 if (image_info->verbose)
2076 printf("Your PNG library (libpng-%s) is rather old.\n",
2077 PNG_LIBPNG_VER_STRING);
2078#endif
2079
glennrp61b4c952009-11-10 20:40:41 +00002080#if (PNG_LIBPNG_VER >= 10400)
2081# ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2082 if (image_info->verbose)
2083 {
2084 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2085 PNG_LIBPNG_VER_STRING);
2086 printf("Please update it.\n");
2087 }
2088# endif
2089#endif
2090
2091
cristyed552522009-10-16 14:04:35 +00002092 quantum_info = (QuantumInfo *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00002093 image=mng_info->image;
2094
glennrpa6a06632011-01-19 15:15:34 +00002095 if (logging != MagickFalse)
2096 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2097 " image->matte=%d",(int) image->matte);
2098
glennrp0e319732011-01-25 21:53:13 +00002099 /* Set to an out-of-range color unless tRNS chunk is present */
2100 transparent_color.red=65537;
2101 transparent_color.green=65537;
2102 transparent_color.blue=65537;
cristy4c08aed2011-07-01 19:47:50 +00002103 transparent_color.alpha=65537;
glennrp0e319732011-01-25 21:53:13 +00002104
glennrpcb395ac2011-03-30 19:50:23 +00002105 num_text = 0;
glennrp4eb39312011-03-30 21:34:55 +00002106 num_text_total = 0;
glennrpcb395ac2011-03-30 19:50:23 +00002107 num_raw_profiles = 0;
2108
cristy3ed852e2009-09-05 21:47:34 +00002109 /*
2110 Allocate the PNG structures
2111 */
2112#ifdef PNG_USER_MEM_SUPPORTED
2113 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING, image,
glennrpcf002022011-01-30 02:38:15 +00002114 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2115 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
cristy3ed852e2009-09-05 21:47:34 +00002116#else
2117 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00002118 MagickPNGErrorHandler,MagickPNGWarningHandler);
cristy3ed852e2009-09-05 21:47:34 +00002119#endif
2120 if (ping == (png_struct *) NULL)
2121 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002122
cristy3ed852e2009-09-05 21:47:34 +00002123 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002124
cristy3ed852e2009-09-05 21:47:34 +00002125 if (ping_info == (png_info *) NULL)
2126 {
2127 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2128 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2129 }
glennrp0fe50b42010-11-16 03:52:51 +00002130
cristy3ed852e2009-09-05 21:47:34 +00002131 end_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00002132
cristy3ed852e2009-09-05 21:47:34 +00002133 if (end_info == (png_info *) NULL)
2134 {
2135 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2136 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2137 }
glennrp0fe50b42010-11-16 03:52:51 +00002138
glennrpcf002022011-01-30 02:38:15 +00002139 ping_pixels=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00002140
glennrpfaa852b2010-03-30 12:17:00 +00002141 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002142 {
2143 /*
2144 PNG image is corrupt.
2145 */
2146 png_destroy_read_struct(&ping,&ping_info,&end_info);
2147#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002148 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002149#endif
2150 if (logging != MagickFalse)
2151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2152 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002153
cristy3ed852e2009-09-05 21:47:34 +00002154 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002155 {
2156 InheritException(exception,&image->exception);
2157 image->columns=0;
2158 }
glennrp0fe50b42010-11-16 03:52:51 +00002159
cristy3ed852e2009-09-05 21:47:34 +00002160 return(GetFirstImageInList(image));
2161 }
2162 /*
2163 Prepare PNG for reading.
2164 */
glennrpfaa852b2010-03-30 12:17:00 +00002165
cristy3ed852e2009-09-05 21:47:34 +00002166 mng_info->image_found++;
2167 png_set_sig_bytes(ping,8);
glennrp0fe50b42010-11-16 03:52:51 +00002168
cristy3ed852e2009-09-05 21:47:34 +00002169 if (LocaleCompare(image_info->magick,"MNG") == 0)
2170 {
2171#if defined(PNG_MNG_FEATURES_SUPPORTED)
2172 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2173 png_set_read_fn(ping,image,png_get_data);
2174#else
2175#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2176 png_permit_empty_plte(ping,MagickTrue);
2177 png_set_read_fn(ping,image,png_get_data);
2178#else
2179 mng_info->image=image;
2180 mng_info->bytes_in_read_buffer=0;
2181 mng_info->found_empty_plte=MagickFalse;
2182 mng_info->have_saved_bkgd_index=MagickFalse;
2183 png_set_read_fn(ping,mng_info,mng_get_data);
2184#endif
2185#endif
2186 }
glennrp0fe50b42010-11-16 03:52:51 +00002187
cristy3ed852e2009-09-05 21:47:34 +00002188 else
2189 png_set_read_fn(ping,image,png_get_data);
2190
2191#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2192 /* Ignore unused chunks and all unknown chunks except for vpAg */
2193 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2194 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2195 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2196 (int)sizeof(unused_chunks)/5);
2197 /* Callback for other unknown chunks */
2198 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2199#endif
2200
glennrp991e92a2010-01-28 03:09:00 +00002201#if (PNG_LIBPNG_VER < 10400)
2202# if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2203 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
cristy3ed852e2009-09-05 21:47:34 +00002204 /* Disable thread-unsafe features of pnggccrd */
2205 if (png_access_version_number() >= 10200)
2206 {
2207 png_uint_32 mmx_disable_mask=0;
2208 png_uint_32 asm_flags;
2209
2210 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2211 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2212 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2213 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2214 asm_flags=png_get_asm_flags(ping);
2215 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2216 }
glennrp991e92a2010-01-28 03:09:00 +00002217# endif
cristy3ed852e2009-09-05 21:47:34 +00002218#endif
2219
2220 png_read_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002221
2222 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2223 &ping_bit_depth,&ping_color_type,
2224 &ping_interlace_method,&ping_compression_method,
2225 &ping_filter_method);
2226
2227 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2228 &ping_trans_color);
2229
2230 (void) png_get_bKGD(ping, ping_info, &ping_background);
2231
2232 if (ping_bit_depth < 8)
cristy3ed852e2009-09-05 21:47:34 +00002233 {
glennrpfaa852b2010-03-30 12:17:00 +00002234 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2235 {
2236 png_set_packing(ping);
2237 ping_bit_depth = 8;
2238 }
cristy3ed852e2009-09-05 21:47:34 +00002239 }
glennrpfaa852b2010-03-30 12:17:00 +00002240
2241 image->depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002242 image->depth=GetImageQuantumDepth(image,MagickFalse);
glennrpfaa852b2010-03-30 12:17:00 +00002243 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00002244 if (logging != MagickFalse)
2245 {
2246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002247 " PNG width: %.20g, height: %.20g",
2248 (double) ping_width, (double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +00002249
cristy3ed852e2009-09-05 21:47:34 +00002250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2251 " PNG color_type: %d, bit_depth: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002252 ping_color_type, ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +00002253
cristy3ed852e2009-09-05 21:47:34 +00002254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2255 " PNG compression_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002256 ping_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +00002257
cristy3ed852e2009-09-05 21:47:34 +00002258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2259 " PNG interlace_method: %d, filter_method: %d",
glennrpfaa852b2010-03-30 12:17:00 +00002260 ping_interlace_method,ping_filter_method);
cristy3ed852e2009-09-05 21:47:34 +00002261 }
2262
glennrpfaa852b2010-03-30 12:17:00 +00002263#ifdef PNG_READ_iCCP_SUPPORTED
2264 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
cristy3ed852e2009-09-05 21:47:34 +00002265 {
2266 int
2267 compression;
2268
glennrpe4017e32011-01-08 17:16:09 +00002269#if (PNG_LIBPNG_VER < 10500)
cristy3ed852e2009-09-05 21:47:34 +00002270 png_charp
glennrpe4017e32011-01-08 17:16:09 +00002271 info;
2272#else
2273 png_bytep
2274 info;
2275#endif
2276
2277 png_charp
cristy3ed852e2009-09-05 21:47:34 +00002278 name;
2279
2280 png_uint_32
2281 profile_length;
2282
2283 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2284 &profile_length);
glennrp0fe50b42010-11-16 03:52:51 +00002285
cristy3ed852e2009-09-05 21:47:34 +00002286 if (profile_length != 0)
2287 {
2288 StringInfo
2289 *profile;
2290
2291 if (logging != MagickFalse)
2292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2293 " Reading PNG iCCP chunk.");
2294 profile=AcquireStringInfo(profile_length);
2295 SetStringInfoDatum(profile,(const unsigned char *) info);
2296 (void) SetImageProfile(image,"icc",profile);
2297 profile=DestroyStringInfo(profile);
2298 }
2299 }
2300#endif
2301#if defined(PNG_READ_sRGB_SUPPORTED)
2302 {
cristy3ed852e2009-09-05 21:47:34 +00002303 if (mng_info->have_global_srgb)
glennrpcf002022011-01-30 02:38:15 +00002304 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2305 (mng_info->global_srgb_intent);
glennrp0fe50b42010-11-16 03:52:51 +00002306
cristy3ed852e2009-09-05 21:47:34 +00002307 if (png_get_sRGB(ping,ping_info,&intent))
2308 {
glennrpcf002022011-01-30 02:38:15 +00002309 image->rendering_intent=Magick_RenderingIntent_from_PNG_RenderingIntent
2310 (intent);
glennrp0fe50b42010-11-16 03:52:51 +00002311
cristy3ed852e2009-09-05 21:47:34 +00002312 if (logging != MagickFalse)
2313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe610a072010-08-05 17:08:46 +00002314 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
cristy3ed852e2009-09-05 21:47:34 +00002315 }
2316 }
2317#endif
2318 {
glennrpfaa852b2010-03-30 12:17:00 +00002319 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2320 if (mng_info->have_global_gama)
2321 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
glennrp0fe50b42010-11-16 03:52:51 +00002322
cristy3ed852e2009-09-05 21:47:34 +00002323 if (png_get_gAMA(ping,ping_info,&file_gamma))
2324 {
2325 image->gamma=(float) file_gamma;
2326 if (logging != MagickFalse)
2327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2328 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2329 }
2330 }
glennrpfaa852b2010-03-30 12:17:00 +00002331 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2332 {
2333 if (mng_info->have_global_chrm != MagickFalse)
2334 {
2335 (void) png_set_cHRM(ping,ping_info,
2336 mng_info->global_chrm.white_point.x,
2337 mng_info->global_chrm.white_point.y,
2338 mng_info->global_chrm.red_primary.x,
2339 mng_info->global_chrm.red_primary.y,
2340 mng_info->global_chrm.green_primary.x,
2341 mng_info->global_chrm.green_primary.y,
2342 mng_info->global_chrm.blue_primary.x,
2343 mng_info->global_chrm.blue_primary.y);
2344 }
2345 }
glennrp0fe50b42010-11-16 03:52:51 +00002346
glennrpfaa852b2010-03-30 12:17:00 +00002347 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
cristy3ed852e2009-09-05 21:47:34 +00002348 {
2349 (void) png_get_cHRM(ping,ping_info,
2350 &image->chromaticity.white_point.x,
2351 &image->chromaticity.white_point.y,
2352 &image->chromaticity.red_primary.x,
2353 &image->chromaticity.red_primary.y,
2354 &image->chromaticity.green_primary.x,
2355 &image->chromaticity.green_primary.y,
2356 &image->chromaticity.blue_primary.x,
2357 &image->chromaticity.blue_primary.y);
glennrp0fe50b42010-11-16 03:52:51 +00002358
cristy3ed852e2009-09-05 21:47:34 +00002359 if (logging != MagickFalse)
2360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2361 " Reading PNG cHRM chunk.");
2362 }
glennrp0fe50b42010-11-16 03:52:51 +00002363
glennrpe610a072010-08-05 17:08:46 +00002364 if (image->rendering_intent != UndefinedIntent)
cristy3ed852e2009-09-05 21:47:34 +00002365 {
glennrpe610a072010-08-05 17:08:46 +00002366 png_set_sRGB(ping,ping_info,
glennrpcf002022011-01-30 02:38:15 +00002367 Magick_RenderingIntent_to_PNG_RenderingIntent
2368 (image->rendering_intent));
glennrpfaa852b2010-03-30 12:17:00 +00002369 png_set_gAMA(ping,ping_info,0.45455f);
2370 png_set_cHRM(ping,ping_info,
2371 0.6400f, 0.3300f, 0.3000f, 0.6000f,
2372 0.1500f, 0.0600f, 0.3127f, 0.3290f);
cristy3ed852e2009-09-05 21:47:34 +00002373 }
cristy3ed852e2009-09-05 21:47:34 +00002374#if defined(PNG_oFFs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002375 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
cristy3ed852e2009-09-05 21:47:34 +00002376 {
cristy905ef802011-02-23 00:29:18 +00002377 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2378 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
glennrp0fe50b42010-11-16 03:52:51 +00002379
cristy3ed852e2009-09-05 21:47:34 +00002380 if (logging != MagickFalse)
2381 if (image->page.x || image->page.y)
2382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002383 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2384 image->page.x,(double) image->page.y);
cristy3ed852e2009-09-05 21:47:34 +00002385 }
2386#endif
2387#if defined(PNG_pHYs_SUPPORTED)
glennrpfaa852b2010-03-30 12:17:00 +00002388 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2389 {
2390 if (mng_info->have_global_phys)
2391 {
2392 png_set_pHYs(ping,ping_info,
2393 mng_info->global_x_pixels_per_unit,
2394 mng_info->global_y_pixels_per_unit,
2395 mng_info->global_phys_unit_type);
2396 }
2397 }
2398
2399 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
cristy3ed852e2009-09-05 21:47:34 +00002400 {
cristy3ed852e2009-09-05 21:47:34 +00002401 /*
2402 Set image resolution.
2403 */
2404 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
cristy0881b522010-04-24 23:45:19 +00002405 &unit_type);
2406 image->x_resolution=(double) x_resolution;
2407 image->y_resolution=(double) y_resolution;
glennrp0fe50b42010-11-16 03:52:51 +00002408
cristy3ed852e2009-09-05 21:47:34 +00002409 if (unit_type == PNG_RESOLUTION_METER)
2410 {
2411 image->units=PixelsPerCentimeterResolution;
2412 image->x_resolution=(double) x_resolution/100.0;
2413 image->y_resolution=(double) y_resolution/100.0;
2414 }
glennrp0fe50b42010-11-16 03:52:51 +00002415
cristy3ed852e2009-09-05 21:47:34 +00002416 if (logging != MagickFalse)
2417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002418 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2419 (double) x_resolution,(double) y_resolution,unit_type);
cristy3ed852e2009-09-05 21:47:34 +00002420 }
cristy3ed852e2009-09-05 21:47:34 +00002421#endif
glennrp823b55c2011-03-14 18:46:46 +00002422
glennrpfaa852b2010-03-30 12:17:00 +00002423 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00002424 {
2425 int
2426 number_colors;
2427
2428 png_colorp
2429 palette;
2430
2431 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002432
cristy3ed852e2009-09-05 21:47:34 +00002433 if ((number_colors == 0) &&
glennrpfaa852b2010-03-30 12:17:00 +00002434 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
cristy3ed852e2009-09-05 21:47:34 +00002435 {
2436 if (mng_info->global_plte_length)
2437 {
2438 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2439 (int) mng_info->global_plte_length);
glennrp0fe50b42010-11-16 03:52:51 +00002440
glennrpfaa852b2010-03-30 12:17:00 +00002441 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002442 if (mng_info->global_trns_length)
2443 {
2444 if (mng_info->global_trns_length >
2445 mng_info->global_plte_length)
2446 (void) ThrowMagickException(&image->exception,
2447 GetMagickModule(),CoderError,
2448 "global tRNS has more entries than global PLTE",
2449 "`%s'",image_info->filename);
2450 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2451 (int) mng_info->global_trns_length,NULL);
2452 }
glennrpbfd9e612011-04-22 14:02:20 +00002453#ifdef PNG_READ_bKGD_SUPPORTED
cristy3ed852e2009-09-05 21:47:34 +00002454 if (
2455#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2456 mng_info->have_saved_bkgd_index ||
2457#endif
glennrpfaa852b2010-03-30 12:17:00 +00002458 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002459 {
2460 png_color_16
2461 background;
2462
2463#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2464 if (mng_info->have_saved_bkgd_index)
2465 background.index=mng_info->saved_bkgd_index;
cristy3ed852e2009-09-05 21:47:34 +00002466#endif
glennrpfaa852b2010-03-30 12:17:00 +00002467 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2468 background.index=ping_background->index;
glennrp0fe50b42010-11-16 03:52:51 +00002469
cristy3ed852e2009-09-05 21:47:34 +00002470 background.red=(png_uint_16)
2471 mng_info->global_plte[background.index].red;
glennrp0fe50b42010-11-16 03:52:51 +00002472
cristy3ed852e2009-09-05 21:47:34 +00002473 background.green=(png_uint_16)
2474 mng_info->global_plte[background.index].green;
glennrp0fe50b42010-11-16 03:52:51 +00002475
cristy3ed852e2009-09-05 21:47:34 +00002476 background.blue=(png_uint_16)
2477 mng_info->global_plte[background.index].blue;
glennrp0fe50b42010-11-16 03:52:51 +00002478
glennrpc6c391a2011-04-27 02:23:56 +00002479 background.gray=(png_uint_16)
2480 mng_info->global_plte[background.index].green;
2481
cristy3ed852e2009-09-05 21:47:34 +00002482 png_set_bKGD(ping,ping_info,&background);
2483 }
2484#endif
2485 }
2486 else
2487 (void) ThrowMagickException(&image->exception,GetMagickModule(),
2488 CoderError,"No global PLTE in file","`%s'",
2489 image_info->filename);
2490 }
2491 }
2492
glennrpbfd9e612011-04-22 14:02:20 +00002493#ifdef PNG_READ_bKGD_SUPPORTED
glennrpfaa852b2010-03-30 12:17:00 +00002494 if (mng_info->have_global_bkgd &&
2495 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
cristy3ed852e2009-09-05 21:47:34 +00002496 image->background_color=mng_info->mng_global_bkgd;
glennrp0fe50b42010-11-16 03:52:51 +00002497
glennrpfaa852b2010-03-30 12:17:00 +00002498 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
cristy3ed852e2009-09-05 21:47:34 +00002499 {
glennrpbfd9e612011-04-22 14:02:20 +00002500 unsigned int
2501 bkgd_scale;
2502
cristy3ed852e2009-09-05 21:47:34 +00002503 /*
2504 Set image background color.
2505 */
2506 if (logging != MagickFalse)
2507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2508 " Reading PNG bKGD chunk.");
glennrp0fe50b42010-11-16 03:52:51 +00002509
glennrpbfd9e612011-04-22 14:02:20 +00002510 /* Scale background components to 16-bit, then scale
2511 * to quantum depth
2512 */
2513 if (logging != MagickFalse)
2514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2515 " raw ping_background=(%d,%d,%d).",ping_background->red,
2516 ping_background->green,ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002517
glennrpbfd9e612011-04-22 14:02:20 +00002518 bkgd_scale = 1;
glennrp2cbb4482010-06-02 04:37:24 +00002519
glennrpbfd9e612011-04-22 14:02:20 +00002520 if (ping_bit_depth == 1)
2521 bkgd_scale = 255;
glennrp2cbb4482010-06-02 04:37:24 +00002522
glennrpbfd9e612011-04-22 14:02:20 +00002523 else if (ping_bit_depth == 2)
2524 bkgd_scale = 85;
glennrp0fe50b42010-11-16 03:52:51 +00002525
glennrpbfd9e612011-04-22 14:02:20 +00002526 else if (ping_bit_depth == 4)
2527 bkgd_scale = 17;
glennrp0fe50b42010-11-16 03:52:51 +00002528
glennrpbfd9e612011-04-22 14:02:20 +00002529 if (ping_bit_depth <= 8)
2530 bkgd_scale *= 257;
glennrp0fe50b42010-11-16 03:52:51 +00002531
glennrpbfd9e612011-04-22 14:02:20 +00002532 ping_background->red *= bkgd_scale;
2533 ping_background->green *= bkgd_scale;
2534 ping_background->blue *= bkgd_scale;
glennrp0fe50b42010-11-16 03:52:51 +00002535
glennrpbfd9e612011-04-22 14:02:20 +00002536 if (logging != MagickFalse)
2537 {
glennrp2cbb4482010-06-02 04:37:24 +00002538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2539 " bkgd_scale=%d.",bkgd_scale);
glennrp0fe50b42010-11-16 03:52:51 +00002540
glennrp2cbb4482010-06-02 04:37:24 +00002541 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2542 " ping_background=(%d,%d,%d).",ping_background->red,
2543 ping_background->green,ping_background->blue);
glennrpbfd9e612011-04-22 14:02:20 +00002544 }
glennrp2cbb4482010-06-02 04:37:24 +00002545
glennrpbfd9e612011-04-22 14:02:20 +00002546 image->background_color.red=
glennrpfaa852b2010-03-30 12:17:00 +00002547 ScaleShortToQuantum(ping_background->red);
glennrp0fe50b42010-11-16 03:52:51 +00002548
glennrpbfd9e612011-04-22 14:02:20 +00002549 image->background_color.green=
glennrpfaa852b2010-03-30 12:17:00 +00002550 ScaleShortToQuantum(ping_background->green);
glennrp0fe50b42010-11-16 03:52:51 +00002551
glennrpbfd9e612011-04-22 14:02:20 +00002552 image->background_color.blue=
2553 ScaleShortToQuantum(ping_background->blue);
glennrp0fe50b42010-11-16 03:52:51 +00002554
cristy4c08aed2011-07-01 19:47:50 +00002555 image->background_color.alpha=OpaqueAlpha;
glennrp2cbb4482010-06-02 04:37:24 +00002556
glennrpbfd9e612011-04-22 14:02:20 +00002557 if (logging != MagickFalse)
2558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2559 " image->background_color=(%.20g,%.20g,%.20g).",
2560 (double) image->background_color.red,
2561 (double) image->background_color.green,
2562 (double) image->background_color.blue);
cristy3ed852e2009-09-05 21:47:34 +00002563 }
glennrpbfd9e612011-04-22 14:02:20 +00002564#endif /* PNG_READ_bKGD_SUPPORTED */
glennrpa6a06632011-01-19 15:15:34 +00002565
glennrpfaa852b2010-03-30 12:17:00 +00002566 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00002567 {
2568 /*
glennrpa6a06632011-01-19 15:15:34 +00002569 Image has a tRNS chunk.
cristy3ed852e2009-09-05 21:47:34 +00002570 */
2571 int
2572 max_sample;
2573
cristy35ef8242010-06-03 16:24:13 +00002574 size_t
2575 one=1;
2576
cristy3ed852e2009-09-05 21:47:34 +00002577 if (logging != MagickFalse)
2578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2579 " Reading PNG tRNS chunk.");
2580
cristyf9cca6a2010-06-04 23:49:28 +00002581 max_sample = (int) ((one << ping_bit_depth) - 1);
cristy3ed852e2009-09-05 21:47:34 +00002582
glennrpfaa852b2010-03-30 12:17:00 +00002583 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2584 (int)ping_trans_color->gray > max_sample) ||
2585 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2586 ((int)ping_trans_color->red > max_sample ||
2587 (int)ping_trans_color->green > max_sample ||
2588 (int)ping_trans_color->blue > max_sample)))
cristy3ed852e2009-09-05 21:47:34 +00002589 {
2590 if (logging != MagickFalse)
2591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2592 " Ignoring PNG tRNS chunk with out-of-range sample.");
cristy3ed852e2009-09-05 21:47:34 +00002593 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
glennrpfaa852b2010-03-30 12:17:00 +00002594 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
cristy3ed852e2009-09-05 21:47:34 +00002595 image->matte=MagickFalse;
2596 }
2597 else
2598 {
glennrpa6a06632011-01-19 15:15:34 +00002599 int
2600 scale_to_short;
2601
2602 scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
2603
2604 /* Scale transparent_color to short */
2605 transparent_color.red= scale_to_short*ping_trans_color->red;
2606 transparent_color.green= scale_to_short*ping_trans_color->green;
2607 transparent_color.blue= scale_to_short*ping_trans_color->blue;
cristy4c08aed2011-07-01 19:47:50 +00002608 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
glennrp05eb4a92010-07-08 02:21:09 +00002609
glennrpfaa852b2010-03-30 12:17:00 +00002610 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00002611 {
glennrp0f111982010-07-07 20:18:33 +00002612 if (logging != MagickFalse)
2613 {
2614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2615 " Raw tRNS graylevel is %d.",ping_trans_color->gray);
glennrp0fe50b42010-11-16 03:52:51 +00002616
glennrp0f111982010-07-07 20:18:33 +00002617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00002618 " scaled graylevel is %d.",transparent_color.alpha);
glennrp0f111982010-07-07 20:18:33 +00002619 }
cristy4c08aed2011-07-01 19:47:50 +00002620 transparent_color.red=transparent_color.alpha;
2621 transparent_color.green=transparent_color.alpha;
2622 transparent_color.blue=transparent_color.alpha;
cristy3ed852e2009-09-05 21:47:34 +00002623 }
2624 }
2625 }
2626#if defined(PNG_READ_sBIT_SUPPORTED)
2627 if (mng_info->have_global_sbit)
2628 {
glennrpfaa852b2010-03-30 12:17:00 +00002629 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
cristy3ed852e2009-09-05 21:47:34 +00002630 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2631 }
2632#endif
2633 num_passes=png_set_interlace_handling(ping);
glennrpfaa852b2010-03-30 12:17:00 +00002634
cristy3ed852e2009-09-05 21:47:34 +00002635 png_read_update_info(ping,ping_info);
glennrpfaa852b2010-03-30 12:17:00 +00002636
2637 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2638
cristy3ed852e2009-09-05 21:47:34 +00002639 /*
2640 Initialize image structure.
2641 */
2642 mng_info->image_box.left=0;
cristybb503372010-05-27 20:51:26 +00002643 mng_info->image_box.right=(ssize_t) ping_width;
cristy3ed852e2009-09-05 21:47:34 +00002644 mng_info->image_box.top=0;
cristybb503372010-05-27 20:51:26 +00002645 mng_info->image_box.bottom=(ssize_t) ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002646 if (mng_info->mng_type == 0)
2647 {
glennrpfaa852b2010-03-30 12:17:00 +00002648 mng_info->mng_width=ping_width;
2649 mng_info->mng_height=ping_height;
cristy3ed852e2009-09-05 21:47:34 +00002650 mng_info->frame=mng_info->image_box;
2651 mng_info->clip=mng_info->image_box;
2652 }
glennrp0fe50b42010-11-16 03:52:51 +00002653
cristy3ed852e2009-09-05 21:47:34 +00002654 else
2655 {
2656 image->page.y=mng_info->y_off[mng_info->object_id];
2657 }
glennrp0fe50b42010-11-16 03:52:51 +00002658
cristy3ed852e2009-09-05 21:47:34 +00002659 image->compression=ZipCompression;
glennrpfaa852b2010-03-30 12:17:00 +00002660 image->columns=ping_width;
2661 image->rows=ping_height;
2662 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
glennrpfaa852b2010-03-30 12:17:00 +00002663 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY))
cristy3ed852e2009-09-05 21:47:34 +00002664 {
cristybefe4d22010-06-07 01:18:58 +00002665 size_t
2666 one;
2667
cristy3ed852e2009-09-05 21:47:34 +00002668 image->storage_class=PseudoClass;
cristybefe4d22010-06-07 01:18:58 +00002669 one=1;
2670 image->colors=one << ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00002671#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2672 if (image->colors > 256)
glennrp67b9c1a2011-04-22 18:47:36 +00002673 image->colors=256;
2674#else
2675 if (image->colors > 65536L)
2676 image->colors=65536L;
cristy3ed852e2009-09-05 21:47:34 +00002677#endif
glennrpfaa852b2010-03-30 12:17:00 +00002678 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002679 {
2680 int
2681 number_colors;
2682
2683 png_colorp
2684 palette;
2685
2686 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
cristybb503372010-05-27 20:51:26 +00002687 image->colors=(size_t) number_colors;
glennrp0fe50b42010-11-16 03:52:51 +00002688
cristy3ed852e2009-09-05 21:47:34 +00002689 if (logging != MagickFalse)
2690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2691 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2692 }
2693 }
2694
2695 if (image->storage_class == PseudoClass)
2696 {
2697 /*
2698 Initialize image colormap.
2699 */
2700 if (AcquireImageColormap(image,image->colors) == MagickFalse)
2701 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002702
glennrpfaa852b2010-03-30 12:17:00 +00002703 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00002704 {
2705 int
2706 number_colors;
2707
2708 png_colorp
2709 palette;
2710
2711 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00002712
glennrp6af6cf12011-04-22 13:05:16 +00002713 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002714 {
2715 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
2716 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
2717 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
2718 }
glennrp6af6cf12011-04-22 13:05:16 +00002719
glennrp67b9c1a2011-04-22 18:47:36 +00002720 for ( ; i < (ssize_t) image->colors; i++)
glennrp6af6cf12011-04-22 13:05:16 +00002721 {
2722 image->colormap[i].red=0;
2723 image->colormap[i].green=0;
2724 image->colormap[i].blue=0;
2725 }
cristy3ed852e2009-09-05 21:47:34 +00002726 }
glennrp0fe50b42010-11-16 03:52:51 +00002727
cristy3ed852e2009-09-05 21:47:34 +00002728 else
2729 {
cristybb503372010-05-27 20:51:26 +00002730 size_t
cristy3ed852e2009-09-05 21:47:34 +00002731 scale;
2732
glennrpfaa852b2010-03-30 12:17:00 +00002733 scale=(QuantumRange/((1UL << ping_bit_depth)-1));
glennrp0fe50b42010-11-16 03:52:51 +00002734
cristy3ed852e2009-09-05 21:47:34 +00002735 if (scale < 1)
2736 scale=1;
glennrp0fe50b42010-11-16 03:52:51 +00002737
cristybb503372010-05-27 20:51:26 +00002738 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00002739 {
2740 image->colormap[i].red=(Quantum) (i*scale);
2741 image->colormap[i].green=(Quantum) (i*scale);
2742 image->colormap[i].blue=(Quantum) (i*scale);
2743 }
2744 }
2745 }
glennrp147bc912011-03-30 18:47:21 +00002746
glennrpcb395ac2011-03-30 19:50:23 +00002747 /* Set some properties for reporting by "identify" */
2748 {
glennrp147bc912011-03-30 18:47:21 +00002749 char
2750 msg[MaxTextExtent];
2751
2752 /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
2753 ping_interlace_method in value */
2754
cristy3b6fd2e2011-05-20 12:53:50 +00002755 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp7cdb11c2011-03-31 18:17:25 +00002756 "%d, %d",(int) ping_width, (int) ping_height);
2757 (void) SetImageProperty(image,"PNG:IHDR.width,height ",msg);
glennrp147bc912011-03-30 18:47:21 +00002758
cristy3b6fd2e2011-05-20 12:53:50 +00002759 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
glennrp147bc912011-03-30 18:47:21 +00002760 (void) SetImageProperty(image,"PNG:IHDR.bit_depth ",msg);
2761
cristy3b6fd2e2011-05-20 12:53:50 +00002762 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
glennrp147bc912011-03-30 18:47:21 +00002763 (void) SetImageProperty(image,"PNG:IHDR.color_type ",msg);
2764
cristy3b6fd2e2011-05-20 12:53:50 +00002765 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
glennrp147bc912011-03-30 18:47:21 +00002766 (int) ping_interlace_method);
2767 (void) SetImageProperty(image,"PNG:IHDR.interlace_method",msg);
glennrpcb395ac2011-03-30 19:50:23 +00002768 }
glennrp147bc912011-03-30 18:47:21 +00002769
cristy3ed852e2009-09-05 21:47:34 +00002770 /*
2771 Read image scanlines.
2772 */
2773 if (image->delay != 0)
2774 mng_info->scenes_found++;
glennrp0fe50b42010-11-16 03:52:51 +00002775
glennrp0ca69b12010-07-26 01:57:52 +00002776 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
glennrp347e40f2010-06-06 11:27:30 +00002777 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
2778 (image_info->first_scene+image_info->number_scenes))))
cristy3ed852e2009-09-05 21:47:34 +00002779 {
2780 if (logging != MagickFalse)
2781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00002782 " Skipping PNG image data for scene %.20g",(double)
cristy3ed852e2009-09-05 21:47:34 +00002783 mng_info->scenes_found-1);
2784 png_destroy_read_struct(&ping,&ping_info,&end_info);
2785#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002786 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002787#endif
2788 if (logging != MagickFalse)
2789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2790 " exit ReadOnePNGImage().");
glennrp0fe50b42010-11-16 03:52:51 +00002791
cristy3ed852e2009-09-05 21:47:34 +00002792 return(image);
2793 }
glennrp0fe50b42010-11-16 03:52:51 +00002794
cristy3ed852e2009-09-05 21:47:34 +00002795 if (logging != MagickFalse)
2796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2797 " Reading PNG IDAT chunk(s)");
glennrp0fe50b42010-11-16 03:52:51 +00002798
cristy3ed852e2009-09-05 21:47:34 +00002799 if (num_passes > 1)
glennrpcf002022011-01-30 02:38:15 +00002800 ping_pixels=(unsigned char *) AcquireQuantumMemory(image->rows,
2801 ping_rowbytes*sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002802
cristy3ed852e2009-09-05 21:47:34 +00002803 else
glennrpcf002022011-01-30 02:38:15 +00002804 ping_pixels=(unsigned char *) AcquireQuantumMemory(ping_rowbytes,
2805 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +00002806
glennrpcf002022011-01-30 02:38:15 +00002807 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002808 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002809
cristy3ed852e2009-09-05 21:47:34 +00002810 if (logging != MagickFalse)
2811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2812 " Converting PNG pixels to pixel packets");
2813 /*
2814 Convert PNG pixels to pixel packets.
2815 */
glennrpfaa852b2010-03-30 12:17:00 +00002816 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00002817 {
2818 /*
2819 PNG image is corrupt.
2820 */
2821 png_destroy_read_struct(&ping,&ping_info,&end_info);
2822#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00002823 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00002824#endif
2825 if (quantum_info != (QuantumInfo *) NULL)
2826 quantum_info = DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00002827
glennrpcf002022011-01-30 02:38:15 +00002828 if (ping_pixels != (unsigned char *) NULL)
2829 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
glennrp0fe50b42010-11-16 03:52:51 +00002830
cristy3ed852e2009-09-05 21:47:34 +00002831 if (logging != MagickFalse)
2832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2833 " exit ReadOnePNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00002834
cristy3ed852e2009-09-05 21:47:34 +00002835 if (image != (Image *) NULL)
cristy7b755eb2009-12-05 14:51:29 +00002836 {
2837 InheritException(exception,&image->exception);
2838 image->columns=0;
2839 }
glennrp0fe50b42010-11-16 03:52:51 +00002840
cristy3ed852e2009-09-05 21:47:34 +00002841 return(GetFirstImageInList(image));
2842 }
glennrp0fe50b42010-11-16 03:52:51 +00002843
cristyed552522009-10-16 14:04:35 +00002844 quantum_info=AcquireQuantumInfo(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00002845
cristyed552522009-10-16 14:04:35 +00002846 if (quantum_info == (QuantumInfo *) NULL)
2847 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002848
glennrpc8cbc5d2011-01-01 00:12:34 +00002849 {
2850
2851 MagickBooleanType
2852 found_transparent_pixel;
2853
2854 found_transparent_pixel=MagickFalse;
2855
cristy3ed852e2009-09-05 21:47:34 +00002856 if (image->storage_class == DirectClass)
cristy3ed852e2009-09-05 21:47:34 +00002857 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002858 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +00002859 {
glennrpc8cbc5d2011-01-01 00:12:34 +00002860 /*
2861 Convert image to DirectClass pixel packets.
2862 */
glennrp67b9c1a2011-04-22 18:47:36 +00002863#if (MAGICKCORE_QUANTUM_DEPTH == 8)
2864 int
2865 depth;
2866
2867 depth=(ssize_t) ping_bit_depth;
2868#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00002869 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
2870 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
2871 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
2872 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002873
glennrpc8cbc5d2011-01-01 00:12:34 +00002874 for (y=0; y < (ssize_t) image->rows; y++)
2875 {
2876 if (num_passes > 1)
2877 row_offset=ping_rowbytes*y;
2878
2879 else
2880 row_offset=0;
2881
glennrpcf002022011-01-30 02:38:15 +00002882 png_read_row(ping,ping_pixels+row_offset,NULL);
glennrpc8cbc5d2011-01-01 00:12:34 +00002883 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
2884
cristy4c08aed2011-07-01 19:47:50 +00002885 if (q == (const Quantum *) NULL)
glennrpc8cbc5d2011-01-01 00:12:34 +00002886 break;
2887
glennrpc8cbc5d2011-01-01 00:12:34 +00002888 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
2889 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002890 GrayQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002891
2892 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
2893 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002894 GrayAlphaQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002895
2896 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
2897 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002898 RGBAQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002899
2900 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2901 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002902 IndexQuantum,ping_pixels+row_offset,exception);
glennrpc8cbc5d2011-01-01 00:12:34 +00002903
2904 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
2905 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
glennrpcf002022011-01-30 02:38:15 +00002906 RGBQuantum,ping_pixels+row_offset,exception);
glennrp3faa9a32011-04-23 14:00:25 +00002907
glennrpc8cbc5d2011-01-01 00:12:34 +00002908 if (found_transparent_pixel == MagickFalse)
2909 {
2910 /* Is there a transparent pixel in the row? */
glennrpa6a06632011-01-19 15:15:34 +00002911 if (y== 0 && logging != MagickFalse)
2912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2913 " Looking for cheap transparent pixel");
2914
glennrpc8cbc5d2011-01-01 00:12:34 +00002915 for (x=(ssize_t) image->columns-1; x >= 0; x--)
2916 {
glennrp5aa37f62011-01-02 03:07:57 +00002917 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
2918 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
cristy4c08aed2011-07-01 19:47:50 +00002919 (GetPixelAlpha(image,q) != OpaqueAlpha))
glennrpc8cbc5d2011-01-01 00:12:34 +00002920 {
glennrpa6a06632011-01-19 15:15:34 +00002921 if (logging != MagickFalse)
2922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2923 " ...got one.");
2924
glennrpc8cbc5d2011-01-01 00:12:34 +00002925 found_transparent_pixel = MagickTrue;
2926 break;
2927 }
glennrp4f25bd02011-01-01 18:51:28 +00002928 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
2929 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
cristy4c08aed2011-07-01 19:47:50 +00002930 (ScaleQuantumToShort(GetPixelRed(image,q)) == transparent_color.red &&
2931 ScaleQuantumToShort(GetPixelGreen(image,q)) == transparent_color.green &&
2932 ScaleQuantumToShort(GetPixelBlue(image,q)) == transparent_color.blue))
glennrp4f25bd02011-01-01 18:51:28 +00002933 {
glennrpa6a06632011-01-19 15:15:34 +00002934 if (logging != MagickFalse)
2935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2936 " ...got one.");
glennrp4f25bd02011-01-01 18:51:28 +00002937 found_transparent_pixel = MagickTrue;
2938 break;
2939 }
cristy4c08aed2011-07-01 19:47:50 +00002940 q+=GetPixelChannels(image);
glennrpc8cbc5d2011-01-01 00:12:34 +00002941 }
2942 }
2943
2944 if ((image->previous == (Image *) NULL) && (num_passes == 1))
2945 {
2946 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
2947 image->rows);
2948
2949 if (status == MagickFalse)
2950 break;
2951 }
2952 if (SyncAuthenticPixels(image,exception) == MagickFalse)
2953 break;
2954 }
2955
2956 if ((image->previous == (Image *) NULL) && (num_passes != 1))
2957 {
2958 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
cristy7a287bf2010-02-14 02:18:09 +00002959 if (status == MagickFalse)
2960 break;
2961 }
cristy3ed852e2009-09-05 21:47:34 +00002962 }
cristy3ed852e2009-09-05 21:47:34 +00002963 }
glennrp0fe50b42010-11-16 03:52:51 +00002964
cristy3ed852e2009-09-05 21:47:34 +00002965 else /* image->storage_class != DirectClass */
glennrpc8cbc5d2011-01-01 00:12:34 +00002966
cristy3ed852e2009-09-05 21:47:34 +00002967 for (pass=0; pass < num_passes; pass++)
2968 {
2969 Quantum
2970 *quantum_scanline;
2971
2972 register Quantum
2973 *r;
2974
2975 /*
2976 Convert grayscale image to PseudoClass pixel packets.
2977 */
glennrpc17d96f2011-06-27 01:20:11 +00002978 if (logging != MagickFalse)
2979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2980 " Converting grayscale pixels to pixel packets");
cristy4c08aed2011-07-01 19:47:50 +00002981
glennrpfaa852b2010-03-30 12:17:00 +00002982 image->matte=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
cristy3ed852e2009-09-05 21:47:34 +00002983 MagickTrue : MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00002984
cristy3ed852e2009-09-05 21:47:34 +00002985 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
2986 (image->matte ? 2 : 1)*sizeof(*quantum_scanline));
glennrp0fe50b42010-11-16 03:52:51 +00002987
cristy3ed852e2009-09-05 21:47:34 +00002988 if (quantum_scanline == (Quantum *) NULL)
2989 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00002990
cristybb503372010-05-27 20:51:26 +00002991 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00002992 {
2993 if (num_passes > 1)
glennrpfaa852b2010-03-30 12:17:00 +00002994 row_offset=ping_rowbytes*y;
glennrpc8cbc5d2011-01-01 00:12:34 +00002995
cristy3ed852e2009-09-05 21:47:34 +00002996 else
2997 row_offset=0;
glennrpc8cbc5d2011-01-01 00:12:34 +00002998
glennrpcf002022011-01-30 02:38:15 +00002999 png_read_row(ping,ping_pixels+row_offset,NULL);
cristy3ed852e2009-09-05 21:47:34 +00003000 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp0fe50b42010-11-16 03:52:51 +00003001
cristy4c08aed2011-07-01 19:47:50 +00003002 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003003 break;
glennrp0fe50b42010-11-16 03:52:51 +00003004
glennrpcf002022011-01-30 02:38:15 +00003005 p=ping_pixels+row_offset;
cristy3ed852e2009-09-05 21:47:34 +00003006 r=quantum_scanline;
glennrpc8cbc5d2011-01-01 00:12:34 +00003007
glennrpfaa852b2010-03-30 12:17:00 +00003008 switch (ping_bit_depth)
cristy3ed852e2009-09-05 21:47:34 +00003009 {
3010 case 1:
3011 {
cristybb503372010-05-27 20:51:26 +00003012 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003013 bit;
3014
cristybb503372010-05-27 20:51:26 +00003015 for (x=(ssize_t) image->columns-7; x > 0; x-=8)
cristy3ed852e2009-09-05 21:47:34 +00003016 {
3017 for (bit=7; bit >= 0; bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003018 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003019 p++;
3020 }
glennrp0fe50b42010-11-16 03:52:51 +00003021
cristy3ed852e2009-09-05 21:47:34 +00003022 if ((image->columns % 8) != 0)
3023 {
cristybb503372010-05-27 20:51:26 +00003024 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
glennrpa18d5bc2011-04-23 14:51:34 +00003025 *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
cristy3ed852e2009-09-05 21:47:34 +00003026 }
glennrp0fe50b42010-11-16 03:52:51 +00003027
cristy3ed852e2009-09-05 21:47:34 +00003028 break;
3029 }
glennrp47b9dd52010-11-24 18:12:06 +00003030
cristy3ed852e2009-09-05 21:47:34 +00003031 case 2:
3032 {
cristybb503372010-05-27 20:51:26 +00003033 for (x=(ssize_t) image->columns-3; x > 0; x-=4)
cristy3ed852e2009-09-05 21:47:34 +00003034 {
glennrpa18d5bc2011-04-23 14:51:34 +00003035 *r++=(*p >> 6) & 0x03;
3036 *r++=(*p >> 4) & 0x03;
3037 *r++=(*p >> 2) & 0x03;
3038 *r++=(*p++) & 0x03;
cristy3ed852e2009-09-05 21:47:34 +00003039 }
glennrp0fe50b42010-11-16 03:52:51 +00003040
cristy3ed852e2009-09-05 21:47:34 +00003041 if ((image->columns % 4) != 0)
3042 {
cristybb503372010-05-27 20:51:26 +00003043 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
glennrpa18d5bc2011-04-23 14:51:34 +00003044 *r++=(Quantum) ((*p >> (i*2)) & 0x03);
cristy3ed852e2009-09-05 21:47:34 +00003045 }
glennrp0fe50b42010-11-16 03:52:51 +00003046
cristy3ed852e2009-09-05 21:47:34 +00003047 break;
3048 }
glennrp47b9dd52010-11-24 18:12:06 +00003049
cristy3ed852e2009-09-05 21:47:34 +00003050 case 4:
3051 {
cristybb503372010-05-27 20:51:26 +00003052 for (x=(ssize_t) image->columns-1; x > 0; x-=2)
cristy3ed852e2009-09-05 21:47:34 +00003053 {
glennrpa18d5bc2011-04-23 14:51:34 +00003054 *r++=(*p >> 4) & 0x0f;
3055 *r++=(*p++) & 0x0f;
cristy3ed852e2009-09-05 21:47:34 +00003056 }
glennrp0fe50b42010-11-16 03:52:51 +00003057
cristy3ed852e2009-09-05 21:47:34 +00003058 if ((image->columns % 2) != 0)
glennrpa18d5bc2011-04-23 14:51:34 +00003059 *r++=(*p++ >> 4) & 0x0f;
glennrp0fe50b42010-11-16 03:52:51 +00003060
cristy3ed852e2009-09-05 21:47:34 +00003061 break;
3062 }
glennrp47b9dd52010-11-24 18:12:06 +00003063
cristy3ed852e2009-09-05 21:47:34 +00003064 case 8:
3065 {
glennrpfaa852b2010-03-30 12:17:00 +00003066 if (ping_color_type == 4)
cristybb503372010-05-27 20:51:26 +00003067 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00003068 {
glennrpa18d5bc2011-04-23 14:51:34 +00003069 *r++=*p++;
cristy4c08aed2011-07-01 19:47:50 +00003070 SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
3071 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp0b206f52011-01-07 04:55:32 +00003072 found_transparent_pixel = MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003073 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00003074 }
glennrp0fe50b42010-11-16 03:52:51 +00003075
cristy3ed852e2009-09-05 21:47:34 +00003076 else
cristybb503372010-05-27 20:51:26 +00003077 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003078 *r++=*p++;
glennrp0fe50b42010-11-16 03:52:51 +00003079
cristy3ed852e2009-09-05 21:47:34 +00003080 break;
3081 }
glennrp47b9dd52010-11-24 18:12:06 +00003082
cristy3ed852e2009-09-05 21:47:34 +00003083 case 16:
3084 {
cristybb503372010-05-27 20:51:26 +00003085 for (x=(ssize_t) image->columns-1; x >= 0; x--)
glennrpa18d5bc2011-04-23 14:51:34 +00003086 {
glennrpc17d96f2011-06-27 01:20:11 +00003087#if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
glennrp58f77c72011-04-23 14:09:09 +00003088 size_t
3089 quantum;
3090
3091 if (image->colors > 256)
glennrpc17d96f2011-06-27 01:20:11 +00003092 quantum=((*p++) << 8);
glennrp58f77c72011-04-23 14:09:09 +00003093
3094 else
glennrpc17d96f2011-06-27 01:20:11 +00003095 quantum=0;
glennrp58f77c72011-04-23 14:09:09 +00003096
glennrp58f77c72011-04-23 14:09:09 +00003097 quantum|=(*p++);
glennrpc17d96f2011-06-27 01:20:11 +00003098 *r=ScaleShortToQuantum(quantum);
glennrp58f77c72011-04-23 14:09:09 +00003099 r++;
glennrp9d0ea4d2011-04-22 18:35:57 +00003100
3101 if (ping_color_type == 4)
3102 {
glennrpc17d96f2011-06-27 01:20:11 +00003103 if (image->colors > 256)
3104 quantum=((*p++) << 8);
3105 else
3106 quantum=0;
3107
glennrp58f77c72011-04-23 14:09:09 +00003108 quantum|=(*p++);
cristy4c08aed2011-07-01 19:47:50 +00003109 SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
3110 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp58f77c72011-04-23 14:09:09 +00003111 found_transparent_pixel = MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00003112 q+=GetPixelChannels(image);
glennrp58f77c72011-04-23 14:09:09 +00003113 }
glennrp58f77c72011-04-23 14:09:09 +00003114
3115#else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3116 *r++=(*p++);
3117 p++; /* strip low byte */
3118
3119 if (ping_color_type == 4)
3120 {
cristy4c08aed2011-07-01 19:47:50 +00003121 SetPixelAlpha(image,*p++,q);
3122 if (GetPixelAlpha(image,q) != OpaqueAlpha)
glennrp9d0ea4d2011-04-22 18:35:57 +00003123 found_transparent_pixel = MagickTrue;
3124 p++;
cristy4c08aed2011-07-01 19:47:50 +00003125 q+=GetPixelChannels(image);
glennrp9d0ea4d2011-04-22 18:35:57 +00003126 }
cristy3ed852e2009-09-05 21:47:34 +00003127#endif
glennrpa18d5bc2011-04-23 14:51:34 +00003128 }
glennrp47b9dd52010-11-24 18:12:06 +00003129
cristy3ed852e2009-09-05 21:47:34 +00003130 break;
3131 }
glennrp47b9dd52010-11-24 18:12:06 +00003132
cristy3ed852e2009-09-05 21:47:34 +00003133 default:
3134 break;
3135 }
glennrp3faa9a32011-04-23 14:00:25 +00003136
cristy3ed852e2009-09-05 21:47:34 +00003137 /*
3138 Transfer image scanline.
3139 */
3140 r=quantum_scanline;
glennrp0fe50b42010-11-16 03:52:51 +00003141
cristy4c08aed2011-07-01 19:47:50 +00003142 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3143
3144 if (q == (const Quantum *) NULL)
3145 break;
cristybb503372010-05-27 20:51:26 +00003146 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00003147 {
3148 SetPixelIndex(image,*r++,q);
3149 q+=GetPixelChannels(image);
3150 }
glennrp0fe50b42010-11-16 03:52:51 +00003151
cristy3ed852e2009-09-05 21:47:34 +00003152 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3153 break;
glennrp0fe50b42010-11-16 03:52:51 +00003154
cristy7a287bf2010-02-14 02:18:09 +00003155 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3156 {
cristycee97112010-05-28 00:44:52 +00003157 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy9fff7b42011-04-29 01:09:31 +00003158 image->rows);
glennrp47b9dd52010-11-24 18:12:06 +00003159
cristy7a287bf2010-02-14 02:18:09 +00003160 if (status == MagickFalse)
3161 break;
3162 }
cristy3ed852e2009-09-05 21:47:34 +00003163 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003164
cristy7a287bf2010-02-14 02:18:09 +00003165 if ((image->previous == (Image *) NULL) && (num_passes != 1))
cristy3ed852e2009-09-05 21:47:34 +00003166 {
3167 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
glennrp47b9dd52010-11-24 18:12:06 +00003168
cristy3ed852e2009-09-05 21:47:34 +00003169 if (status == MagickFalse)
3170 break;
3171 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003172
cristy3ed852e2009-09-05 21:47:34 +00003173 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3174 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003175
3176 image->matte=found_transparent_pixel;
3177
3178 if (logging != MagickFalse)
3179 {
3180 if (found_transparent_pixel != MagickFalse)
3181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3182 " Found transparent pixel");
3183 else
glennrp5aa37f62011-01-02 03:07:57 +00003184 {
3185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3186 " No transparent pixel was found");
glennrpbb4f99d2011-05-22 11:13:17 +00003187
glennrp5aa37f62011-01-02 03:07:57 +00003188 ping_color_type&=0x03;
3189 }
glennrpc8cbc5d2011-01-01 00:12:34 +00003190 }
3191 }
3192
cristyb32b90a2009-09-07 21:45:48 +00003193 if (quantum_info != (QuantumInfo *) NULL)
3194 quantum_info=DestroyQuantumInfo(quantum_info);
glennrp0fe50b42010-11-16 03:52:51 +00003195
cristy5c6f7892010-05-05 22:53:29 +00003196 if (image->storage_class == PseudoClass)
3197 {
cristyaeb2cbc2010-05-07 13:28:58 +00003198 MagickBooleanType
cristy5c6f7892010-05-05 22:53:29 +00003199 matte;
3200
3201 matte=image->matte;
3202 image->matte=MagickFalse;
3203 (void) SyncImage(image);
cristyaeb2cbc2010-05-07 13:28:58 +00003204 image->matte=matte;
cristy5c6f7892010-05-05 22:53:29 +00003205 }
glennrp47b9dd52010-11-24 18:12:06 +00003206
glennrp4eb39312011-03-30 21:34:55 +00003207 png_read_end(ping,end_info);
cristy3ed852e2009-09-05 21:47:34 +00003208
3209 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
cristybb503372010-05-27 20:51:26 +00003210 (ssize_t) image_info->first_scene && image->delay != 0)
cristy3ed852e2009-09-05 21:47:34 +00003211 {
3212 png_destroy_read_struct(&ping,&ping_info,&end_info);
glennrpcf002022011-01-30 02:38:15 +00003213 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003214 image->colors=2;
3215 (void) SetImageBackgroundColor(image);
3216#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003217 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003218#endif
3219 if (logging != MagickFalse)
3220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3221 " exit ReadOnePNGImage() early.");
3222 return(image);
3223 }
glennrp47b9dd52010-11-24 18:12:06 +00003224
glennrpfaa852b2010-03-30 12:17:00 +00003225 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
cristy3ed852e2009-09-05 21:47:34 +00003226 {
3227 ClassType
3228 storage_class;
3229
3230 /*
3231 Image has a transparent background.
3232 */
3233 storage_class=image->storage_class;
3234 image->matte=MagickTrue;
glennrpc11cf6a2010-03-20 16:46:19 +00003235
glennrp3c218112010-11-27 15:31:26 +00003236/* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
glennrpc11cf6a2010-03-20 16:46:19 +00003237
glennrp0fe50b42010-11-16 03:52:51 +00003238 if (storage_class == PseudoClass)
3239 {
3240 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrpc11cf6a2010-03-20 16:46:19 +00003241 {
glennrp0fe50b42010-11-16 03:52:51 +00003242 for (x=0; x < ping_num_trans; x++)
3243 {
cristy4c08aed2011-07-01 19:47:50 +00003244 image->colormap[x].alpha =
3245 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
glennrp0fe50b42010-11-16 03:52:51 +00003246 }
glennrpc11cf6a2010-03-20 16:46:19 +00003247 }
glennrp47b9dd52010-11-24 18:12:06 +00003248
glennrp0fe50b42010-11-16 03:52:51 +00003249 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3250 {
3251 for (x=0; x < (int) image->colors; x++)
3252 {
3253 if (ScaleQuantumToShort(image->colormap[x].red) ==
cristy4c08aed2011-07-01 19:47:50 +00003254 transparent_color.alpha)
glennrp0fe50b42010-11-16 03:52:51 +00003255 {
cristy4c08aed2011-07-01 19:47:50 +00003256 image->colormap[x].alpha = (Quantum) TransparentAlpha;
glennrp0fe50b42010-11-16 03:52:51 +00003257 }
3258 }
3259 }
3260 (void) SyncImage(image);
3261 }
glennrp47b9dd52010-11-24 18:12:06 +00003262
glennrpa6a06632011-01-19 15:15:34 +00003263#if 1 /* Should have already been done above, but glennrp problem P10
3264 * needs this.
3265 */
glennrp0fe50b42010-11-16 03:52:51 +00003266 else
3267 {
3268 for (y=0; y < (ssize_t) image->rows; y++)
glennrpc11cf6a2010-03-20 16:46:19 +00003269 {
glennrp0fe50b42010-11-16 03:52:51 +00003270 image->storage_class=storage_class;
3271 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3272
cristy4c08aed2011-07-01 19:47:50 +00003273 if (q == (const Quantum *) NULL)
glennrp0fe50b42010-11-16 03:52:51 +00003274 break;
3275
glennrp0fe50b42010-11-16 03:52:51 +00003276
glennrpa6a06632011-01-19 15:15:34 +00003277 /* Caution: on a Q8 build, this does not distinguish between
3278 * 16-bit colors that differ only in the low byte
3279 */
glennrp0fe50b42010-11-16 03:52:51 +00003280 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3281 {
cristy4c08aed2011-07-01 19:47:50 +00003282 if (ScaleQuantumToShort(GetPixelRed(image,q)) == transparent_color.red &&
3283 ScaleQuantumToShort(GetPixelGreen(image,q)) == transparent_color.green &&
3284 ScaleQuantumToShort(GetPixelBlue(image,q)) == transparent_color.blue)
glennrp4f25bd02011-01-01 18:51:28 +00003285 {
cristy4c08aed2011-07-01 19:47:50 +00003286 SetPixelAlpha(image,TransparentAlpha,q);
glennrp4f25bd02011-01-01 18:51:28 +00003287 }
glennrp0fe50b42010-11-16 03:52:51 +00003288
glennrp67b9c1a2011-04-22 18:47:36 +00003289#if 0 /* I have not found a case where this is needed. */
glennrp0fe50b42010-11-16 03:52:51 +00003290 else
glennrp4f25bd02011-01-01 18:51:28 +00003291 {
cristy4c08aed2011-07-01 19:47:50 +00003292 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
glennrp4f25bd02011-01-01 18:51:28 +00003293 }
glennrpa6a06632011-01-19 15:15:34 +00003294#endif
glennrp0fe50b42010-11-16 03:52:51 +00003295
cristy4c08aed2011-07-01 19:47:50 +00003296 q+=GetPixelChannels(image);
glennrp0fe50b42010-11-16 03:52:51 +00003297 }
3298
3299 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3300 break;
glennrpc11cf6a2010-03-20 16:46:19 +00003301 }
glennrp0fe50b42010-11-16 03:52:51 +00003302 }
glennrpa6a06632011-01-19 15:15:34 +00003303#endif
glennrpc11cf6a2010-03-20 16:46:19 +00003304
cristy3ed852e2009-09-05 21:47:34 +00003305 image->storage_class=DirectClass;
3306 }
glennrp3c218112010-11-27 15:31:26 +00003307
cristyb40fc462010-08-08 00:49:49 +00003308 if ((ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3309 (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3310 image->colorspace=GRAYColorspace;
glennrp47b9dd52010-11-24 18:12:06 +00003311
cristyeb3b22a2011-03-31 20:16:11 +00003312 for (j = 0; j < 2; j++)
glennrp4eb39312011-03-30 21:34:55 +00003313 {
3314 if (j == 0)
glennrpa0ed0092011-04-18 16:36:29 +00003315 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3316 MagickTrue : MagickFalse;
glennrp4eb39312011-03-30 21:34:55 +00003317 else
glennrpa0ed0092011-04-18 16:36:29 +00003318 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3319 MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +00003320
glennrp4eb39312011-03-30 21:34:55 +00003321 if (status != MagickFalse)
3322 for (i=0; i < (ssize_t) num_text; i++)
3323 {
3324 /* Check for a profile */
glennrp0fe50b42010-11-16 03:52:51 +00003325
glennrp4eb39312011-03-30 21:34:55 +00003326 if (logging != MagickFalse)
3327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3328 " Reading PNG text chunk");
glennrp0fe50b42010-11-16 03:52:51 +00003329
glennrp4eb39312011-03-30 21:34:55 +00003330 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
glennrp97f90e22011-02-22 05:47:58 +00003331 {
glennrp4eb39312011-03-30 21:34:55 +00003332 (void) Magick_png_read_raw_profile(image,image_info,text,(int) i);
3333 num_raw_profiles++;
glennrp97f90e22011-02-22 05:47:58 +00003334 }
glennrp0fe50b42010-11-16 03:52:51 +00003335
glennrp4eb39312011-03-30 21:34:55 +00003336 else
3337 {
3338 char
3339 *value;
3340
3341 length=text[i].text_length;
3342 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3343 sizeof(*value));
3344 if (value == (char *) NULL)
3345 {
3346 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3347 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3348 image->filename);
3349 break;
3350 }
3351 *value='\0';
3352 (void) ConcatenateMagickString(value,text[i].text,length+2);
3353
3354 /* Don't save "density" or "units" property if we have a pHYs
3355 * chunk
3356 */
3357 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3358 (LocaleCompare(text[i].key,"density") != 0 &&
3359 LocaleCompare(text[i].key,"units") != 0))
3360 (void) SetImageProperty(image,text[i].key,value);
3361
3362 if (logging != MagickFalse)
3363 {
3364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3365 " length: %lu",(unsigned long) length);
3366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3367 " Keyword: %s",text[i].key);
3368 }
3369
3370 value=DestroyString(value);
3371 }
3372 }
3373 num_text_total += num_text;
cristy3ed852e2009-09-05 21:47:34 +00003374 }
glennrp3c218112010-11-27 15:31:26 +00003375
cristy3ed852e2009-09-05 21:47:34 +00003376#ifdef MNG_OBJECT_BUFFERS
3377 /*
3378 Store the object if necessary.
3379 */
3380 if (object_id && !mng_info->frozen[object_id])
3381 {
3382 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3383 {
3384 /*
3385 create a new object buffer.
3386 */
3387 mng_info->ob[object_id]=(MngBuffer *)
cristy73bd4a52010-10-05 11:24:23 +00003388 AcquireMagickMemory(sizeof(MngBuffer));
glennrp0fe50b42010-11-16 03:52:51 +00003389
cristy3ed852e2009-09-05 21:47:34 +00003390 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3391 {
3392 mng_info->ob[object_id]->image=(Image *) NULL;
3393 mng_info->ob[object_id]->reference_count=1;
3394 }
3395 }
glennrp47b9dd52010-11-24 18:12:06 +00003396
cristy3ed852e2009-09-05 21:47:34 +00003397 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3398 mng_info->ob[object_id]->frozen)
3399 {
3400 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3401 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3402 ResourceLimitError,"MemoryAllocationFailed","`%s'",
3403 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003404
cristy3ed852e2009-09-05 21:47:34 +00003405 if (mng_info->ob[object_id]->frozen)
3406 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3407 ResourceLimitError,"Cannot overwrite frozen MNG object buffer",
3408 "`%s'",image->filename);
3409 }
glennrp0fe50b42010-11-16 03:52:51 +00003410
cristy3ed852e2009-09-05 21:47:34 +00003411 else
3412 {
cristy3ed852e2009-09-05 21:47:34 +00003413
3414 if (mng_info->ob[object_id]->image != (Image *) NULL)
3415 mng_info->ob[object_id]->image=DestroyImage
3416 (mng_info->ob[object_id]->image);
glennrp0fe50b42010-11-16 03:52:51 +00003417
cristy3ed852e2009-09-05 21:47:34 +00003418 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3419 &image->exception);
glennrp0fe50b42010-11-16 03:52:51 +00003420
cristy3ed852e2009-09-05 21:47:34 +00003421 if (mng_info->ob[object_id]->image != (Image *) NULL)
3422 mng_info->ob[object_id]->image->file=(FILE *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00003423
cristy3ed852e2009-09-05 21:47:34 +00003424 else
3425 (void) ThrowMagickException(&image->exception,GetMagickModule(),
3426 ResourceLimitError,"Cloning image for object buffer failed",
3427 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00003428
glennrpfaa852b2010-03-30 12:17:00 +00003429 if (ping_width > 250000L || ping_height > 250000L)
cristy3ed852e2009-09-05 21:47:34 +00003430 png_error(ping,"PNG Image dimensions are too large.");
glennrp0fe50b42010-11-16 03:52:51 +00003431
glennrpfaa852b2010-03-30 12:17:00 +00003432 mng_info->ob[object_id]->width=ping_width;
3433 mng_info->ob[object_id]->height=ping_height;
3434 mng_info->ob[object_id]->color_type=ping_color_type;
3435 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3436 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3437 mng_info->ob[object_id]->compression_method=
3438 ping_compression_method;
3439 mng_info->ob[object_id]->filter_method=ping_filter_method;
glennrp0fe50b42010-11-16 03:52:51 +00003440
glennrpfaa852b2010-03-30 12:17:00 +00003441 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
cristy3ed852e2009-09-05 21:47:34 +00003442 {
3443 int
3444 number_colors;
3445
3446 png_colorp
3447 plte;
3448
3449 /*
3450 Copy the PLTE to the object buffer.
3451 */
3452 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3453 mng_info->ob[object_id]->plte_length=number_colors;
glennrp3c218112010-11-27 15:31:26 +00003454
cristy3ed852e2009-09-05 21:47:34 +00003455 for (i=0; i < number_colors; i++)
3456 {
3457 mng_info->ob[object_id]->plte[i]=plte[i];
3458 }
3459 }
glennrp47b9dd52010-11-24 18:12:06 +00003460
cristy3ed852e2009-09-05 21:47:34 +00003461 else
3462 mng_info->ob[object_id]->plte_length=0;
3463 }
3464 }
3465#endif
glennrp0a55b4c2011-03-23 12:38:38 +00003466
3467 /* Set image->matte to MagickTrue if the input colortype supports
3468 * alpha or if a valid tRNS chunk is present, no matter whether there
3469 * is actual transparency present.
3470 */
3471 image->matte=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3472 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3473 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3474 MagickTrue : MagickFalse;
3475
glennrpcb395ac2011-03-30 19:50:23 +00003476 /* Set more properties for identify to retrieve */
3477 {
3478 char
3479 msg[MaxTextExtent];
3480
glennrp4eb39312011-03-30 21:34:55 +00003481 if (num_text_total != 0)
glennrpcb395ac2011-03-30 19:50:23 +00003482 {
3483 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
cristy3b6fd2e2011-05-20 12:53:50 +00003484 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp613276d2011-03-30 21:46:49 +00003485 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
glennrpcb395ac2011-03-30 19:50:23 +00003486 (void) SetImageProperty(image,"PNG:text ",msg);
3487 }
3488
3489 if (num_raw_profiles != 0)
3490 {
cristy3b6fd2e2011-05-20 12:53:50 +00003491 (void) FormatLocaleString(msg,MaxTextExtent,
glennrpcb395ac2011-03-30 19:50:23 +00003492 "%d were found", num_raw_profiles);
3493 (void) SetImageProperty(image,"PNG:text-encoded profiles",msg);
3494 }
3495
glennrpcb395ac2011-03-30 19:50:23 +00003496 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
glennrp59612252011-03-30 21:45:21 +00003497 {
cristy3b6fd2e2011-05-20 12:53:50 +00003498 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003499 "chunk was found (see Chromaticity, above)");
3500 (void) SetImageProperty(image,"PNG:cHRM ",msg);
3501 }
glennrpcb395ac2011-03-30 19:50:23 +00003502
3503 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
glennrp59612252011-03-30 21:45:21 +00003504 {
cristy3b6fd2e2011-05-20 12:53:50 +00003505 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003506 "chunk was found (see Background color, above)");
3507 (void) SetImageProperty(image,"PNG:bKGD ",msg);
3508 }
3509
cristy3b6fd2e2011-05-20 12:53:50 +00003510 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
glennrp59612252011-03-30 21:45:21 +00003511 "chunk was found");
glennrpcb395ac2011-03-30 19:50:23 +00003512
3513 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
3514 (void) SetImageProperty(image,"PNG:iCCP ",msg);
3515
glennrpcb395ac2011-03-30 19:50:23 +00003516 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3517 (void) SetImageProperty(image,"PNG:tRNS ",msg);
glennrp4eb39312011-03-30 21:34:55 +00003518
3519#if defined(PNG_sRGB_SUPPORTED)
3520 if (png_get_valid(ping,ping_info,PNG_INFO_sRGB))
3521 {
cristy3b6fd2e2011-05-20 12:53:50 +00003522 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003523 "intent=%d (See Rendering intent)",
glennrp4eb39312011-03-30 21:34:55 +00003524 (int) intent);
3525 (void) SetImageProperty(image,"PNG:sRGB ",msg);
3526 }
3527#endif
3528
3529 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
3530 {
cristy3b6fd2e2011-05-20 12:53:50 +00003531 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003532 "gamma=%.8g (See Gamma, above)",
glennrp4eb39312011-03-30 21:34:55 +00003533 file_gamma);
3534 (void) SetImageProperty(image,"PNG:gAMA ",msg);
3535 }
3536
3537#if defined(PNG_pHYs_SUPPORTED)
3538 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3539 {
cristy3b6fd2e2011-05-20 12:53:50 +00003540 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003541 "x_res=%.10g, y_res=%.10g, units=%d",
glennrp4eb39312011-03-30 21:34:55 +00003542 (double) x_resolution,(double) y_resolution, unit_type);
3543 (void) SetImageProperty(image,"PNG:pHYs ",msg);
3544 }
3545#endif
3546
3547#if defined(PNG_oFFs_SUPPORTED)
3548 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3549 {
cristy3b6fd2e2011-05-20 12:53:50 +00003550 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
glennrp4eb39312011-03-30 21:34:55 +00003551 (double) image->page.x,(double) image->page.y);
3552 (void) SetImageProperty(image,"PNG:oFFs ",msg);
3553 }
3554#endif
3555
glennrp07523c72011-03-31 18:12:10 +00003556 if ((image->page.width != 0 && image->page.width != image->columns) ||
3557 (image->page.height != 0 && image->page.height != image->rows))
3558 {
cristy3b6fd2e2011-05-20 12:53:50 +00003559 (void) FormatLocaleString(msg,MaxTextExtent,
glennrp07523c72011-03-31 18:12:10 +00003560 "width=%.20g, height=%.20g",
3561 (double) image->page.width,(double) image->page.height);
3562 (void) SetImageProperty(image,"PNG:vpAg ",msg);
3563 }
glennrpcb395ac2011-03-30 19:50:23 +00003564 }
3565
cristy3ed852e2009-09-05 21:47:34 +00003566 /*
3567 Relinquish resources.
3568 */
3569 png_destroy_read_struct(&ping,&ping_info,&end_info);
3570
glennrpcf002022011-01-30 02:38:15 +00003571 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +00003572#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00003573 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00003574#endif
3575
3576 if (logging != MagickFalse)
3577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3578 " exit ReadOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003579
cristy3ed852e2009-09-05 21:47:34 +00003580 return(image);
3581
3582/* end of reading one PNG image */
3583}
3584
3585static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3586{
3587 Image
3588 *image,
3589 *previous;
3590
3591 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00003592 have_mng_structure,
3593 logging,
cristy3ed852e2009-09-05 21:47:34 +00003594 status;
3595
3596 MngInfo
3597 *mng_info;
3598
3599 char
3600 magic_number[MaxTextExtent];
3601
cristy3ed852e2009-09-05 21:47:34 +00003602 ssize_t
3603 count;
3604
3605 /*
3606 Open image file.
3607 */
3608 assert(image_info != (const ImageInfo *) NULL);
3609 assert(image_info->signature == MagickSignature);
glennrp47b9dd52010-11-24 18:12:06 +00003610
cristy3ed852e2009-09-05 21:47:34 +00003611 if (image_info->debug != MagickFalse)
3612 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3613 image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00003614
cristy3ed852e2009-09-05 21:47:34 +00003615 assert(exception != (ExceptionInfo *) NULL);
3616 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00003617 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003618 image=AcquireImage(image_info);
3619 mng_info=(MngInfo *) NULL;
3620 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp47b9dd52010-11-24 18:12:06 +00003621
cristy3ed852e2009-09-05 21:47:34 +00003622 if (status == MagickFalse)
3623 ThrowReaderException(FileOpenError,"UnableToOpenFile");
glennrp47b9dd52010-11-24 18:12:06 +00003624
cristy3ed852e2009-09-05 21:47:34 +00003625 /*
3626 Verify PNG signature.
3627 */
3628 count=ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00003629
glennrpdde35db2011-02-21 12:06:32 +00003630 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00003631 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00003632
cristy3ed852e2009-09-05 21:47:34 +00003633 /*
3634 Allocate a MngInfo structure.
3635 */
3636 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00003637 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003638
cristy3ed852e2009-09-05 21:47:34 +00003639 if (mng_info == (MngInfo *) NULL)
3640 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003641
cristy3ed852e2009-09-05 21:47:34 +00003642 /*
3643 Initialize members of the MngInfo structure.
3644 */
3645 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3646 mng_info->image=image;
3647 have_mng_structure=MagickTrue;
3648
3649 previous=image;
3650 image=ReadOnePNGImage(mng_info,image_info,exception);
3651 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00003652
cristy3ed852e2009-09-05 21:47:34 +00003653 if (image == (Image *) NULL)
3654 {
3655 if (previous != (Image *) NULL)
3656 {
3657 if (previous->signature != MagickSignature)
3658 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003659
cristy3ed852e2009-09-05 21:47:34 +00003660 (void) CloseBlob(previous);
3661 (void) DestroyImageList(previous);
3662 }
glennrp0fe50b42010-11-16 03:52:51 +00003663
cristy3ed852e2009-09-05 21:47:34 +00003664 if (logging != MagickFalse)
3665 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3666 "exit ReadPNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00003667
cristy3ed852e2009-09-05 21:47:34 +00003668 return((Image *) NULL);
3669 }
glennrp47b9dd52010-11-24 18:12:06 +00003670
cristy3ed852e2009-09-05 21:47:34 +00003671 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00003672
cristy3ed852e2009-09-05 21:47:34 +00003673 if ((image->columns == 0) || (image->rows == 0))
3674 {
3675 if (logging != MagickFalse)
3676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3677 "exit ReadPNGImage() with error.");
glennrp0fe50b42010-11-16 03:52:51 +00003678
cristy3ed852e2009-09-05 21:47:34 +00003679 ThrowReaderException(CorruptImageError,"CorruptImage");
3680 }
glennrp47b9dd52010-11-24 18:12:06 +00003681
cristy3ed852e2009-09-05 21:47:34 +00003682 if (LocaleCompare(image_info->magick,"PNG24") == 0)
3683 {
3684 (void) SetImageType(image,TrueColorType);
3685 image->matte=MagickFalse;
3686 }
glennrp0fe50b42010-11-16 03:52:51 +00003687
cristy3ed852e2009-09-05 21:47:34 +00003688 if (LocaleCompare(image_info->magick,"PNG32") == 0)
3689 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +00003690
cristy3ed852e2009-09-05 21:47:34 +00003691 if (logging != MagickFalse)
glennrp97f90e22011-02-22 05:47:58 +00003692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3693 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
3694 (double) image->page.width,(double) image->page.height,
3695 (double) image->page.x,(double) image->page.y);
3696
3697 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00003698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003699
cristy3ed852e2009-09-05 21:47:34 +00003700 return(image);
3701}
3702
3703
3704
3705#if defined(JNG_SUPPORTED)
3706/*
3707%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3708% %
3709% %
3710% %
3711% R e a d O n e J N G I m a g e %
3712% %
3713% %
3714% %
3715%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3716%
3717% ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
3718% (minus the 8-byte signature) and returns it. It allocates the memory
3719% necessary for the new Image structure and returns a pointer to the new
3720% image.
3721%
3722% JNG support written by Glenn Randers-Pehrson, glennrp@image...
3723%
3724% The format of the ReadOneJNGImage method is:
3725%
3726% Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
3727% ExceptionInfo *exception)
3728%
3729% A description of each parameter follows:
3730%
3731% o mng_info: Specifies a pointer to a MngInfo structure.
3732%
3733% o image_info: the image info.
3734%
3735% o exception: return any errors or warnings in this structure.
3736%
3737*/
3738static Image *ReadOneJNGImage(MngInfo *mng_info,
3739 const ImageInfo *image_info, ExceptionInfo *exception)
3740{
3741 Image
3742 *alpha_image,
3743 *color_image,
3744 *image,
3745 *jng_image;
3746
3747 ImageInfo
3748 *alpha_image_info,
3749 *color_image_info;
3750
cristy4383ec82011-01-05 15:42:32 +00003751 MagickBooleanType
3752 logging;
3753
cristybb503372010-05-27 20:51:26 +00003754 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003755 y;
3756
3757 MagickBooleanType
3758 status;
3759
3760 png_uint_32
3761 jng_height,
3762 jng_width;
3763
3764 png_byte
3765 jng_color_type,
3766 jng_image_sample_depth,
3767 jng_image_compression_method,
3768 jng_image_interlace_method,
3769 jng_alpha_sample_depth,
3770 jng_alpha_compression_method,
3771 jng_alpha_filter_method,
3772 jng_alpha_interlace_method;
3773
cristy4c08aed2011-07-01 19:47:50 +00003774 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +00003775 *s;
3776
cristybb503372010-05-27 20:51:26 +00003777 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00003778 i,
3779 x;
3780
cristy4c08aed2011-07-01 19:47:50 +00003781 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00003782 *q;
3783
3784 register unsigned char
3785 *p;
3786
3787 unsigned int
cristy3ed852e2009-09-05 21:47:34 +00003788 read_JSEP,
3789 reading_idat,
3790 skip_to_iend;
3791
cristybb503372010-05-27 20:51:26 +00003792 size_t
cristy3ed852e2009-09-05 21:47:34 +00003793 length;
3794
3795 jng_alpha_compression_method=0;
3796 jng_alpha_sample_depth=8;
3797 jng_color_type=0;
3798 jng_height=0;
3799 jng_width=0;
3800 alpha_image=(Image *) NULL;
3801 color_image=(Image *) NULL;
3802 alpha_image_info=(ImageInfo *) NULL;
3803 color_image_info=(ImageInfo *) NULL;
3804
3805 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00003806 " Enter ReadOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00003807
3808 image=mng_info->image;
glennrp0fe50b42010-11-16 03:52:51 +00003809
cristy4c08aed2011-07-01 19:47:50 +00003810 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00003811 {
3812 /*
3813 Allocate next image structure.
3814 */
3815 if (logging != MagickFalse)
3816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3817 " AcquireNextImage()");
glennrp0fe50b42010-11-16 03:52:51 +00003818
cristy3ed852e2009-09-05 21:47:34 +00003819 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00003820
cristy3ed852e2009-09-05 21:47:34 +00003821 if (GetNextImageInList(image) == (Image *) NULL)
3822 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00003823
cristy3ed852e2009-09-05 21:47:34 +00003824 image=SyncNextImageInList(image);
3825 }
3826 mng_info->image=image;
3827
3828 /*
3829 Signature bytes have already been read.
3830 */
3831
3832 read_JSEP=MagickFalse;
3833 reading_idat=MagickFalse;
3834 skip_to_iend=MagickFalse;
3835 for (;;)
3836 {
3837 char
3838 type[MaxTextExtent];
3839
3840 unsigned char
3841 *chunk;
3842
3843 unsigned int
3844 count;
3845
3846 /*
3847 Read a new JNG chunk.
3848 */
3849 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
3850 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00003851
cristy3ed852e2009-09-05 21:47:34 +00003852 if (status == MagickFalse)
3853 break;
glennrp0fe50b42010-11-16 03:52:51 +00003854
cristy3ed852e2009-09-05 21:47:34 +00003855 type[0]='\0';
3856 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
3857 length=ReadBlobMSBLong(image);
3858 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
3859
3860 if (logging != MagickFalse)
3861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00003862 " Reading JNG chunk type %c%c%c%c, length: %.20g",
3863 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00003864
3865 if (length > PNG_UINT_31_MAX || count == 0)
3866 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00003867
cristy3ed852e2009-09-05 21:47:34 +00003868 p=NULL;
3869 chunk=(unsigned char *) NULL;
glennrp47b9dd52010-11-24 18:12:06 +00003870
cristy3ed852e2009-09-05 21:47:34 +00003871 if (length)
3872 {
3873 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp0fe50b42010-11-16 03:52:51 +00003874
cristy3ed852e2009-09-05 21:47:34 +00003875 if (chunk == (unsigned char *) NULL)
3876 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00003877
cristybb503372010-05-27 20:51:26 +00003878 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00003879 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp0fe50b42010-11-16 03:52:51 +00003880
cristy3ed852e2009-09-05 21:47:34 +00003881 p=chunk;
3882 }
glennrp47b9dd52010-11-24 18:12:06 +00003883
cristy3ed852e2009-09-05 21:47:34 +00003884 (void) ReadBlobMSBLong(image); /* read crc word */
3885
3886 if (skip_to_iend)
3887 {
3888 if (length)
3889 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003890
cristy3ed852e2009-09-05 21:47:34 +00003891 continue;
3892 }
3893
3894 if (memcmp(type,mng_JHDR,4) == 0)
3895 {
3896 if (length == 16)
3897 {
cristybb503372010-05-27 20:51:26 +00003898 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003899 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00003900 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00003901 (p[6] << 8) | p[7]);
3902 jng_color_type=p[8];
3903 jng_image_sample_depth=p[9];
3904 jng_image_compression_method=p[10];
3905 jng_image_interlace_method=p[11];
glennrp47b9dd52010-11-24 18:12:06 +00003906
cristy3ed852e2009-09-05 21:47:34 +00003907 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
3908 NoInterlace;
glennrp47b9dd52010-11-24 18:12:06 +00003909
cristy3ed852e2009-09-05 21:47:34 +00003910 jng_alpha_sample_depth=p[12];
3911 jng_alpha_compression_method=p[13];
3912 jng_alpha_filter_method=p[14];
3913 jng_alpha_interlace_method=p[15];
glennrp47b9dd52010-11-24 18:12:06 +00003914
cristy3ed852e2009-09-05 21:47:34 +00003915 if (logging != MagickFalse)
3916 {
3917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003918 " jng_width: %16lu",(unsigned long) jng_width);
glennrp47b9dd52010-11-24 18:12:06 +00003919
cristy3ed852e2009-09-05 21:47:34 +00003920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +00003921 " jng_width: %16lu",(unsigned long) jng_height);
glennrp47b9dd52010-11-24 18:12:06 +00003922
cristy3ed852e2009-09-05 21:47:34 +00003923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3924 " jng_color_type: %16d",jng_color_type);
glennrp47b9dd52010-11-24 18:12:06 +00003925
cristy3ed852e2009-09-05 21:47:34 +00003926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3927 " jng_image_sample_depth: %3d",
3928 jng_image_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003929
cristy3ed852e2009-09-05 21:47:34 +00003930 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3931 " jng_image_compression_method:%3d",
3932 jng_image_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003933
cristy3ed852e2009-09-05 21:47:34 +00003934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3935 " jng_image_interlace_method: %3d",
3936 jng_image_interlace_method);
glennrp47b9dd52010-11-24 18:12:06 +00003937
cristy3ed852e2009-09-05 21:47:34 +00003938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3939 " jng_alpha_sample_depth: %3d",
3940 jng_alpha_sample_depth);
glennrp47b9dd52010-11-24 18:12:06 +00003941
cristy3ed852e2009-09-05 21:47:34 +00003942 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3943 " jng_alpha_compression_method:%3d",
3944 jng_alpha_compression_method);
glennrp47b9dd52010-11-24 18:12:06 +00003945
cristy3ed852e2009-09-05 21:47:34 +00003946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3947 " jng_alpha_filter_method: %3d",
3948 jng_alpha_filter_method);
glennrp47b9dd52010-11-24 18:12:06 +00003949
cristy3ed852e2009-09-05 21:47:34 +00003950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3951 " jng_alpha_interlace_method: %3d",
3952 jng_alpha_interlace_method);
3953 }
3954 }
glennrp47b9dd52010-11-24 18:12:06 +00003955
cristy3ed852e2009-09-05 21:47:34 +00003956 if (length)
3957 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00003958
cristy3ed852e2009-09-05 21:47:34 +00003959 continue;
3960 }
3961
3962
3963 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
3964 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
3965 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
3966 {
3967 /*
3968 o create color_image
3969 o open color_blob, attached to color_image
3970 o if (color type has alpha)
3971 open alpha_blob, attached to alpha_image
3972 */
3973
cristy73bd4a52010-10-05 11:24:23 +00003974 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
glennrp47b9dd52010-11-24 18:12:06 +00003975
cristy3ed852e2009-09-05 21:47:34 +00003976 if (color_image_info == (ImageInfo *) NULL)
3977 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00003978
cristy3ed852e2009-09-05 21:47:34 +00003979 GetImageInfo(color_image_info);
3980 color_image=AcquireImage(color_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00003981
cristy3ed852e2009-09-05 21:47:34 +00003982 if (color_image == (Image *) NULL)
3983 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3984
3985 if (logging != MagickFalse)
3986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3987 " Creating color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00003988
cristy3ed852e2009-09-05 21:47:34 +00003989 (void) AcquireUniqueFilename(color_image->filename);
3990 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
3991 exception);
glennrp0fe50b42010-11-16 03:52:51 +00003992
cristy3ed852e2009-09-05 21:47:34 +00003993 if (status == MagickFalse)
3994 return((Image *) NULL);
3995
3996 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
3997 {
3998 alpha_image_info=(ImageInfo *)
cristy73bd4a52010-10-05 11:24:23 +00003999 AcquireMagickMemory(sizeof(ImageInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004000
cristy3ed852e2009-09-05 21:47:34 +00004001 if (alpha_image_info == (ImageInfo *) NULL)
4002 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004003
cristy3ed852e2009-09-05 21:47:34 +00004004 GetImageInfo(alpha_image_info);
4005 alpha_image=AcquireImage(alpha_image_info);
glennrp0fe50b42010-11-16 03:52:51 +00004006
cristy3ed852e2009-09-05 21:47:34 +00004007 if (alpha_image == (Image *) NULL)
4008 {
4009 alpha_image=DestroyImage(alpha_image);
4010 ThrowReaderException(ResourceLimitError,
4011 "MemoryAllocationFailed");
4012 }
glennrp0fe50b42010-11-16 03:52:51 +00004013
cristy3ed852e2009-09-05 21:47:34 +00004014 if (logging != MagickFalse)
4015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4016 " Creating alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004017
cristy3ed852e2009-09-05 21:47:34 +00004018 (void) AcquireUniqueFilename(alpha_image->filename);
4019 status=OpenBlob(alpha_image_info,alpha_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);
glennrp0fe50b42010-11-16 03:52:51 +00004024
cristy3ed852e2009-09-05 21:47:34 +00004025 if (jng_alpha_compression_method == 0)
4026 {
4027 unsigned char
4028 data[18];
4029
4030 if (logging != MagickFalse)
4031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4032 " Writing IHDR chunk to alpha_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004033
cristy3ed852e2009-09-05 21:47:34 +00004034 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4035 "\211PNG\r\n\032\n");
glennrp0fe50b42010-11-16 03:52:51 +00004036
cristy3ed852e2009-09-05 21:47:34 +00004037 (void) WriteBlobMSBULong(alpha_image,13L);
4038 PNGType(data,mng_IHDR);
glennrp03812ae2010-12-24 01:31:34 +00004039 LogPNGChunk(logging,mng_IHDR,13L);
cristy3ed852e2009-09-05 21:47:34 +00004040 PNGLong(data+4,jng_width);
4041 PNGLong(data+8,jng_height);
4042 data[12]=jng_alpha_sample_depth;
4043 data[13]=0; /* color_type gray */
4044 data[14]=0; /* compression method 0 */
4045 data[15]=0; /* filter_method 0 */
4046 data[16]=0; /* interlace_method 0 */
4047 (void) WriteBlob(alpha_image,17,data);
4048 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4049 }
4050 }
4051 reading_idat=MagickTrue;
4052 }
4053
4054 if (memcmp(type,mng_JDAT,4) == 0)
4055 {
glennrp47b9dd52010-11-24 18:12:06 +00004056 /* Copy chunk to color_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004057
4058 if (logging != MagickFalse)
4059 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4060 " Copying JDAT chunk data to color_blob.");
4061
4062 (void) WriteBlob(color_image,length,chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004063
cristy3ed852e2009-09-05 21:47:34 +00004064 if (length)
4065 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00004066
cristy3ed852e2009-09-05 21:47:34 +00004067 continue;
4068 }
4069
4070 if (memcmp(type,mng_IDAT,4) == 0)
4071 {
4072 png_byte
4073 data[5];
4074
glennrp47b9dd52010-11-24 18:12:06 +00004075 /* Copy IDAT header and chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004076
4077 if (image_info->ping == MagickFalse)
4078 {
4079 if (logging != MagickFalse)
4080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4081 " Copying IDAT chunk data to alpha_blob.");
4082
cristybb503372010-05-27 20:51:26 +00004083 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +00004084 PNGType(data,mng_IDAT);
glennrp03812ae2010-12-24 01:31:34 +00004085 LogPNGChunk(logging,mng_IDAT,length);
cristy3ed852e2009-09-05 21:47:34 +00004086 (void) WriteBlob(alpha_image,4,data);
4087 (void) WriteBlob(alpha_image,length,chunk);
4088 (void) WriteBlobMSBULong(alpha_image,
4089 crc32(crc32(0,data,4),chunk,(uInt) length));
4090 }
glennrp0fe50b42010-11-16 03:52:51 +00004091
cristy3ed852e2009-09-05 21:47:34 +00004092 if (length)
4093 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004094
cristy3ed852e2009-09-05 21:47:34 +00004095 continue;
4096 }
4097
4098 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4099 {
glennrp47b9dd52010-11-24 18:12:06 +00004100 /* Copy chunk data to alpha_image->blob */
cristy3ed852e2009-09-05 21:47:34 +00004101
4102 if (image_info->ping == MagickFalse)
4103 {
4104 if (logging != MagickFalse)
4105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4106 " Copying JDAA chunk data to alpha_blob.");
4107
4108 (void) WriteBlob(alpha_image,length,chunk);
4109 }
glennrp0fe50b42010-11-16 03:52:51 +00004110
cristy3ed852e2009-09-05 21:47:34 +00004111 if (length)
4112 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004113
cristy3ed852e2009-09-05 21:47:34 +00004114 continue;
4115 }
4116
4117 if (memcmp(type,mng_JSEP,4) == 0)
4118 {
4119 read_JSEP=MagickTrue;
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_bKGD,4) == 0)
4128 {
4129 if (length == 2)
4130 {
4131 image->background_color.red=ScaleCharToQuantum(p[1]);
4132 image->background_color.green=image->background_color.red;
4133 image->background_color.blue=image->background_color.red;
4134 }
glennrp0fe50b42010-11-16 03:52:51 +00004135
cristy3ed852e2009-09-05 21:47:34 +00004136 if (length == 6)
4137 {
4138 image->background_color.red=ScaleCharToQuantum(p[1]);
4139 image->background_color.green=ScaleCharToQuantum(p[3]);
4140 image->background_color.blue=ScaleCharToQuantum(p[5]);
4141 }
glennrp0fe50b42010-11-16 03:52:51 +00004142
cristy3ed852e2009-09-05 21:47:34 +00004143 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4144 continue;
4145 }
4146
4147 if (memcmp(type,mng_gAMA,4) == 0)
4148 {
4149 if (length == 4)
cristy8182b072010-05-30 20:10:53 +00004150 image->gamma=((float) mng_get_long(p))*0.00001;
glennrp0fe50b42010-11-16 03:52:51 +00004151
cristy3ed852e2009-09-05 21:47:34 +00004152 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4153 continue;
4154 }
4155
4156 if (memcmp(type,mng_cHRM,4) == 0)
4157 {
4158 if (length == 32)
4159 {
cristy8182b072010-05-30 20:10:53 +00004160 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4161 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4162 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4163 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4164 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4165 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4166 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4167 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00004168 }
glennrp47b9dd52010-11-24 18:12:06 +00004169
cristy3ed852e2009-09-05 21:47:34 +00004170 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4171 continue;
4172 }
4173
4174 if (memcmp(type,mng_sRGB,4) == 0)
4175 {
4176 if (length == 1)
4177 {
glennrpe610a072010-08-05 17:08:46 +00004178 image->rendering_intent=
glennrpcf002022011-01-30 02:38:15 +00004179 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00004180 image->gamma=0.45455f;
4181 image->chromaticity.red_primary.x=0.6400f;
4182 image->chromaticity.red_primary.y=0.3300f;
4183 image->chromaticity.green_primary.x=0.3000f;
4184 image->chromaticity.green_primary.y=0.6000f;
4185 image->chromaticity.blue_primary.x=0.1500f;
4186 image->chromaticity.blue_primary.y=0.0600f;
4187 image->chromaticity.white_point.x=0.3127f;
4188 image->chromaticity.white_point.y=0.3290f;
4189 }
glennrp47b9dd52010-11-24 18:12:06 +00004190
cristy3ed852e2009-09-05 21:47:34 +00004191 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4192 continue;
4193 }
4194
4195 if (memcmp(type,mng_oFFs,4) == 0)
4196 {
4197 if (length > 8)
4198 {
glennrp5eae7602011-02-22 15:21:32 +00004199 image->page.x=(ssize_t) mng_get_long(p);
4200 image->page.y=(ssize_t) mng_get_long(&p[4]);
glennrp0fe50b42010-11-16 03:52:51 +00004201
cristy3ed852e2009-09-05 21:47:34 +00004202 if ((int) p[8] != 0)
4203 {
4204 image->page.x/=10000;
4205 image->page.y/=10000;
4206 }
4207 }
glennrp47b9dd52010-11-24 18:12:06 +00004208
cristy3ed852e2009-09-05 21:47:34 +00004209 if (length)
4210 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004211
cristy3ed852e2009-09-05 21:47:34 +00004212 continue;
4213 }
4214
4215 if (memcmp(type,mng_pHYs,4) == 0)
4216 {
4217 if (length > 8)
4218 {
cristy8182b072010-05-30 20:10:53 +00004219 image->x_resolution=(double) mng_get_long(p);
4220 image->y_resolution=(double) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00004221 if ((int) p[8] == PNG_RESOLUTION_METER)
4222 {
4223 image->units=PixelsPerCentimeterResolution;
4224 image->x_resolution=image->x_resolution/100.0f;
4225 image->y_resolution=image->y_resolution/100.0f;
4226 }
4227 }
glennrp0fe50b42010-11-16 03:52:51 +00004228
cristy3ed852e2009-09-05 21:47:34 +00004229 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4230 continue;
4231 }
4232
4233#if 0
4234 if (memcmp(type,mng_iCCP,4) == 0)
4235 {
glennrpfd05d622011-02-25 04:10:33 +00004236 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00004237 if (length)
4238 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004239
cristy3ed852e2009-09-05 21:47:34 +00004240 continue;
4241 }
4242#endif
4243
4244 if (length)
4245 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4246
4247 if (memcmp(type,mng_IEND,4))
4248 continue;
glennrp0fe50b42010-11-16 03:52:51 +00004249
cristy3ed852e2009-09-05 21:47:34 +00004250 break;
4251 }
4252
4253
4254 /* IEND found */
4255
4256 /*
4257 Finish up reading image data:
4258
4259 o read main image from color_blob.
4260
4261 o close color_blob.
4262
4263 o if (color_type has alpha)
4264 if alpha_encoding is PNG
4265 read secondary image from alpha_blob via ReadPNG
4266 if alpha_encoding is JPEG
4267 read secondary image from alpha_blob via ReadJPEG
4268
4269 o close alpha_blob.
4270
4271 o copy intensity of secondary image into
cristy4c08aed2011-07-01 19:47:50 +00004272 alpha samples of main image.
cristy3ed852e2009-09-05 21:47:34 +00004273
4274 o destroy the secondary image.
4275 */
4276
4277 (void) CloseBlob(color_image);
glennrp47b9dd52010-11-24 18:12:06 +00004278
cristy3ed852e2009-09-05 21:47:34 +00004279 if (logging != MagickFalse)
4280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4281 " Reading jng_image from color_blob.");
glennrp0fe50b42010-11-16 03:52:51 +00004282
cristy3b6fd2e2011-05-20 12:53:50 +00004283 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00004284 color_image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004285
cristy3ed852e2009-09-05 21:47:34 +00004286 color_image_info->ping=MagickFalse; /* To do: avoid this */
4287 jng_image=ReadImage(color_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004288
cristy3ed852e2009-09-05 21:47:34 +00004289 if (jng_image == (Image *) NULL)
4290 return((Image *) NULL);
4291
4292 (void) RelinquishUniqueFileResource(color_image->filename);
4293 color_image=DestroyImage(color_image);
4294 color_image_info=DestroyImageInfo(color_image_info);
4295
4296 if (jng_image == (Image *) NULL)
4297 return((Image *) NULL);
4298
4299 if (logging != MagickFalse)
4300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4301 " Copying jng_image pixels to main image.");
glennrp0fe50b42010-11-16 03:52:51 +00004302
cristy3ed852e2009-09-05 21:47:34 +00004303 image->rows=jng_height;
4304 image->columns=jng_width;
glennrp0fe50b42010-11-16 03:52:51 +00004305
cristybb503372010-05-27 20:51:26 +00004306 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004307 {
4308 s=GetVirtualPixels(jng_image,0,y,image->columns,1,&image->exception);
4309 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00004310 for (x=(ssize_t) image->columns; x != 0; x--)
4311 {
4312 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4313 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4314 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4315 q+=GetPixelChannels(image);
4316 s+=GetPixelChannels(jng_image);
4317 }
glennrp47b9dd52010-11-24 18:12:06 +00004318
cristy3ed852e2009-09-05 21:47:34 +00004319 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4320 break;
4321 }
glennrp0fe50b42010-11-16 03:52:51 +00004322
cristy3ed852e2009-09-05 21:47:34 +00004323 jng_image=DestroyImage(jng_image);
glennrp0fe50b42010-11-16 03:52:51 +00004324
cristy3ed852e2009-09-05 21:47:34 +00004325 if (image_info->ping == MagickFalse)
4326 {
4327 if (jng_color_type >= 12)
4328 {
4329 if (jng_alpha_compression_method == 0)
4330 {
4331 png_byte
4332 data[5];
4333 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4334 PNGType(data,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +00004335 LogPNGChunk(logging,mng_IEND,0L);
cristy3ed852e2009-09-05 21:47:34 +00004336 (void) WriteBlob(alpha_image,4,data);
4337 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4338 }
glennrp0fe50b42010-11-16 03:52:51 +00004339
cristy3ed852e2009-09-05 21:47:34 +00004340 (void) CloseBlob(alpha_image);
glennrp0fe50b42010-11-16 03:52:51 +00004341
cristy3ed852e2009-09-05 21:47:34 +00004342 if (logging != MagickFalse)
4343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00004344 " Reading alpha from alpha_blob.");
cristy3ed852e2009-09-05 21:47:34 +00004345
cristy3b6fd2e2011-05-20 12:53:50 +00004346 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00004347 "%s",alpha_image->filename);
4348
4349 jng_image=ReadImage(alpha_image_info,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004350
cristy3ed852e2009-09-05 21:47:34 +00004351 if (jng_image != (Image *) NULL)
cristybb503372010-05-27 20:51:26 +00004352 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00004353 {
4354 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
cristy4c08aed2011-07-01 19:47:50 +00004355 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +00004356 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00004357
cristy3ed852e2009-09-05 21:47:34 +00004358 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00004359 for (x=(ssize_t) image->columns; x != 0; x--)
4360 {
4361 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4362 q+=GetPixelChannels(image);
4363 s+=GetPixelChannels(jng_image);
4364 }
glennrp0fe50b42010-11-16 03:52:51 +00004365
cristy3ed852e2009-09-05 21:47:34 +00004366 else
cristy4c08aed2011-07-01 19:47:50 +00004367 for (x=(ssize_t) image->columns; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00004368 {
cristy4c08aed2011-07-01 19:47:50 +00004369 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4370 if (GetPixelAlpha(image,q) != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +00004371 image->matte=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00004372 q+=GetPixelChannels(image);
4373 s+=GetPixelChannels(jng_image);
cristy3ed852e2009-09-05 21:47:34 +00004374 }
glennrp0fe50b42010-11-16 03:52:51 +00004375
cristy3ed852e2009-09-05 21:47:34 +00004376 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4377 break;
4378 }
4379 (void) RelinquishUniqueFileResource(alpha_image->filename);
4380 alpha_image=DestroyImage(alpha_image);
4381 alpha_image_info=DestroyImageInfo(alpha_image_info);
4382 if (jng_image != (Image *) NULL)
4383 jng_image=DestroyImage(jng_image);
4384 }
4385 }
4386
glennrp47b9dd52010-11-24 18:12:06 +00004387 /* Read the JNG image. */
4388
cristy3ed852e2009-09-05 21:47:34 +00004389 if (mng_info->mng_type == 0)
4390 {
4391 mng_info->mng_width=jng_width;
4392 mng_info->mng_height=jng_height;
4393 }
glennrp0fe50b42010-11-16 03:52:51 +00004394
cristy3ed852e2009-09-05 21:47:34 +00004395 if (image->page.width == 0 && image->page.height == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004396 {
4397 image->page.width=jng_width;
4398 image->page.height=jng_height;
4399 }
4400
cristy3ed852e2009-09-05 21:47:34 +00004401 if (image->page.x == 0 && image->page.y == 0)
glennrp0fe50b42010-11-16 03:52:51 +00004402 {
4403 image->page.x=mng_info->x_off[mng_info->object_id];
4404 image->page.y=mng_info->y_off[mng_info->object_id];
4405 }
4406
cristy3ed852e2009-09-05 21:47:34 +00004407 else
glennrp0fe50b42010-11-16 03:52:51 +00004408 {
4409 image->page.y=mng_info->y_off[mng_info->object_id];
4410 }
4411
cristy3ed852e2009-09-05 21:47:34 +00004412 mng_info->image_found++;
4413 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4414 2*GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00004415
cristy3ed852e2009-09-05 21:47:34 +00004416 if (logging != MagickFalse)
4417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4418 " exit ReadOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004419
cristy3ed852e2009-09-05 21:47:34 +00004420 return(image);
4421}
4422
4423/*
4424%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4425% %
4426% %
4427% %
4428% R e a d J N G I m a g e %
4429% %
4430% %
4431% %
4432%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4433%
4434% ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4435% (including the 8-byte signature) and returns it. It allocates the memory
4436% necessary for the new Image structure and returns a pointer to the new
4437% image.
4438%
4439% JNG support written by Glenn Randers-Pehrson, glennrp@image...
4440%
4441% The format of the ReadJNGImage method is:
4442%
4443% Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4444% *exception)
4445%
4446% A description of each parameter follows:
4447%
4448% o image_info: the image info.
4449%
4450% o exception: return any errors or warnings in this structure.
4451%
4452*/
4453
4454static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4455{
4456 Image
4457 *image,
4458 *previous;
4459
4460 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004461 have_mng_structure,
4462 logging,
cristy3ed852e2009-09-05 21:47:34 +00004463 status;
4464
4465 MngInfo
4466 *mng_info;
4467
4468 char
4469 magic_number[MaxTextExtent];
4470
cristy3ed852e2009-09-05 21:47:34 +00004471 size_t
4472 count;
4473
4474 /*
4475 Open image file.
4476 */
4477 assert(image_info != (const ImageInfo *) NULL);
4478 assert(image_info->signature == MagickSignature);
4479 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4480 assert(exception != (ExceptionInfo *) NULL);
4481 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004482 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004483 image=AcquireImage(image_info);
4484 mng_info=(MngInfo *) NULL;
4485 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004486
cristy3ed852e2009-09-05 21:47:34 +00004487 if (status == MagickFalse)
4488 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004489
cristy3ed852e2009-09-05 21:47:34 +00004490 if (LocaleCompare(image_info->magick,"JNG") != 0)
4491 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004492
glennrp47b9dd52010-11-24 18:12:06 +00004493 /* Verify JNG signature. */
4494
cristy3ed852e2009-09-05 21:47:34 +00004495 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
glennrp47b9dd52010-11-24 18:12:06 +00004496
glennrp3b8763e2011-02-21 12:08:18 +00004497 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
cristy3ed852e2009-09-05 21:47:34 +00004498 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp0fe50b42010-11-16 03:52:51 +00004499
glennrp47b9dd52010-11-24 18:12:06 +00004500 /* Allocate a MngInfo structure. */
4501
cristy3ed852e2009-09-05 21:47:34 +00004502 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +00004503 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
glennrp0fe50b42010-11-16 03:52:51 +00004504
cristy3ed852e2009-09-05 21:47:34 +00004505 if (mng_info == (MngInfo *) NULL)
4506 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004507
glennrp47b9dd52010-11-24 18:12:06 +00004508 /* Initialize members of the MngInfo structure. */
4509
cristy3ed852e2009-09-05 21:47:34 +00004510 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4511 have_mng_structure=MagickTrue;
4512
4513 mng_info->image=image;
4514 previous=image;
4515 image=ReadOneJNGImage(mng_info,image_info,exception);
4516 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +00004517
cristy3ed852e2009-09-05 21:47:34 +00004518 if (image == (Image *) NULL)
4519 {
4520 if (IsImageObject(previous) != MagickFalse)
4521 {
4522 (void) CloseBlob(previous);
4523 (void) DestroyImageList(previous);
4524 }
glennrp0fe50b42010-11-16 03:52:51 +00004525
cristy3ed852e2009-09-05 21:47:34 +00004526 if (logging != MagickFalse)
4527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4528 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004529
cristy3ed852e2009-09-05 21:47:34 +00004530 return((Image *) NULL);
4531 }
4532 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00004533
cristy3ed852e2009-09-05 21:47:34 +00004534 if (image->columns == 0 || image->rows == 0)
4535 {
4536 if (logging != MagickFalse)
4537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4538 "exit ReadJNGImage() with error");
glennrp0fe50b42010-11-16 03:52:51 +00004539
cristy3ed852e2009-09-05 21:47:34 +00004540 ThrowReaderException(CorruptImageError,"CorruptImage");
4541 }
glennrp0fe50b42010-11-16 03:52:51 +00004542
cristy3ed852e2009-09-05 21:47:34 +00004543 if (logging != MagickFalse)
4544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +00004545
cristy3ed852e2009-09-05 21:47:34 +00004546 return(image);
4547}
4548#endif
4549
4550static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4551{
4552 char
4553 page_geometry[MaxTextExtent];
4554
4555 Image
4556 *image,
4557 *previous;
4558
cristy4383ec82011-01-05 15:42:32 +00004559 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +00004560 logging,
4561 have_mng_structure;
cristy4383ec82011-01-05 15:42:32 +00004562
cristy3ed852e2009-09-05 21:47:34 +00004563 volatile int
4564 first_mng_object,
cristy3ed852e2009-09-05 21:47:34 +00004565 object_id,
4566 term_chunk_found,
4567 skip_to_iend;
4568
cristybb503372010-05-27 20:51:26 +00004569 volatile ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004570 image_count=0;
4571
4572 MagickBooleanType
4573 status;
4574
4575 MagickOffsetType
4576 offset;
4577
4578 MngInfo
4579 *mng_info;
4580
4581 MngBox
4582 default_fb,
4583 fb,
4584 previous_fb;
4585
4586#if defined(MNG_INSERT_LAYERS)
4587 PixelPacket
4588 mng_background_color;
4589#endif
4590
4591 register unsigned char
4592 *p;
4593
cristybb503372010-05-27 20:51:26 +00004594 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004595 i;
4596
4597 size_t
4598 count;
4599
cristybb503372010-05-27 20:51:26 +00004600 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00004601 loop_level;
4602
4603 volatile short
4604 skipping_loop;
4605
4606#if defined(MNG_INSERT_LAYERS)
4607 unsigned int
4608 mandatory_back=0;
4609#endif
4610
4611 volatile unsigned int
4612#ifdef MNG_OBJECT_BUFFERS
4613 mng_background_object=0,
4614#endif
4615 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4616
cristybb503372010-05-27 20:51:26 +00004617 size_t
cristy3ed852e2009-09-05 21:47:34 +00004618 default_frame_timeout,
4619 frame_timeout,
4620#if defined(MNG_INSERT_LAYERS)
4621 image_height,
4622 image_width,
4623#endif
4624 length;
4625
glennrp38ea0832010-06-02 18:50:28 +00004626 /* These delays are all measured in image ticks_per_second,
4627 * not in MNG ticks_per_second
4628 */
cristybb503372010-05-27 20:51:26 +00004629 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +00004630 default_frame_delay,
4631 final_delay,
4632 final_image_delay,
4633 frame_delay,
4634#if defined(MNG_INSERT_LAYERS)
4635 insert_layers,
4636#endif
4637 mng_iterations=1,
4638 simplicity=0,
4639 subframe_height=0,
4640 subframe_width=0;
4641
4642 previous_fb.top=0;
4643 previous_fb.bottom=0;
4644 previous_fb.left=0;
4645 previous_fb.right=0;
4646 default_fb.top=0;
4647 default_fb.bottom=0;
4648 default_fb.left=0;
4649 default_fb.right=0;
4650
glennrp47b9dd52010-11-24 18:12:06 +00004651 /* Open image file. */
4652
cristy3ed852e2009-09-05 21:47:34 +00004653 assert(image_info != (const ImageInfo *) NULL);
4654 assert(image_info->signature == MagickSignature);
4655 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4656 assert(exception != (ExceptionInfo *) NULL);
4657 assert(exception->signature == MagickSignature);
glennrpfd05d622011-02-25 04:10:33 +00004658 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00004659 image=AcquireImage(image_info);
4660 mng_info=(MngInfo *) NULL;
4661 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
glennrp0fe50b42010-11-16 03:52:51 +00004662
cristy3ed852e2009-09-05 21:47:34 +00004663 if (status == MagickFalse)
4664 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004665
cristy3ed852e2009-09-05 21:47:34 +00004666 first_mng_object=MagickFalse;
4667 skipping_loop=(-1);
4668 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00004669
4670 /* Allocate a MngInfo structure. */
4671
cristy73bd4a52010-10-05 11:24:23 +00004672 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +00004673
cristy3ed852e2009-09-05 21:47:34 +00004674 if (mng_info == (MngInfo *) NULL)
4675 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00004676
glennrp47b9dd52010-11-24 18:12:06 +00004677 /* Initialize members of the MngInfo structure. */
4678
cristy3ed852e2009-09-05 21:47:34 +00004679 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4680 mng_info->image=image;
4681 have_mng_structure=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00004682
4683 if (LocaleCompare(image_info->magick,"MNG") == 0)
4684 {
4685 char
4686 magic_number[MaxTextExtent];
4687
glennrp47b9dd52010-11-24 18:12:06 +00004688 /* Verify MNG signature. */
cristy3ed852e2009-09-05 21:47:34 +00004689 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4690 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
4691 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00004692
4693 /* Initialize some nonzero members of the MngInfo structure. */
cristy3ed852e2009-09-05 21:47:34 +00004694 for (i=0; i < MNG_MAX_OBJECTS; i++)
4695 {
cristybb503372010-05-27 20:51:26 +00004696 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
4697 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
cristy3ed852e2009-09-05 21:47:34 +00004698 }
4699 mng_info->exists[0]=MagickTrue;
4700 }
glennrp47b9dd52010-11-24 18:12:06 +00004701
cristy3ed852e2009-09-05 21:47:34 +00004702 first_mng_object=MagickTrue;
4703 mng_type=0;
4704#if defined(MNG_INSERT_LAYERS)
4705 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
4706#endif
4707 default_frame_delay=0;
4708 default_frame_timeout=0;
4709 frame_delay=0;
4710 final_delay=1;
4711 mng_info->ticks_per_second=1UL*image->ticks_per_second;
4712 object_id=0;
4713 skip_to_iend=MagickFalse;
4714 term_chunk_found=MagickFalse;
4715 mng_info->framing_mode=1;
4716#if defined(MNG_INSERT_LAYERS)
4717 mandatory_back=MagickFalse;
4718#endif
4719#if defined(MNG_INSERT_LAYERS)
4720 mng_background_color=image->background_color;
4721#endif
4722 default_fb=mng_info->frame;
4723 previous_fb=mng_info->frame;
4724 do
4725 {
4726 char
4727 type[MaxTextExtent];
4728
4729 if (LocaleCompare(image_info->magick,"MNG") == 0)
4730 {
4731 unsigned char
4732 *chunk;
4733
4734 /*
4735 Read a new chunk.
4736 */
4737 type[0]='\0';
4738 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4739 length=ReadBlobMSBLong(image);
4740 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
4741
4742 if (logging != MagickFalse)
4743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004744 " Reading MNG chunk type %c%c%c%c, length: %.20g",
4745 type[0],type[1],type[2],type[3],(double) length);
cristy3ed852e2009-09-05 21:47:34 +00004746
4747 if (length > PNG_UINT_31_MAX)
4748 status=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004749
cristy3ed852e2009-09-05 21:47:34 +00004750 if (count == 0)
4751 ThrowReaderException(CorruptImageError,"CorruptImage");
glennrp0fe50b42010-11-16 03:52:51 +00004752
cristy3ed852e2009-09-05 21:47:34 +00004753 p=NULL;
4754 chunk=(unsigned char *) NULL;
glennrp0fe50b42010-11-16 03:52:51 +00004755
cristy3ed852e2009-09-05 21:47:34 +00004756 if (length)
4757 {
4758 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
glennrp47b9dd52010-11-24 18:12:06 +00004759
cristy3ed852e2009-09-05 21:47:34 +00004760 if (chunk == (unsigned char *) NULL)
4761 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00004762
cristybb503372010-05-27 20:51:26 +00004763 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00004764 chunk[i]=(unsigned char) ReadBlobByte(image);
glennrp47b9dd52010-11-24 18:12:06 +00004765
cristy3ed852e2009-09-05 21:47:34 +00004766 p=chunk;
4767 }
glennrp0fe50b42010-11-16 03:52:51 +00004768
cristy3ed852e2009-09-05 21:47:34 +00004769 (void) ReadBlobMSBLong(image); /* read crc word */
4770
4771#if !defined(JNG_SUPPORTED)
4772 if (memcmp(type,mng_JHDR,4) == 0)
4773 {
4774 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004775
cristy3ed852e2009-09-05 21:47:34 +00004776 if (mng_info->jhdr_warning == 0)
4777 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4778 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004779
cristy3ed852e2009-09-05 21:47:34 +00004780 mng_info->jhdr_warning++;
4781 }
4782#endif
4783 if (memcmp(type,mng_DHDR,4) == 0)
4784 {
4785 skip_to_iend=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004786
cristy3ed852e2009-09-05 21:47:34 +00004787 if (mng_info->dhdr_warning == 0)
4788 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4789 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004790
cristy3ed852e2009-09-05 21:47:34 +00004791 mng_info->dhdr_warning++;
4792 }
4793 if (memcmp(type,mng_MEND,4) == 0)
4794 break;
glennrp47b9dd52010-11-24 18:12:06 +00004795
cristy3ed852e2009-09-05 21:47:34 +00004796 if (skip_to_iend)
4797 {
4798 if (memcmp(type,mng_IEND,4) == 0)
4799 skip_to_iend=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00004800
cristy3ed852e2009-09-05 21:47:34 +00004801 if (length)
4802 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00004803
cristy3ed852e2009-09-05 21:47:34 +00004804 if (logging != MagickFalse)
4805 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4806 " Skip to IEND.");
glennrp0fe50b42010-11-16 03:52:51 +00004807
cristy3ed852e2009-09-05 21:47:34 +00004808 continue;
4809 }
glennrp0fe50b42010-11-16 03:52:51 +00004810
cristy3ed852e2009-09-05 21:47:34 +00004811 if (memcmp(type,mng_MHDR,4) == 0)
4812 {
cristybb503372010-05-27 20:51:26 +00004813 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004814 (p[2] << 8) | p[3]);
glennrp0fe50b42010-11-16 03:52:51 +00004815
cristybb503372010-05-27 20:51:26 +00004816 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00004817 (p[6] << 8) | p[7]);
glennrp0fe50b42010-11-16 03:52:51 +00004818
cristy3ed852e2009-09-05 21:47:34 +00004819 if (logging != MagickFalse)
4820 {
4821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004822 " MNG width: %.20g",(double) mng_info->mng_width);
cristy3ed852e2009-09-05 21:47:34 +00004823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004824 " MNG height: %.20g",(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00004825 }
glennrp0fe50b42010-11-16 03:52:51 +00004826
cristy3ed852e2009-09-05 21:47:34 +00004827 p+=8;
cristy8182b072010-05-30 20:10:53 +00004828 mng_info->ticks_per_second=(size_t) mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00004829
cristy3ed852e2009-09-05 21:47:34 +00004830 if (mng_info->ticks_per_second == 0)
4831 default_frame_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00004832
cristy3ed852e2009-09-05 21:47:34 +00004833 else
4834 default_frame_delay=1UL*image->ticks_per_second/
4835 mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00004836
cristy3ed852e2009-09-05 21:47:34 +00004837 frame_delay=default_frame_delay;
4838 simplicity=0;
glennrp0fe50b42010-11-16 03:52:51 +00004839
cristy3ed852e2009-09-05 21:47:34 +00004840 if (length > 16)
4841 {
4842 p+=16;
cristy8182b072010-05-30 20:10:53 +00004843 simplicity=(size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00004844 }
glennrp0fe50b42010-11-16 03:52:51 +00004845
cristy3ed852e2009-09-05 21:47:34 +00004846 mng_type=1; /* Full MNG */
glennrp0fe50b42010-11-16 03:52:51 +00004847
cristy3ed852e2009-09-05 21:47:34 +00004848 if ((simplicity != 0) && ((simplicity | 11) == 11))
4849 mng_type=2; /* LC */
glennrp0fe50b42010-11-16 03:52:51 +00004850
cristy3ed852e2009-09-05 21:47:34 +00004851 if ((simplicity != 0) && ((simplicity | 9) == 9))
4852 mng_type=3; /* VLC */
glennrp0fe50b42010-11-16 03:52:51 +00004853
cristy3ed852e2009-09-05 21:47:34 +00004854#if defined(MNG_INSERT_LAYERS)
4855 if (mng_type != 3)
4856 insert_layers=MagickTrue;
4857#endif
cristy4c08aed2011-07-01 19:47:50 +00004858 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00004859 {
glennrp47b9dd52010-11-24 18:12:06 +00004860 /* Allocate next image structure. */
cristy3ed852e2009-09-05 21:47:34 +00004861 AcquireNextImage(image_info,image);
glennrp0fe50b42010-11-16 03:52:51 +00004862
cristy3ed852e2009-09-05 21:47:34 +00004863 if (GetNextImageInList(image) == (Image *) NULL)
4864 return((Image *) NULL);
glennrp0fe50b42010-11-16 03:52:51 +00004865
cristy3ed852e2009-09-05 21:47:34 +00004866 image=SyncNextImageInList(image);
4867 mng_info->image=image;
4868 }
4869
4870 if ((mng_info->mng_width > 65535L) ||
4871 (mng_info->mng_height > 65535L))
4872 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
glennrp0fe50b42010-11-16 03:52:51 +00004873
cristy3b6fd2e2011-05-20 12:53:50 +00004874 (void) FormatLocaleString(page_geometry,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00004875 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
cristyf2faecf2010-05-28 19:19:36 +00004876 mng_info->mng_height);
glennrp0fe50b42010-11-16 03:52:51 +00004877
cristy3ed852e2009-09-05 21:47:34 +00004878 mng_info->frame.left=0;
cristybb503372010-05-27 20:51:26 +00004879 mng_info->frame.right=(ssize_t) mng_info->mng_width;
cristy3ed852e2009-09-05 21:47:34 +00004880 mng_info->frame.top=0;
cristybb503372010-05-27 20:51:26 +00004881 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
cristy3ed852e2009-09-05 21:47:34 +00004882 mng_info->clip=default_fb=previous_fb=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004883
cristy3ed852e2009-09-05 21:47:34 +00004884 for (i=0; i < MNG_MAX_OBJECTS; i++)
4885 mng_info->object_clip[i]=mng_info->frame;
glennrp0fe50b42010-11-16 03:52:51 +00004886
cristy3ed852e2009-09-05 21:47:34 +00004887 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4888 continue;
4889 }
4890
4891 if (memcmp(type,mng_TERM,4) == 0)
4892 {
4893 int
4894 repeat=0;
4895
4896
4897 if (length)
4898 repeat=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00004899
cristy3ed852e2009-09-05 21:47:34 +00004900 if (repeat == 3)
4901 {
cristy8182b072010-05-30 20:10:53 +00004902 final_delay=(png_uint_32) mng_get_long(&p[2]);
4903 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
glennrp0fe50b42010-11-16 03:52:51 +00004904
cristy3ed852e2009-09-05 21:47:34 +00004905 if (mng_iterations == PNG_UINT_31_MAX)
4906 mng_iterations=0;
glennrp0fe50b42010-11-16 03:52:51 +00004907
cristy3ed852e2009-09-05 21:47:34 +00004908 image->iterations=mng_iterations;
4909 term_chunk_found=MagickTrue;
4910 }
glennrp0fe50b42010-11-16 03:52:51 +00004911
cristy3ed852e2009-09-05 21:47:34 +00004912 if (logging != MagickFalse)
4913 {
4914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4915 " repeat=%d",repeat);
glennrp0fe50b42010-11-16 03:52:51 +00004916
cristy3ed852e2009-09-05 21:47:34 +00004917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004918 " final_delay=%.20g",(double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00004919
cristy3ed852e2009-09-05 21:47:34 +00004920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004921 " image->iterations=%.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +00004922 }
glennrp0fe50b42010-11-16 03:52:51 +00004923
cristy3ed852e2009-09-05 21:47:34 +00004924 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4925 continue;
4926 }
4927 if (memcmp(type,mng_DEFI,4) == 0)
4928 {
4929 if (mng_type == 3)
4930 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4931 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
4932 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004933
cristy3ed852e2009-09-05 21:47:34 +00004934 object_id=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00004935
cristy3ed852e2009-09-05 21:47:34 +00004936 if (mng_type == 2 && object_id != 0)
4937 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4938 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
4939 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00004940
cristy3ed852e2009-09-05 21:47:34 +00004941 if (object_id > MNG_MAX_OBJECTS)
4942 {
4943 /*
4944 Instead ofsuing a warning we should allocate a larger
4945 MngInfo structure and continue.
4946 */
4947 (void) ThrowMagickException(&image->exception,GetMagickModule(),
4948 CoderError,"object id too large","`%s'",image->filename);
4949 object_id=MNG_MAX_OBJECTS;
4950 }
glennrp0fe50b42010-11-16 03:52:51 +00004951
cristy3ed852e2009-09-05 21:47:34 +00004952 if (mng_info->exists[object_id])
4953 if (mng_info->frozen[object_id])
4954 {
4955 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4956 (void) ThrowMagickException(&image->exception,
4957 GetMagickModule(),CoderError,
4958 "DEFI cannot redefine a frozen MNG object","`%s'",
4959 image->filename);
4960 continue;
4961 }
glennrp0fe50b42010-11-16 03:52:51 +00004962
cristy3ed852e2009-09-05 21:47:34 +00004963 mng_info->exists[object_id]=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00004964
cristy3ed852e2009-09-05 21:47:34 +00004965 if (length > 2)
4966 mng_info->invisible[object_id]=p[2];
glennrp0fe50b42010-11-16 03:52:51 +00004967
cristy3ed852e2009-09-05 21:47:34 +00004968 /*
4969 Extract object offset info.
4970 */
4971 if (length > 11)
4972 {
glennrp0fe50b42010-11-16 03:52:51 +00004973 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
4974 (p[5] << 16) | (p[6] << 8) | p[7]);
4975
4976 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
4977 (p[9] << 16) | (p[10] << 8) | p[11]);
4978
cristy3ed852e2009-09-05 21:47:34 +00004979 if (logging != MagickFalse)
4980 {
4981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004982 " x_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004983 mng_info->x_off[object_id]);
glennrp0fe50b42010-11-16 03:52:51 +00004984
cristy3ed852e2009-09-05 21:47:34 +00004985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00004986 " y_off[%d]: %.20g",object_id,(double)
cristyf2faecf2010-05-28 19:19:36 +00004987 mng_info->y_off[object_id]);
cristy3ed852e2009-09-05 21:47:34 +00004988 }
4989 }
glennrp0fe50b42010-11-16 03:52:51 +00004990
cristy3ed852e2009-09-05 21:47:34 +00004991 /*
4992 Extract object clipping info.
4993 */
4994 if (length > 27)
4995 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
4996 &p[12]);
glennrp0fe50b42010-11-16 03:52:51 +00004997
cristy3ed852e2009-09-05 21:47:34 +00004998 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4999 continue;
5000 }
5001 if (memcmp(type,mng_bKGD,4) == 0)
5002 {
5003 mng_info->have_global_bkgd=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005004
cristy3ed852e2009-09-05 21:47:34 +00005005 if (length > 5)
5006 {
5007 mng_info->mng_global_bkgd.red=
5008 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005009
cristy3ed852e2009-09-05 21:47:34 +00005010 mng_info->mng_global_bkgd.green=
5011 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005012
cristy3ed852e2009-09-05 21:47:34 +00005013 mng_info->mng_global_bkgd.blue=
5014 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005015
cristy3ed852e2009-09-05 21:47:34 +00005016 mng_info->have_global_bkgd=MagickTrue;
5017 }
glennrp0fe50b42010-11-16 03:52:51 +00005018
cristy3ed852e2009-09-05 21:47:34 +00005019 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5020 continue;
5021 }
5022 if (memcmp(type,mng_BACK,4) == 0)
5023 {
5024#if defined(MNG_INSERT_LAYERS)
5025 if (length > 6)
5026 mandatory_back=p[6];
glennrp0fe50b42010-11-16 03:52:51 +00005027
cristy3ed852e2009-09-05 21:47:34 +00005028 else
5029 mandatory_back=0;
glennrp0fe50b42010-11-16 03:52:51 +00005030
cristy3ed852e2009-09-05 21:47:34 +00005031 if (mandatory_back && length > 5)
5032 {
5033 mng_background_color.red=
5034 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
glennrp0fe50b42010-11-16 03:52:51 +00005035
cristy3ed852e2009-09-05 21:47:34 +00005036 mng_background_color.green=
5037 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
glennrp0fe50b42010-11-16 03:52:51 +00005038
cristy3ed852e2009-09-05 21:47:34 +00005039 mng_background_color.blue=
5040 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
glennrp0fe50b42010-11-16 03:52:51 +00005041
cristy4c08aed2011-07-01 19:47:50 +00005042 mng_background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00005043 }
glennrp0fe50b42010-11-16 03:52:51 +00005044
cristy3ed852e2009-09-05 21:47:34 +00005045#ifdef MNG_OBJECT_BUFFERS
5046 if (length > 8)
5047 mng_background_object=(p[7] << 8) | p[8];
5048#endif
5049#endif
5050 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5051 continue;
5052 }
glennrp47b9dd52010-11-24 18:12:06 +00005053
cristy3ed852e2009-09-05 21:47:34 +00005054 if (memcmp(type,mng_PLTE,4) == 0)
5055 {
glennrp47b9dd52010-11-24 18:12:06 +00005056 /* Read global PLTE. */
5057
cristy3ed852e2009-09-05 21:47:34 +00005058 if (length && (length < 769))
5059 {
5060 if (mng_info->global_plte == (png_colorp) NULL)
5061 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5062 sizeof(*mng_info->global_plte));
glennrp0fe50b42010-11-16 03:52:51 +00005063
cristybb503372010-05-27 20:51:26 +00005064 for (i=0; i < (ssize_t) (length/3); i++)
cristy3ed852e2009-09-05 21:47:34 +00005065 {
5066 mng_info->global_plte[i].red=p[3*i];
5067 mng_info->global_plte[i].green=p[3*i+1];
5068 mng_info->global_plte[i].blue=p[3*i+2];
5069 }
glennrp0fe50b42010-11-16 03:52:51 +00005070
cristy35ef8242010-06-03 16:24:13 +00005071 mng_info->global_plte_length=(unsigned int) (length/3);
cristy3ed852e2009-09-05 21:47:34 +00005072 }
5073#ifdef MNG_LOOSE
5074 for ( ; i < 256; i++)
5075 {
5076 mng_info->global_plte[i].red=i;
5077 mng_info->global_plte[i].green=i;
5078 mng_info->global_plte[i].blue=i;
5079 }
glennrp0fe50b42010-11-16 03:52:51 +00005080
cristy3ed852e2009-09-05 21:47:34 +00005081 if (length)
5082 mng_info->global_plte_length=256;
5083#endif
5084 else
5085 mng_info->global_plte_length=0;
glennrp0fe50b42010-11-16 03:52:51 +00005086
cristy3ed852e2009-09-05 21:47:34 +00005087 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5088 continue;
5089 }
glennrp47b9dd52010-11-24 18:12:06 +00005090
cristy3ed852e2009-09-05 21:47:34 +00005091 if (memcmp(type,mng_tRNS,4) == 0)
5092 {
5093 /* read global tRNS */
5094
5095 if (length < 257)
cristybb503372010-05-27 20:51:26 +00005096 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00005097 mng_info->global_trns[i]=p[i];
5098
5099#ifdef MNG_LOOSE
5100 for ( ; i < 256; i++)
5101 mng_info->global_trns[i]=255;
5102#endif
cristy12560f32010-06-03 16:51:08 +00005103 mng_info->global_trns_length=(unsigned int) length;
cristy3ed852e2009-09-05 21:47:34 +00005104 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5105 continue;
5106 }
5107 if (memcmp(type,mng_gAMA,4) == 0)
5108 {
5109 if (length == 4)
5110 {
cristybb503372010-05-27 20:51:26 +00005111 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005112 igamma;
5113
cristy8182b072010-05-30 20:10:53 +00005114 igamma=mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005115 mng_info->global_gamma=((float) igamma)*0.00001;
5116 mng_info->have_global_gama=MagickTrue;
5117 }
glennrp0fe50b42010-11-16 03:52:51 +00005118
cristy3ed852e2009-09-05 21:47:34 +00005119 else
5120 mng_info->have_global_gama=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005121
cristy3ed852e2009-09-05 21:47:34 +00005122 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5123 continue;
5124 }
5125
5126 if (memcmp(type,mng_cHRM,4) == 0)
5127 {
glennrp47b9dd52010-11-24 18:12:06 +00005128 /* Read global cHRM */
5129
cristy3ed852e2009-09-05 21:47:34 +00005130 if (length == 32)
5131 {
cristy8182b072010-05-30 20:10:53 +00005132 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5133 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5134 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
cristy3ed852e2009-09-05 21:47:34 +00005135 mng_info->global_chrm.red_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005136 mng_get_long(&p[12]);
cristy3ed852e2009-09-05 21:47:34 +00005137 mng_info->global_chrm.green_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005138 mng_get_long(&p[16]);
cristy3ed852e2009-09-05 21:47:34 +00005139 mng_info->global_chrm.green_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005140 mng_get_long(&p[20]);
cristy3ed852e2009-09-05 21:47:34 +00005141 mng_info->global_chrm.blue_primary.x=0.00001*
cristy8182b072010-05-30 20:10:53 +00005142 mng_get_long(&p[24]);
cristy3ed852e2009-09-05 21:47:34 +00005143 mng_info->global_chrm.blue_primary.y=0.00001*
cristy8182b072010-05-30 20:10:53 +00005144 mng_get_long(&p[28]);
cristy3ed852e2009-09-05 21:47:34 +00005145 mng_info->have_global_chrm=MagickTrue;
5146 }
5147 else
5148 mng_info->have_global_chrm=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005149
cristy3ed852e2009-09-05 21:47:34 +00005150 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5151 continue;
5152 }
glennrp47b9dd52010-11-24 18:12:06 +00005153
cristy3ed852e2009-09-05 21:47:34 +00005154 if (memcmp(type,mng_sRGB,4) == 0)
5155 {
5156 /*
5157 Read global sRGB.
5158 */
5159 if (length)
5160 {
glennrpe610a072010-08-05 17:08:46 +00005161 mng_info->global_srgb_intent=
glennrpcf002022011-01-30 02:38:15 +00005162 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
cristy3ed852e2009-09-05 21:47:34 +00005163 mng_info->have_global_srgb=MagickTrue;
5164 }
5165 else
5166 mng_info->have_global_srgb=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005167
cristy3ed852e2009-09-05 21:47:34 +00005168 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5169 continue;
5170 }
glennrp47b9dd52010-11-24 18:12:06 +00005171
cristy3ed852e2009-09-05 21:47:34 +00005172 if (memcmp(type,mng_iCCP,4) == 0)
5173 {
glennrpfd05d622011-02-25 04:10:33 +00005174 /* To do: */
cristy3ed852e2009-09-05 21:47:34 +00005175
5176 /*
5177 Read global iCCP.
5178 */
5179 if (length)
5180 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005181
cristy3ed852e2009-09-05 21:47:34 +00005182 continue;
5183 }
glennrp47b9dd52010-11-24 18:12:06 +00005184
cristy3ed852e2009-09-05 21:47:34 +00005185 if (memcmp(type,mng_FRAM,4) == 0)
5186 {
5187 if (mng_type == 3)
5188 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5189 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5190 image->filename);
glennrp0fe50b42010-11-16 03:52:51 +00005191
cristy3ed852e2009-09-05 21:47:34 +00005192 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5193 image->delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005194
cristy3ed852e2009-09-05 21:47:34 +00005195 frame_delay=default_frame_delay;
5196 frame_timeout=default_frame_timeout;
5197 fb=default_fb;
glennrp47b9dd52010-11-24 18:12:06 +00005198
cristy3ed852e2009-09-05 21:47:34 +00005199 if (length)
5200 if (p[0])
5201 mng_info->framing_mode=p[0];
glennrp0fe50b42010-11-16 03:52:51 +00005202
cristy3ed852e2009-09-05 21:47:34 +00005203 if (logging != MagickFalse)
5204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5205 " Framing_mode=%d",mng_info->framing_mode);
glennrp0fe50b42010-11-16 03:52:51 +00005206
cristy3ed852e2009-09-05 21:47:34 +00005207 if (length > 6)
5208 {
glennrp47b9dd52010-11-24 18:12:06 +00005209 /* Note the delay and frame clipping boundaries. */
5210
cristy3ed852e2009-09-05 21:47:34 +00005211 p++; /* framing mode */
glennrp47b9dd52010-11-24 18:12:06 +00005212
cristybb503372010-05-27 20:51:26 +00005213 while (*p && ((p-chunk) < (ssize_t) length))
cristy3ed852e2009-09-05 21:47:34 +00005214 p++; /* frame name */
glennrp47b9dd52010-11-24 18:12:06 +00005215
cristy3ed852e2009-09-05 21:47:34 +00005216 p++; /* frame name terminator */
glennrp47b9dd52010-11-24 18:12:06 +00005217
cristybb503372010-05-27 20:51:26 +00005218 if ((p-chunk) < (ssize_t) (length-4))
cristy3ed852e2009-09-05 21:47:34 +00005219 {
5220 int
5221 change_delay,
5222 change_timeout,
5223 change_clipping;
5224
5225 change_delay=(*p++);
5226 change_timeout=(*p++);
5227 change_clipping=(*p++);
5228 p++; /* change_sync */
glennrp47b9dd52010-11-24 18:12:06 +00005229
cristy3ed852e2009-09-05 21:47:34 +00005230 if (change_delay)
5231 {
cristy8182b072010-05-30 20:10:53 +00005232 frame_delay=1UL*image->ticks_per_second*
5233 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005234
cristy8182b072010-05-30 20:10:53 +00005235 if (mng_info->ticks_per_second != 0)
5236 frame_delay/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005237
glennrpbb010dd2010-06-01 13:07:15 +00005238 else
5239 frame_delay=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005240
cristy3ed852e2009-09-05 21:47:34 +00005241 if (change_delay == 2)
5242 default_frame_delay=frame_delay;
glennrp0fe50b42010-11-16 03:52:51 +00005243
cristy3ed852e2009-09-05 21:47:34 +00005244 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005245
cristy3ed852e2009-09-05 21:47:34 +00005246 if (logging != MagickFalse)
5247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005248 " Framing_delay=%.20g",(double) frame_delay);
cristy3ed852e2009-09-05 21:47:34 +00005249 }
glennrp47b9dd52010-11-24 18:12:06 +00005250
cristy3ed852e2009-09-05 21:47:34 +00005251 if (change_timeout)
5252 {
glennrpbb010dd2010-06-01 13:07:15 +00005253 frame_timeout=1UL*image->ticks_per_second*
5254 mng_get_long(p);
glennrp0fe50b42010-11-16 03:52:51 +00005255
glennrpbb010dd2010-06-01 13:07:15 +00005256 if (mng_info->ticks_per_second != 0)
5257 frame_timeout/=mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00005258
glennrpbb010dd2010-06-01 13:07:15 +00005259 else
5260 frame_timeout=PNG_UINT_31_MAX;
glennrp0fe50b42010-11-16 03:52:51 +00005261
cristy3ed852e2009-09-05 21:47:34 +00005262 if (change_delay == 2)
5263 default_frame_timeout=frame_timeout;
glennrp0fe50b42010-11-16 03:52:51 +00005264
cristy3ed852e2009-09-05 21:47:34 +00005265 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +00005266
cristy3ed852e2009-09-05 21:47:34 +00005267 if (logging != MagickFalse)
5268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005269 " Framing_timeout=%.20g",(double) frame_timeout);
cristy3ed852e2009-09-05 21:47:34 +00005270 }
glennrp47b9dd52010-11-24 18:12:06 +00005271
cristy3ed852e2009-09-05 21:47:34 +00005272 if (change_clipping)
5273 {
5274 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5275 p+=17;
5276 previous_fb=fb;
glennrp0fe50b42010-11-16 03:52:51 +00005277
cristy3ed852e2009-09-05 21:47:34 +00005278 if (logging != MagickFalse)
5279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005280 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005281 (double) fb.left,(double) fb.right,(double) fb.top,
5282 (double) fb.bottom);
glennrp47b9dd52010-11-24 18:12:06 +00005283
cristy3ed852e2009-09-05 21:47:34 +00005284 if (change_clipping == 2)
5285 default_fb=fb;
5286 }
5287 }
5288 }
5289 mng_info->clip=fb;
5290 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
glennrp0fe50b42010-11-16 03:52:51 +00005291
cristybb503372010-05-27 20:51:26 +00005292 subframe_width=(size_t) (mng_info->clip.right
cristy3ed852e2009-09-05 21:47:34 +00005293 -mng_info->clip.left);
glennrp0fe50b42010-11-16 03:52:51 +00005294
cristybb503372010-05-27 20:51:26 +00005295 subframe_height=(size_t) (mng_info->clip.bottom
cristy3ed852e2009-09-05 21:47:34 +00005296 -mng_info->clip.top);
5297 /*
5298 Insert a background layer behind the frame if framing_mode is 4.
5299 */
5300#if defined(MNG_INSERT_LAYERS)
5301 if (logging != MagickFalse)
5302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005303 " subframe_width=%.20g, subframe_height=%.20g",(double)
5304 subframe_width,(double) subframe_height);
glennrp0fe50b42010-11-16 03:52:51 +00005305
cristy3ed852e2009-09-05 21:47:34 +00005306 if (insert_layers && (mng_info->framing_mode == 4) &&
5307 (subframe_width) && (subframe_height))
5308 {
glennrp47b9dd52010-11-24 18:12:06 +00005309 /* Allocate next image structure. */
cristy4c08aed2011-07-01 19:47:50 +00005310 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005311 {
5312 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005313
cristy3ed852e2009-09-05 21:47:34 +00005314 if (GetNextImageInList(image) == (Image *) NULL)
5315 {
5316 image=DestroyImageList(image);
5317 MngInfoFreeStruct(mng_info,&have_mng_structure);
5318 return((Image *) NULL);
5319 }
glennrp47b9dd52010-11-24 18:12:06 +00005320
cristy3ed852e2009-09-05 21:47:34 +00005321 image=SyncNextImageInList(image);
5322 }
glennrp0fe50b42010-11-16 03:52:51 +00005323
cristy3ed852e2009-09-05 21:47:34 +00005324 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005325
cristy3ed852e2009-09-05 21:47:34 +00005326 if (term_chunk_found)
5327 {
5328 image->start_loop=MagickTrue;
5329 image->iterations=mng_iterations;
5330 term_chunk_found=MagickFalse;
5331 }
glennrp0fe50b42010-11-16 03:52:51 +00005332
cristy3ed852e2009-09-05 21:47:34 +00005333 else
5334 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005335
cristy3ed852e2009-09-05 21:47:34 +00005336 image->columns=subframe_width;
5337 image->rows=subframe_height;
5338 image->page.width=subframe_width;
5339 image->page.height=subframe_height;
5340 image->page.x=mng_info->clip.left;
5341 image->page.y=mng_info->clip.top;
5342 image->background_color=mng_background_color;
5343 image->matte=MagickFalse;
5344 image->delay=0;
5345 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005346
cristy3ed852e2009-09-05 21:47:34 +00005347 if (logging != MagickFalse)
5348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005349 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005350 (double) mng_info->clip.left,(double) mng_info->clip.right,
5351 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005352 }
5353#endif
5354 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5355 continue;
5356 }
5357 if (memcmp(type,mng_CLIP,4) == 0)
5358 {
5359 unsigned int
5360 first_object,
5361 last_object;
5362
5363 /*
5364 Read CLIP.
5365 */
5366 first_object=(p[0] << 8) | p[1];
5367 last_object=(p[2] << 8) | p[3];
glennrp47b9dd52010-11-24 18:12:06 +00005368
cristy3ed852e2009-09-05 21:47:34 +00005369 for (i=(int) first_object; i <= (int) last_object; i++)
5370 {
5371 if (mng_info->exists[i] && !mng_info->frozen[i])
5372 {
5373 MngBox
5374 box;
5375
5376 box=mng_info->object_clip[i];
5377 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5378 }
5379 }
glennrp47b9dd52010-11-24 18:12:06 +00005380
cristy3ed852e2009-09-05 21:47:34 +00005381 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5382 continue;
5383 }
5384 if (memcmp(type,mng_SAVE,4) == 0)
5385 {
5386 for (i=1; i < MNG_MAX_OBJECTS; i++)
5387 if (mng_info->exists[i])
5388 {
5389 mng_info->frozen[i]=MagickTrue;
5390#ifdef MNG_OBJECT_BUFFERS
5391 if (mng_info->ob[i] != (MngBuffer *) NULL)
5392 mng_info->ob[i]->frozen=MagickTrue;
5393#endif
5394 }
glennrp0fe50b42010-11-16 03:52:51 +00005395
cristy3ed852e2009-09-05 21:47:34 +00005396 if (length)
5397 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005398
cristy3ed852e2009-09-05 21:47:34 +00005399 continue;
5400 }
5401
5402 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5403 {
glennrp47b9dd52010-11-24 18:12:06 +00005404 /* Read DISC or SEEK. */
5405
cristy3ed852e2009-09-05 21:47:34 +00005406 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5407 {
5408 for (i=1; i < MNG_MAX_OBJECTS; i++)
5409 MngInfoDiscardObject(mng_info,i);
5410 }
glennrp0fe50b42010-11-16 03:52:51 +00005411
cristy3ed852e2009-09-05 21:47:34 +00005412 else
5413 {
cristybb503372010-05-27 20:51:26 +00005414 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00005415 j;
5416
cristybb503372010-05-27 20:51:26 +00005417 for (j=0; j < (ssize_t) length; j+=2)
cristy3ed852e2009-09-05 21:47:34 +00005418 {
5419 i=p[j] << 8 | p[j+1];
5420 MngInfoDiscardObject(mng_info,i);
5421 }
5422 }
glennrp0fe50b42010-11-16 03:52:51 +00005423
cristy3ed852e2009-09-05 21:47:34 +00005424 if (length)
5425 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp0fe50b42010-11-16 03:52:51 +00005426
cristy3ed852e2009-09-05 21:47:34 +00005427 continue;
5428 }
glennrp47b9dd52010-11-24 18:12:06 +00005429
cristy3ed852e2009-09-05 21:47:34 +00005430 if (memcmp(type,mng_MOVE,4) == 0)
5431 {
cristybb503372010-05-27 20:51:26 +00005432 size_t
cristy3ed852e2009-09-05 21:47:34 +00005433 first_object,
5434 last_object;
5435
glennrp47b9dd52010-11-24 18:12:06 +00005436 /* read MOVE */
5437
cristy3ed852e2009-09-05 21:47:34 +00005438 first_object=(p[0] << 8) | p[1];
5439 last_object=(p[2] << 8) | p[3];
cristybb503372010-05-27 20:51:26 +00005440 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
cristy3ed852e2009-09-05 21:47:34 +00005441 {
5442 if (mng_info->exists[i] && !mng_info->frozen[i])
5443 {
5444 MngPair
5445 new_pair;
5446
5447 MngPair
5448 old_pair;
5449
5450 old_pair.a=mng_info->x_off[i];
5451 old_pair.b=mng_info->y_off[i];
5452 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5453 mng_info->x_off[i]=new_pair.a;
5454 mng_info->y_off[i]=new_pair.b;
5455 }
5456 }
glennrp47b9dd52010-11-24 18:12:06 +00005457
cristy3ed852e2009-09-05 21:47:34 +00005458 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5459 continue;
5460 }
5461
5462 if (memcmp(type,mng_LOOP,4) == 0)
5463 {
cristybb503372010-05-27 20:51:26 +00005464 ssize_t loop_iters=1;
cristy3ed852e2009-09-05 21:47:34 +00005465 loop_level=chunk[0];
5466 mng_info->loop_active[loop_level]=1; /* mark loop active */
glennrp47b9dd52010-11-24 18:12:06 +00005467
5468 /* Record starting point. */
cristy8182b072010-05-30 20:10:53 +00005469 loop_iters=mng_get_long(&chunk[1]);
glennrp0fe50b42010-11-16 03:52:51 +00005470
cristy3ed852e2009-09-05 21:47:34 +00005471 if (logging != MagickFalse)
5472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005473 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5474 (double) loop_iters);
glennrp0fe50b42010-11-16 03:52:51 +00005475
cristy3ed852e2009-09-05 21:47:34 +00005476 if (loop_iters == 0)
5477 skipping_loop=loop_level;
glennrp0fe50b42010-11-16 03:52:51 +00005478
cristy3ed852e2009-09-05 21:47:34 +00005479 else
5480 {
5481 mng_info->loop_jump[loop_level]=TellBlob(image);
5482 mng_info->loop_count[loop_level]=loop_iters;
5483 }
glennrp0fe50b42010-11-16 03:52:51 +00005484
cristy3ed852e2009-09-05 21:47:34 +00005485 mng_info->loop_iteration[loop_level]=0;
5486 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5487 continue;
5488 }
glennrp47b9dd52010-11-24 18:12:06 +00005489
cristy3ed852e2009-09-05 21:47:34 +00005490 if (memcmp(type,mng_ENDL,4) == 0)
5491 {
5492 loop_level=chunk[0];
glennrp47b9dd52010-11-24 18:12:06 +00005493
cristy3ed852e2009-09-05 21:47:34 +00005494 if (skipping_loop > 0)
5495 {
5496 if (skipping_loop == loop_level)
5497 {
5498 /*
5499 Found end of zero-iteration loop.
5500 */
5501 skipping_loop=(-1);
5502 mng_info->loop_active[loop_level]=0;
5503 }
5504 }
glennrp47b9dd52010-11-24 18:12:06 +00005505
cristy3ed852e2009-09-05 21:47:34 +00005506 else
5507 {
5508 if (mng_info->loop_active[loop_level] == 1)
5509 {
5510 mng_info->loop_count[loop_level]--;
5511 mng_info->loop_iteration[loop_level]++;
glennrp0fe50b42010-11-16 03:52:51 +00005512
cristy3ed852e2009-09-05 21:47:34 +00005513 if (logging != MagickFalse)
5514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005515 " ENDL: LOOP level %.20g has %.20g remaining iters ",
cristye8c25f92010-06-03 00:53:06 +00005516 (double) loop_level,(double)
cristyf2faecf2010-05-28 19:19:36 +00005517 mng_info->loop_count[loop_level]);
glennrp47b9dd52010-11-24 18:12:06 +00005518
cristy3ed852e2009-09-05 21:47:34 +00005519 if (mng_info->loop_count[loop_level] != 0)
5520 {
5521 offset=SeekBlob(image,mng_info->loop_jump[loop_level],
5522 SEEK_SET);
glennrp0fe50b42010-11-16 03:52:51 +00005523
cristy3ed852e2009-09-05 21:47:34 +00005524 if (offset < 0)
5525 ThrowReaderException(CorruptImageError,
5526 "ImproperImageHeader");
5527 }
glennrp47b9dd52010-11-24 18:12:06 +00005528
cristy3ed852e2009-09-05 21:47:34 +00005529 else
5530 {
5531 short
5532 last_level;
5533
5534 /*
5535 Finished loop.
5536 */
5537 mng_info->loop_active[loop_level]=0;
5538 last_level=(-1);
5539 for (i=0; i < loop_level; i++)
5540 if (mng_info->loop_active[i] == 1)
5541 last_level=(short) i;
5542 loop_level=last_level;
5543 }
5544 }
5545 }
glennrp47b9dd52010-11-24 18:12:06 +00005546
cristy3ed852e2009-09-05 21:47:34 +00005547 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5548 continue;
5549 }
glennrp47b9dd52010-11-24 18:12:06 +00005550
cristy3ed852e2009-09-05 21:47:34 +00005551 if (memcmp(type,mng_CLON,4) == 0)
5552 {
5553 if (mng_info->clon_warning == 0)
5554 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5555 CoderError,"CLON is not implemented yet","`%s'",
5556 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005557
cristy3ed852e2009-09-05 21:47:34 +00005558 mng_info->clon_warning++;
5559 }
glennrp47b9dd52010-11-24 18:12:06 +00005560
cristy3ed852e2009-09-05 21:47:34 +00005561 if (memcmp(type,mng_MAGN,4) == 0)
5562 {
5563 png_uint_16
5564 magn_first,
5565 magn_last,
5566 magn_mb,
5567 magn_ml,
5568 magn_mr,
5569 magn_mt,
5570 magn_mx,
5571 magn_my,
5572 magn_methx,
5573 magn_methy;
5574
5575 if (length > 1)
5576 magn_first=(p[0] << 8) | p[1];
glennrp0fe50b42010-11-16 03:52:51 +00005577
cristy3ed852e2009-09-05 21:47:34 +00005578 else
5579 magn_first=0;
glennrp0fe50b42010-11-16 03:52:51 +00005580
cristy3ed852e2009-09-05 21:47:34 +00005581 if (length > 3)
5582 magn_last=(p[2] << 8) | p[3];
glennrp0fe50b42010-11-16 03:52:51 +00005583
cristy3ed852e2009-09-05 21:47:34 +00005584 else
5585 magn_last=magn_first;
5586#ifndef MNG_OBJECT_BUFFERS
5587 if (magn_first || magn_last)
5588 if (mng_info->magn_warning == 0)
5589 {
5590 (void) ThrowMagickException(&image->exception,
5591 GetMagickModule(),CoderError,
5592 "MAGN is not implemented yet for nonzero objects",
5593 "`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005594
cristy3ed852e2009-09-05 21:47:34 +00005595 mng_info->magn_warning++;
5596 }
5597#endif
5598 if (length > 4)
5599 magn_methx=p[4];
glennrp47b9dd52010-11-24 18:12:06 +00005600
cristy3ed852e2009-09-05 21:47:34 +00005601 else
5602 magn_methx=0;
5603
5604 if (length > 6)
5605 magn_mx=(p[5] << 8) | p[6];
glennrp47b9dd52010-11-24 18:12:06 +00005606
cristy3ed852e2009-09-05 21:47:34 +00005607 else
5608 magn_mx=1;
glennrp47b9dd52010-11-24 18:12:06 +00005609
cristy3ed852e2009-09-05 21:47:34 +00005610 if (magn_mx == 0)
5611 magn_mx=1;
5612
5613 if (length > 8)
5614 magn_my=(p[7] << 8) | p[8];
glennrp47b9dd52010-11-24 18:12:06 +00005615
cristy3ed852e2009-09-05 21:47:34 +00005616 else
5617 magn_my=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005618
cristy3ed852e2009-09-05 21:47:34 +00005619 if (magn_my == 0)
5620 magn_my=1;
5621
5622 if (length > 10)
5623 magn_ml=(p[9] << 8) | p[10];
glennrp47b9dd52010-11-24 18:12:06 +00005624
cristy3ed852e2009-09-05 21:47:34 +00005625 else
5626 magn_ml=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005627
cristy3ed852e2009-09-05 21:47:34 +00005628 if (magn_ml == 0)
5629 magn_ml=1;
5630
5631 if (length > 12)
5632 magn_mr=(p[11] << 8) | p[12];
glennrp47b9dd52010-11-24 18:12:06 +00005633
cristy3ed852e2009-09-05 21:47:34 +00005634 else
5635 magn_mr=magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00005636
cristy3ed852e2009-09-05 21:47:34 +00005637 if (magn_mr == 0)
5638 magn_mr=1;
5639
5640 if (length > 14)
5641 magn_mt=(p[13] << 8) | p[14];
glennrp47b9dd52010-11-24 18:12:06 +00005642
cristy3ed852e2009-09-05 21:47:34 +00005643 else
5644 magn_mt=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005645
cristy3ed852e2009-09-05 21:47:34 +00005646 if (magn_mt == 0)
5647 magn_mt=1;
5648
5649 if (length > 16)
5650 magn_mb=(p[15] << 8) | p[16];
glennrp47b9dd52010-11-24 18:12:06 +00005651
cristy3ed852e2009-09-05 21:47:34 +00005652 else
5653 magn_mb=magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00005654
cristy3ed852e2009-09-05 21:47:34 +00005655 if (magn_mb == 0)
5656 magn_mb=1;
5657
5658 if (length > 17)
5659 magn_methy=p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005660
cristy3ed852e2009-09-05 21:47:34 +00005661 else
5662 magn_methy=magn_methx;
5663
glennrp47b9dd52010-11-24 18:12:06 +00005664
cristy3ed852e2009-09-05 21:47:34 +00005665 if (magn_methx > 5 || magn_methy > 5)
5666 if (mng_info->magn_warning == 0)
5667 {
5668 (void) ThrowMagickException(&image->exception,
5669 GetMagickModule(),CoderError,
5670 "Unknown MAGN method in MNG datastream","`%s'",
5671 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005672
cristy3ed852e2009-09-05 21:47:34 +00005673 mng_info->magn_warning++;
5674 }
5675#ifdef MNG_OBJECT_BUFFERS
5676 /* Magnify existing objects in the range magn_first to magn_last */
5677#endif
5678 if (magn_first == 0 || magn_last == 0)
5679 {
5680 /* Save the magnification factors for object 0 */
5681 mng_info->magn_mb=magn_mb;
5682 mng_info->magn_ml=magn_ml;
5683 mng_info->magn_mr=magn_mr;
5684 mng_info->magn_mt=magn_mt;
5685 mng_info->magn_mx=magn_mx;
5686 mng_info->magn_my=magn_my;
5687 mng_info->magn_methx=magn_methx;
5688 mng_info->magn_methy=magn_methy;
5689 }
5690 }
glennrp47b9dd52010-11-24 18:12:06 +00005691
cristy3ed852e2009-09-05 21:47:34 +00005692 if (memcmp(type,mng_PAST,4) == 0)
5693 {
5694 if (mng_info->past_warning == 0)
5695 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5696 CoderError,"PAST is not implemented yet","`%s'",
5697 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005698
cristy3ed852e2009-09-05 21:47:34 +00005699 mng_info->past_warning++;
5700 }
glennrp47b9dd52010-11-24 18:12:06 +00005701
cristy3ed852e2009-09-05 21:47:34 +00005702 if (memcmp(type,mng_SHOW,4) == 0)
5703 {
5704 if (mng_info->show_warning == 0)
5705 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5706 CoderError,"SHOW is not implemented yet","`%s'",
5707 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005708
cristy3ed852e2009-09-05 21:47:34 +00005709 mng_info->show_warning++;
5710 }
glennrp47b9dd52010-11-24 18:12:06 +00005711
cristy3ed852e2009-09-05 21:47:34 +00005712 if (memcmp(type,mng_sBIT,4) == 0)
5713 {
5714 if (length < 4)
5715 mng_info->have_global_sbit=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005716
cristy3ed852e2009-09-05 21:47:34 +00005717 else
5718 {
5719 mng_info->global_sbit.gray=p[0];
5720 mng_info->global_sbit.red=p[0];
5721 mng_info->global_sbit.green=p[1];
5722 mng_info->global_sbit.blue=p[2];
5723 mng_info->global_sbit.alpha=p[3];
5724 mng_info->have_global_sbit=MagickTrue;
5725 }
5726 }
5727 if (memcmp(type,mng_pHYs,4) == 0)
5728 {
5729 if (length > 8)
5730 {
5731 mng_info->global_x_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005732 (size_t) mng_get_long(p);
cristy3ed852e2009-09-05 21:47:34 +00005733 mng_info->global_y_pixels_per_unit=
cristy8182b072010-05-30 20:10:53 +00005734 (size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005735 mng_info->global_phys_unit_type=p[8];
5736 mng_info->have_global_phys=MagickTrue;
5737 }
glennrp47b9dd52010-11-24 18:12:06 +00005738
cristy3ed852e2009-09-05 21:47:34 +00005739 else
5740 mng_info->have_global_phys=MagickFalse;
5741 }
5742 if (memcmp(type,mng_pHYg,4) == 0)
5743 {
5744 if (mng_info->phyg_warning == 0)
5745 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5746 CoderError,"pHYg is not implemented.","`%s'",image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005747
cristy3ed852e2009-09-05 21:47:34 +00005748 mng_info->phyg_warning++;
5749 }
5750 if (memcmp(type,mng_BASI,4) == 0)
5751 {
5752 skip_to_iend=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005753
cristy3ed852e2009-09-05 21:47:34 +00005754 if (mng_info->basi_warning == 0)
5755 (void) ThrowMagickException(&image->exception,GetMagickModule(),
5756 CoderError,"BASI is not implemented yet","`%s'",
5757 image->filename);
glennrp47b9dd52010-11-24 18:12:06 +00005758
cristy3ed852e2009-09-05 21:47:34 +00005759 mng_info->basi_warning++;
5760#ifdef MNG_BASI_SUPPORTED
cristybb503372010-05-27 20:51:26 +00005761 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005762 (p[2] << 8) | p[3]);
cristybb503372010-05-27 20:51:26 +00005763 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
cristy3ed852e2009-09-05 21:47:34 +00005764 (p[6] << 8) | p[7]);
5765 basi_color_type=p[8];
5766 basi_compression_method=p[9];
5767 basi_filter_type=p[10];
5768 basi_interlace_method=p[11];
5769 if (length > 11)
5770 basi_red=(p[12] << 8) & p[13];
glennrp47b9dd52010-11-24 18:12:06 +00005771
cristy3ed852e2009-09-05 21:47:34 +00005772 else
5773 basi_red=0;
glennrp47b9dd52010-11-24 18:12:06 +00005774
cristy3ed852e2009-09-05 21:47:34 +00005775 if (length > 13)
5776 basi_green=(p[14] << 8) & p[15];
glennrp47b9dd52010-11-24 18:12:06 +00005777
cristy3ed852e2009-09-05 21:47:34 +00005778 else
5779 basi_green=0;
glennrp47b9dd52010-11-24 18:12:06 +00005780
cristy3ed852e2009-09-05 21:47:34 +00005781 if (length > 15)
5782 basi_blue=(p[16] << 8) & p[17];
glennrp47b9dd52010-11-24 18:12:06 +00005783
cristy3ed852e2009-09-05 21:47:34 +00005784 else
5785 basi_blue=0;
glennrp47b9dd52010-11-24 18:12:06 +00005786
cristy3ed852e2009-09-05 21:47:34 +00005787 if (length > 17)
5788 basi_alpha=(p[18] << 8) & p[19];
glennrp47b9dd52010-11-24 18:12:06 +00005789
cristy3ed852e2009-09-05 21:47:34 +00005790 else
5791 {
5792 if (basi_sample_depth == 16)
5793 basi_alpha=65535L;
5794 else
5795 basi_alpha=255;
5796 }
glennrp47b9dd52010-11-24 18:12:06 +00005797
cristy3ed852e2009-09-05 21:47:34 +00005798 if (length > 19)
5799 basi_viewable=p[20];
glennrp47b9dd52010-11-24 18:12:06 +00005800
cristy3ed852e2009-09-05 21:47:34 +00005801 else
5802 basi_viewable=0;
glennrp47b9dd52010-11-24 18:12:06 +00005803
cristy3ed852e2009-09-05 21:47:34 +00005804#endif
5805 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5806 continue;
5807 }
glennrp47b9dd52010-11-24 18:12:06 +00005808
cristy3ed852e2009-09-05 21:47:34 +00005809 if (memcmp(type,mng_IHDR,4)
5810#if defined(JNG_SUPPORTED)
5811 && memcmp(type,mng_JHDR,4)
5812#endif
5813 )
5814 {
5815 /* Not an IHDR or JHDR chunk */
5816 if (length)
5817 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
glennrp47b9dd52010-11-24 18:12:06 +00005818
cristy3ed852e2009-09-05 21:47:34 +00005819 continue;
5820 }
5821/* Process IHDR */
5822 if (logging != MagickFalse)
5823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5824 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00005825
cristy3ed852e2009-09-05 21:47:34 +00005826 mng_info->exists[object_id]=MagickTrue;
5827 mng_info->viewable[object_id]=MagickTrue;
glennrp47b9dd52010-11-24 18:12:06 +00005828
cristy3ed852e2009-09-05 21:47:34 +00005829 if (mng_info->invisible[object_id])
5830 {
5831 if (logging != MagickFalse)
5832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5833 " Skipping invisible object");
glennrp47b9dd52010-11-24 18:12:06 +00005834
cristy3ed852e2009-09-05 21:47:34 +00005835 skip_to_iend=MagickTrue;
5836 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5837 continue;
5838 }
5839#if defined(MNG_INSERT_LAYERS)
5840 if (length < 8)
5841 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
glennrp47b9dd52010-11-24 18:12:06 +00005842
cristy8182b072010-05-30 20:10:53 +00005843 image_width=(size_t) mng_get_long(p);
5844 image_height=(size_t) mng_get_long(&p[4]);
cristy3ed852e2009-09-05 21:47:34 +00005845#endif
5846 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5847
5848 /*
5849 Insert a transparent background layer behind the entire animation
5850 if it is not full screen.
5851 */
5852#if defined(MNG_INSERT_LAYERS)
5853 if (insert_layers && mng_type && first_mng_object)
5854 {
5855 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
5856 (image_width < mng_info->mng_width) ||
cristybb503372010-05-27 20:51:26 +00005857 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
cristy3ed852e2009-09-05 21:47:34 +00005858 (image_height < mng_info->mng_height) ||
cristybb503372010-05-27 20:51:26 +00005859 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
cristy3ed852e2009-09-05 21:47:34 +00005860 {
cristy4c08aed2011-07-01 19:47:50 +00005861 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005862 {
5863 /*
5864 Allocate next image structure.
5865 */
5866 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005867
cristy3ed852e2009-09-05 21:47:34 +00005868 if (GetNextImageInList(image) == (Image *) NULL)
5869 {
5870 image=DestroyImageList(image);
5871 MngInfoFreeStruct(mng_info,&have_mng_structure);
5872 return((Image *) NULL);
5873 }
glennrp47b9dd52010-11-24 18:12:06 +00005874
cristy3ed852e2009-09-05 21:47:34 +00005875 image=SyncNextImageInList(image);
5876 }
5877 mng_info->image=image;
glennrp47b9dd52010-11-24 18:12:06 +00005878
cristy3ed852e2009-09-05 21:47:34 +00005879 if (term_chunk_found)
5880 {
5881 image->start_loop=MagickTrue;
5882 image->iterations=mng_iterations;
5883 term_chunk_found=MagickFalse;
5884 }
glennrp47b9dd52010-11-24 18:12:06 +00005885
cristy3ed852e2009-09-05 21:47:34 +00005886 else
5887 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005888
5889 /* Make a background rectangle. */
5890
cristy3ed852e2009-09-05 21:47:34 +00005891 image->delay=0;
5892 image->columns=mng_info->mng_width;
5893 image->rows=mng_info->mng_height;
5894 image->page.width=mng_info->mng_width;
5895 image->page.height=mng_info->mng_height;
5896 image->page.x=0;
5897 image->page.y=0;
5898 image->background_color=mng_background_color;
5899 (void) SetImageBackgroundColor(image);
5900 if (logging != MagickFalse)
5901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00005902 " Inserted transparent background layer, W=%.20g, H=%.20g",
5903 (double) mng_info->mng_width,(double) mng_info->mng_height);
cristy3ed852e2009-09-05 21:47:34 +00005904 }
5905 }
5906 /*
5907 Insert a background layer behind the upcoming image if
5908 framing_mode is 3, and we haven't already inserted one.
5909 */
5910 if (insert_layers && (mng_info->framing_mode == 3) &&
5911 (subframe_width) && (subframe_height) && (simplicity == 0 ||
5912 (simplicity & 0x08)))
5913 {
cristy4c08aed2011-07-01 19:47:50 +00005914 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005915 {
5916 /*
5917 Allocate next image structure.
5918 */
5919 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005920
cristy3ed852e2009-09-05 21:47:34 +00005921 if (GetNextImageInList(image) == (Image *) NULL)
5922 {
5923 image=DestroyImageList(image);
5924 MngInfoFreeStruct(mng_info,&have_mng_structure);
5925 return((Image *) NULL);
5926 }
glennrp47b9dd52010-11-24 18:12:06 +00005927
cristy3ed852e2009-09-05 21:47:34 +00005928 image=SyncNextImageInList(image);
5929 }
glennrp0fe50b42010-11-16 03:52:51 +00005930
cristy3ed852e2009-09-05 21:47:34 +00005931 mng_info->image=image;
glennrp0fe50b42010-11-16 03:52:51 +00005932
cristy3ed852e2009-09-05 21:47:34 +00005933 if (term_chunk_found)
5934 {
5935 image->start_loop=MagickTrue;
5936 image->iterations=mng_iterations;
5937 term_chunk_found=MagickFalse;
5938 }
glennrp0fe50b42010-11-16 03:52:51 +00005939
cristy3ed852e2009-09-05 21:47:34 +00005940 else
5941 image->start_loop=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005942
cristy3ed852e2009-09-05 21:47:34 +00005943 image->delay=0;
5944 image->columns=subframe_width;
5945 image->rows=subframe_height;
5946 image->page.width=subframe_width;
5947 image->page.height=subframe_height;
5948 image->page.x=mng_info->clip.left;
5949 image->page.y=mng_info->clip.top;
5950 image->background_color=mng_background_color;
5951 image->matte=MagickFalse;
5952 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00005953
cristy3ed852e2009-09-05 21:47:34 +00005954 if (logging != MagickFalse)
5955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00005956 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
cristye8c25f92010-06-03 00:53:06 +00005957 (double) mng_info->clip.left,(double) mng_info->clip.right,
5958 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
cristy3ed852e2009-09-05 21:47:34 +00005959 }
5960#endif /* MNG_INSERT_LAYERS */
5961 first_mng_object=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00005962
cristy4c08aed2011-07-01 19:47:50 +00005963 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00005964 {
5965 /*
5966 Allocate next image structure.
5967 */
5968 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00005969
cristy3ed852e2009-09-05 21:47:34 +00005970 if (GetNextImageInList(image) == (Image *) NULL)
5971 {
5972 image=DestroyImageList(image);
5973 MngInfoFreeStruct(mng_info,&have_mng_structure);
5974 return((Image *) NULL);
5975 }
glennrp47b9dd52010-11-24 18:12:06 +00005976
cristy3ed852e2009-09-05 21:47:34 +00005977 image=SyncNextImageInList(image);
5978 }
5979 mng_info->image=image;
5980 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
5981 GetBlobSize(image));
glennrp0fe50b42010-11-16 03:52:51 +00005982
cristy3ed852e2009-09-05 21:47:34 +00005983 if (status == MagickFalse)
5984 break;
glennrp0fe50b42010-11-16 03:52:51 +00005985
cristy3ed852e2009-09-05 21:47:34 +00005986 if (term_chunk_found)
5987 {
5988 image->start_loop=MagickTrue;
5989 term_chunk_found=MagickFalse;
5990 }
glennrp0fe50b42010-11-16 03:52:51 +00005991
cristy3ed852e2009-09-05 21:47:34 +00005992 else
5993 image->start_loop=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00005994
cristy3ed852e2009-09-05 21:47:34 +00005995 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
5996 {
5997 image->delay=frame_delay;
5998 frame_delay=default_frame_delay;
5999 }
glennrp0fe50b42010-11-16 03:52:51 +00006000
cristy3ed852e2009-09-05 21:47:34 +00006001 else
6002 image->delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006003
cristy3ed852e2009-09-05 21:47:34 +00006004 image->page.width=mng_info->mng_width;
6005 image->page.height=mng_info->mng_height;
6006 image->page.x=mng_info->x_off[object_id];
6007 image->page.y=mng_info->y_off[object_id];
6008 image->iterations=mng_iterations;
glennrp47b9dd52010-11-24 18:12:06 +00006009
cristy3ed852e2009-09-05 21:47:34 +00006010 /*
6011 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6012 */
glennrp47b9dd52010-11-24 18:12:06 +00006013
cristy3ed852e2009-09-05 21:47:34 +00006014 if (logging != MagickFalse)
6015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6016 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6017 type[2],type[3]);
glennrp47b9dd52010-11-24 18:12:06 +00006018
cristybb503372010-05-27 20:51:26 +00006019 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
glennrp47b9dd52010-11-24 18:12:06 +00006020
cristy3ed852e2009-09-05 21:47:34 +00006021 if (offset < 0)
6022 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6023 }
6024
6025 previous=image;
6026 mng_info->image=image;
6027 mng_info->mng_type=mng_type;
6028 mng_info->object_id=object_id;
6029
6030 if (memcmp(type,mng_IHDR,4) == 0)
6031 image=ReadOnePNGImage(mng_info,image_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006032
cristy3ed852e2009-09-05 21:47:34 +00006033#if defined(JNG_SUPPORTED)
6034 else
6035 image=ReadOneJNGImage(mng_info,image_info,exception);
6036#endif
6037
6038 if (image == (Image *) NULL)
6039 {
6040 if (IsImageObject(previous) != MagickFalse)
6041 {
6042 (void) DestroyImageList(previous);
6043 (void) CloseBlob(previous);
6044 }
glennrp47b9dd52010-11-24 18:12:06 +00006045
cristy3ed852e2009-09-05 21:47:34 +00006046 MngInfoFreeStruct(mng_info,&have_mng_structure);
6047 return((Image *) NULL);
6048 }
glennrp0fe50b42010-11-16 03:52:51 +00006049
cristy3ed852e2009-09-05 21:47:34 +00006050 if (image->columns == 0 || image->rows == 0)
6051 {
6052 (void) CloseBlob(image);
6053 image=DestroyImageList(image);
6054 MngInfoFreeStruct(mng_info,&have_mng_structure);
6055 return((Image *) NULL);
6056 }
glennrp0fe50b42010-11-16 03:52:51 +00006057
cristy3ed852e2009-09-05 21:47:34 +00006058 mng_info->image=image;
6059
6060 if (mng_type)
6061 {
6062 MngBox
6063 crop_box;
6064
6065 if (mng_info->magn_methx || mng_info->magn_methy)
6066 {
6067 png_uint_32
6068 magnified_height,
6069 magnified_width;
6070
6071 if (logging != MagickFalse)
6072 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6073 " Processing MNG MAGN chunk");
6074
6075 if (mng_info->magn_methx == 1)
6076 {
6077 magnified_width=mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006078
cristy3ed852e2009-09-05 21:47:34 +00006079 if (image->columns > 1)
6080 magnified_width += mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006081
cristy3ed852e2009-09-05 21:47:34 +00006082 if (image->columns > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006083 magnified_width += (png_uint_32)
6084 ((image->columns-2)*(mng_info->magn_mx));
cristy3ed852e2009-09-05 21:47:34 +00006085 }
glennrp47b9dd52010-11-24 18:12:06 +00006086
cristy3ed852e2009-09-05 21:47:34 +00006087 else
6088 {
cristy4e5bc842010-06-09 13:56:01 +00006089 magnified_width=(png_uint_32) image->columns;
glennrp47b9dd52010-11-24 18:12:06 +00006090
cristy3ed852e2009-09-05 21:47:34 +00006091 if (image->columns > 1)
6092 magnified_width += mng_info->magn_ml-1;
glennrp47b9dd52010-11-24 18:12:06 +00006093
cristy3ed852e2009-09-05 21:47:34 +00006094 if (image->columns > 2)
6095 magnified_width += mng_info->magn_mr-1;
glennrp47b9dd52010-11-24 18:12:06 +00006096
cristy3ed852e2009-09-05 21:47:34 +00006097 if (image->columns > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006098 magnified_width += (png_uint_32)
6099 ((image->columns-3)*(mng_info->magn_mx-1));
cristy3ed852e2009-09-05 21:47:34 +00006100 }
glennrp47b9dd52010-11-24 18:12:06 +00006101
cristy3ed852e2009-09-05 21:47:34 +00006102 if (mng_info->magn_methy == 1)
6103 {
6104 magnified_height=mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006105
cristy3ed852e2009-09-05 21:47:34 +00006106 if (image->rows > 1)
6107 magnified_height += mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006108
cristy3ed852e2009-09-05 21:47:34 +00006109 if (image->rows > 2)
glennrp47b9dd52010-11-24 18:12:06 +00006110 magnified_height += (png_uint_32)
6111 ((image->rows-2)*(mng_info->magn_my));
cristy3ed852e2009-09-05 21:47:34 +00006112 }
glennrp47b9dd52010-11-24 18:12:06 +00006113
cristy3ed852e2009-09-05 21:47:34 +00006114 else
6115 {
cristy4e5bc842010-06-09 13:56:01 +00006116 magnified_height=(png_uint_32) image->rows;
glennrp47b9dd52010-11-24 18:12:06 +00006117
cristy3ed852e2009-09-05 21:47:34 +00006118 if (image->rows > 1)
6119 magnified_height += mng_info->magn_mt-1;
glennrp47b9dd52010-11-24 18:12:06 +00006120
cristy3ed852e2009-09-05 21:47:34 +00006121 if (image->rows > 2)
6122 magnified_height += mng_info->magn_mb-1;
glennrp47b9dd52010-11-24 18:12:06 +00006123
cristy3ed852e2009-09-05 21:47:34 +00006124 if (image->rows > 3)
glennrp47b9dd52010-11-24 18:12:06 +00006125 magnified_height += (png_uint_32)
6126 ((image->rows-3)*(mng_info->magn_my-1));
cristy3ed852e2009-09-05 21:47:34 +00006127 }
glennrp47b9dd52010-11-24 18:12:06 +00006128
cristy3ed852e2009-09-05 21:47:34 +00006129 if (magnified_height > image->rows ||
6130 magnified_width > image->columns)
6131 {
6132 Image
6133 *large_image;
6134
6135 int
6136 yy;
6137
cristy4c08aed2011-07-01 19:47:50 +00006138 Quantum
cristy3ed852e2009-09-05 21:47:34 +00006139 *next,
6140 *prev;
6141
6142 png_uint_16
6143 magn_methx,
6144 magn_methy;
6145
cristy4c08aed2011-07-01 19:47:50 +00006146 ssize_t
6147 m,
6148 y;
6149
6150 register Quantum
6151 *n,
6152 *q;
6153
6154 register ssize_t
6155 x;
6156
glennrp47b9dd52010-11-24 18:12:06 +00006157 /* Allocate next image structure. */
6158
cristy3ed852e2009-09-05 21:47:34 +00006159 if (logging != MagickFalse)
6160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6161 " Allocate magnified image");
glennrp47b9dd52010-11-24 18:12:06 +00006162
cristy3ed852e2009-09-05 21:47:34 +00006163 AcquireNextImage(image_info,image);
glennrp47b9dd52010-11-24 18:12:06 +00006164
cristy3ed852e2009-09-05 21:47:34 +00006165 if (GetNextImageInList(image) == (Image *) NULL)
6166 {
6167 image=DestroyImageList(image);
6168 MngInfoFreeStruct(mng_info,&have_mng_structure);
6169 return((Image *) NULL);
6170 }
6171
6172 large_image=SyncNextImageInList(image);
6173
6174 large_image->columns=magnified_width;
6175 large_image->rows=magnified_height;
6176
6177 magn_methx=mng_info->magn_methx;
6178 magn_methy=mng_info->magn_methy;
6179
glennrp3faa9a32011-04-23 14:00:25 +00006180#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006181#define QM unsigned short
6182 if (magn_methx != 1 || magn_methy != 1)
6183 {
6184 /*
6185 Scale pixels to unsigned shorts to prevent
6186 overflow of intermediate values of interpolations
6187 */
cristybb503372010-05-27 20:51:26 +00006188 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006189 {
6190 q=GetAuthenticPixels(image,0,y,image->columns,1,
6191 exception);
glennrp47b9dd52010-11-24 18:12:06 +00006192
cristybb503372010-05-27 20:51:26 +00006193 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006194 {
cristy4c08aed2011-07-01 19:47:50 +00006195 SetPixelRed(image,ScaleQuantumToShort(
6196 GetPixelRed(image,q)),q);
6197 SetPixelGreen(image,ScaleQuantumToShort(
6198 GetPixelGreen(image,q)),q);
6199 SetPixelBlue(image,ScaleQuantumToShort(
6200 GetPixelBlue(image,q)),q);
6201 SetPixelAlpha(image,ScaleQuantumToShort(
6202 GetPixelAlpha(image,q)),q);
6203 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006204 }
glennrp47b9dd52010-11-24 18:12:06 +00006205
cristy3ed852e2009-09-05 21:47:34 +00006206 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6207 break;
6208 }
6209 }
6210#else
6211#define QM Quantum
6212#endif
6213
6214 if (image->matte != MagickFalse)
6215 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006216
cristy3ed852e2009-09-05 21:47:34 +00006217 else
6218 {
cristy4c08aed2011-07-01 19:47:50 +00006219 large_image->background_color.alpha=OpaqueAlpha;
cristy3ed852e2009-09-05 21:47:34 +00006220 (void) SetImageBackgroundColor(large_image);
glennrp47b9dd52010-11-24 18:12:06 +00006221
cristy3ed852e2009-09-05 21:47:34 +00006222 if (magn_methx == 4)
6223 magn_methx=2;
glennrp47b9dd52010-11-24 18:12:06 +00006224
cristy3ed852e2009-09-05 21:47:34 +00006225 if (magn_methx == 5)
6226 magn_methx=3;
glennrp47b9dd52010-11-24 18:12:06 +00006227
cristy3ed852e2009-09-05 21:47:34 +00006228 if (magn_methy == 4)
6229 magn_methy=2;
glennrp47b9dd52010-11-24 18:12:06 +00006230
cristy3ed852e2009-09-05 21:47:34 +00006231 if (magn_methy == 5)
6232 magn_methy=3;
6233 }
6234
6235 /* magnify the rows into the right side of the large image */
6236
6237 if (logging != MagickFalse)
6238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006239 " Magnify the rows to %.20g",(double) large_image->rows);
cristybb503372010-05-27 20:51:26 +00006240 m=(ssize_t) mng_info->magn_mt;
cristy3ed852e2009-09-05 21:47:34 +00006241 yy=0;
6242 length=(size_t) image->columns;
cristy4c08aed2011-07-01 19:47:50 +00006243 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6244 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
glennrp47b9dd52010-11-24 18:12:06 +00006245
cristy4c08aed2011-07-01 19:47:50 +00006246 if ((prev == (Quantum *) NULL) ||
6247 (next == (Quantum *) NULL))
cristy3ed852e2009-09-05 21:47:34 +00006248 {
6249 image=DestroyImageList(image);
6250 MngInfoFreeStruct(mng_info,&have_mng_structure);
6251 ThrowReaderException(ResourceLimitError,
6252 "MemoryAllocationFailed");
6253 }
glennrp47b9dd52010-11-24 18:12:06 +00006254
cristy3ed852e2009-09-05 21:47:34 +00006255 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6256 (void) CopyMagickMemory(next,n,length);
glennrp47b9dd52010-11-24 18:12:06 +00006257
cristybb503372010-05-27 20:51:26 +00006258 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006259 {
6260 if (y == 0)
cristybb503372010-05-27 20:51:26 +00006261 m=(ssize_t) mng_info->magn_mt;
glennrp47b9dd52010-11-24 18:12:06 +00006262
cristybb503372010-05-27 20:51:26 +00006263 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6264 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006265
cristybb503372010-05-27 20:51:26 +00006266 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6267 m=(ssize_t) mng_info->magn_mb;
glennrp47b9dd52010-11-24 18:12:06 +00006268
cristybb503372010-05-27 20:51:26 +00006269 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006270 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006271
cristy3ed852e2009-09-05 21:47:34 +00006272 else
cristybb503372010-05-27 20:51:26 +00006273 m=(ssize_t) mng_info->magn_my;
glennrp47b9dd52010-11-24 18:12:06 +00006274
cristy3ed852e2009-09-05 21:47:34 +00006275 n=prev;
6276 prev=next;
6277 next=n;
glennrp47b9dd52010-11-24 18:12:06 +00006278
cristybb503372010-05-27 20:51:26 +00006279 if (y < (ssize_t) image->rows-1)
cristy3ed852e2009-09-05 21:47:34 +00006280 {
6281 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6282 exception);
6283 (void) CopyMagickMemory(next,n,length);
6284 }
glennrp47b9dd52010-11-24 18:12:06 +00006285
cristy3ed852e2009-09-05 21:47:34 +00006286 for (i=0; i < m; i++, yy++)
6287 {
cristy4c08aed2011-07-01 19:47:50 +00006288 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006289 *pixels;
6290
cristybb503372010-05-27 20:51:26 +00006291 assert(yy < (ssize_t) large_image->rows);
cristy3ed852e2009-09-05 21:47:34 +00006292 pixels=prev;
6293 n=next;
6294 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
cristy9fff7b42011-04-29 01:09:31 +00006295 1,exception);
cristy3ed852e2009-09-05 21:47:34 +00006296 q+=(large_image->columns-image->columns);
glennrp47b9dd52010-11-24 18:12:06 +00006297
cristybb503372010-05-27 20:51:26 +00006298 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006299 {
glennrpfd05d622011-02-25 04:10:33 +00006300 /* To do: get color as function of indexes[x] */
cristy3ed852e2009-09-05 21:47:34 +00006301 /*
6302 if (image->storage_class == PseudoClass)
6303 {
6304 }
6305 */
6306
6307 if (magn_methy <= 1)
6308 {
glennrpbb4f99d2011-05-22 11:13:17 +00006309 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006310 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6311 SetPixelGreen(large_image,GetPixelGreen(image,pixels),q);
6312 SetPixelBlue(large_image,GetPixelBlue(image,pixels),q);
6313 SetPixelAlpha(large_image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006314 }
glennrp47b9dd52010-11-24 18:12:06 +00006315
cristy3ed852e2009-09-05 21:47:34 +00006316 else if (magn_methy == 2 || magn_methy == 4)
6317 {
6318 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006319 {
cristy4c08aed2011-07-01 19:47:50 +00006320 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6321 SetPixelGreen(large_image,GetPixelGreen(image,pixels),q);
6322 SetPixelBlue(large_image,GetPixelBlue(image,pixels),q);
6323 SetPixelAlpha(large_image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006324 }
glennrp47b9dd52010-11-24 18:12:06 +00006325
cristy3ed852e2009-09-05 21:47:34 +00006326 else
6327 {
6328 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006329 SetPixelRed(large_image,((QM) (((ssize_t)
6330 (2*i*(GetPixelRed(image,n)
6331 -GetPixelRed(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006332 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006333 +GetPixelRed(image,pixels)))),q);
6334 SetPixelGreen(large_image,((QM) (((ssize_t)
6335 (2*i*(GetPixelGreen(image,n)
6336 -GetPixelGreen(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006337 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006338 +GetPixelGreen(image,pixels)))),q);
6339 SetPixelBlue(large_image,((QM) (((ssize_t)
6340 (2*i*(GetPixelBlue(image,n)
6341 -GetPixelBlue(image,pixels)+m))/
glennrpbb4f99d2011-05-22 11:13:17 +00006342 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006343 +GetPixelBlue(image,pixels)))),q);
glennrp47b9dd52010-11-24 18:12:06 +00006344
cristy3ed852e2009-09-05 21:47:34 +00006345 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006346 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6347 (2*i*(GetPixelAlpha(image,n)
6348 -GetPixelAlpha(image,pixels)+m))
glennrpbb4f99d2011-05-22 11:13:17 +00006349 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006350 GetPixelAlpha(image,pixels)))),q);
cristy3ed852e2009-09-05 21:47:34 +00006351 }
glennrp47b9dd52010-11-24 18:12:06 +00006352
cristy3ed852e2009-09-05 21:47:34 +00006353 if (magn_methy == 4)
6354 {
6355 /* Replicate nearest */
6356 if (i <= ((m+1) << 1))
cristy4c08aed2011-07-01 19:47:50 +00006357 SetPixelAlpha(large_image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006358 else
cristy4c08aed2011-07-01 19:47:50 +00006359 SetPixelAlpha(large_image,GetPixelAlpha(image,n),q);
cristy3ed852e2009-09-05 21:47:34 +00006360 }
6361 }
glennrp47b9dd52010-11-24 18:12:06 +00006362
cristy3ed852e2009-09-05 21:47:34 +00006363 else /* if (magn_methy == 3 || magn_methy == 5) */
6364 {
6365 /* Replicate nearest */
6366 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006367 {
cristy4c08aed2011-07-01 19:47:50 +00006368 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6369 SetPixelGreen(large_image,GetPixelGreen(image,pixels),q);
6370 SetPixelBlue(large_image,GetPixelBlue(image,pixels),q);
6371 SetPixelAlpha(large_image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006372 }
glennrp47b9dd52010-11-24 18:12:06 +00006373
cristy3ed852e2009-09-05 21:47:34 +00006374 else
glennrpbb4f99d2011-05-22 11:13:17 +00006375 {
cristy4c08aed2011-07-01 19:47:50 +00006376 SetPixelRed(large_image,GetPixelRed(image,n),q);
6377 SetPixelGreen(large_image,GetPixelGreen(image,n),q);
6378 SetPixelBlue(large_image,GetPixelBlue(image,n),q);
6379 SetPixelAlpha(large_image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006380 }
glennrp47b9dd52010-11-24 18:12:06 +00006381
cristy3ed852e2009-09-05 21:47:34 +00006382 if (magn_methy == 5)
6383 {
cristy4c08aed2011-07-01 19:47:50 +00006384 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6385 (GetPixelAlpha(image,n)
6386 -GetPixelAlpha(image,pixels))
glennrpbb4f99d2011-05-22 11:13:17 +00006387 +m))/((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006388 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006389 }
6390 }
cristy4c08aed2011-07-01 19:47:50 +00006391 n+=GetPixelChannels(image);
6392 q+=GetPixelChannels(large_image);
6393 pixels+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006394 } /* x */
glennrp47b9dd52010-11-24 18:12:06 +00006395
cristy3ed852e2009-09-05 21:47:34 +00006396 if (SyncAuthenticPixels(large_image,exception) == 0)
6397 break;
glennrp47b9dd52010-11-24 18:12:06 +00006398
cristy3ed852e2009-09-05 21:47:34 +00006399 } /* i */
6400 } /* y */
glennrp47b9dd52010-11-24 18:12:06 +00006401
cristy4c08aed2011-07-01 19:47:50 +00006402 prev=(Quantum *) RelinquishMagickMemory(prev);
6403 next=(Quantum *) RelinquishMagickMemory(next);
cristy3ed852e2009-09-05 21:47:34 +00006404
6405 length=image->columns;
6406
6407 if (logging != MagickFalse)
6408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6409 " Delete original image");
6410
6411 DeleteImageFromList(&image);
6412
6413 image=large_image;
6414
6415 mng_info->image=image;
6416
6417 /* magnify the columns */
6418 if (logging != MagickFalse)
6419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006420 " Magnify the columns to %.20g",(double) image->columns);
cristy3ed852e2009-09-05 21:47:34 +00006421
cristybb503372010-05-27 20:51:26 +00006422 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006423 {
cristy4c08aed2011-07-01 19:47:50 +00006424 register Quantum
cristy3ed852e2009-09-05 21:47:34 +00006425 *pixels;
6426
6427 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00006428 pixels=q+(image->columns-length)*GetPixelChannels(image);
6429 n=pixels+GetPixelChannels(image);
glennrp47b9dd52010-11-24 18:12:06 +00006430
cristybb503372010-05-27 20:51:26 +00006431 for (x=(ssize_t) (image->columns-length);
6432 x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00006433 {
glennrp7c7b3152011-04-26 04:01:27 +00006434 /* To do: Rewrite using Get/Set***PixelComponent() */
6435
cristybb503372010-05-27 20:51:26 +00006436 if (x == (ssize_t) (image->columns-length))
6437 m=(ssize_t) mng_info->magn_ml;
glennrp47b9dd52010-11-24 18:12:06 +00006438
cristybb503372010-05-27 20:51:26 +00006439 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6440 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006441
cristybb503372010-05-27 20:51:26 +00006442 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6443 m=(ssize_t) mng_info->magn_mr;
glennrp47b9dd52010-11-24 18:12:06 +00006444
cristybb503372010-05-27 20:51:26 +00006445 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
cristy3ed852e2009-09-05 21:47:34 +00006446 m=1;
glennrp47b9dd52010-11-24 18:12:06 +00006447
cristy3ed852e2009-09-05 21:47:34 +00006448 else
cristybb503372010-05-27 20:51:26 +00006449 m=(ssize_t) mng_info->magn_mx;
glennrp47b9dd52010-11-24 18:12:06 +00006450
cristy3ed852e2009-09-05 21:47:34 +00006451 for (i=0; i < m; i++)
6452 {
6453 if (magn_methx <= 1)
6454 {
6455 /* replicate previous */
cristy4c08aed2011-07-01 19:47:50 +00006456 SetPixelRed(image,GetPixelRed(image,pixels),q);
6457 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6458 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6459 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
cristy3ed852e2009-09-05 21:47:34 +00006460 }
glennrp47b9dd52010-11-24 18:12:06 +00006461
cristy3ed852e2009-09-05 21:47:34 +00006462 else if (magn_methx == 2 || magn_methx == 4)
6463 {
6464 if (i == 0)
glennrpbb4f99d2011-05-22 11:13:17 +00006465 {
cristy4c08aed2011-07-01 19:47:50 +00006466 SetPixelRed(image,GetPixelRed(image,pixels),q);
6467 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6468 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6469 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006470 }
glennrp47b9dd52010-11-24 18:12:06 +00006471
glennrpbb4f99d2011-05-22 11:13:17 +00006472 /* To do: Rewrite using Get/Set***PixelComponent() */
cristy3ed852e2009-09-05 21:47:34 +00006473 else
6474 {
6475 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006476 SetPixelRed(image,(QM) ((2*i*(
6477 GetPixelRed(image,n)
6478 -GetPixelRed(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006479 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006480 GetPixelRed(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006481
cristy4c08aed2011-07-01 19:47:50 +00006482 SetPixelGreen(image,(QM) ((2*i*(
6483 GetPixelGreen(image,n)
6484 -GetPixelGreen(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006485 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006486 GetPixelGreen(image,pixels)),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006487
cristy4c08aed2011-07-01 19:47:50 +00006488 SetPixelBlue(image,(QM) ((2*i*(
6489 GetPixelBlue(image,n)
6490 -GetPixelBlue(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006491 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006492 GetPixelBlue(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006493 if (image->matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00006494 SetPixelAlpha(image,(QM) ((2*i*(
6495 GetPixelAlpha(image,n)
6496 -GetPixelAlpha(image,pixels))+m)
glennrpbb4f99d2011-05-22 11:13:17 +00006497 /((ssize_t) (m*2))+
cristy4c08aed2011-07-01 19:47:50 +00006498 GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006499 }
glennrp47b9dd52010-11-24 18:12:06 +00006500
cristy3ed852e2009-09-05 21:47:34 +00006501 if (magn_methx == 4)
6502 {
6503 /* Replicate nearest */
6504 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006505 {
cristy4c08aed2011-07-01 19:47:50 +00006506 SetPixelAlpha(image,
6507 GetPixelAlpha(image,pixels)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006508 }
cristy3ed852e2009-09-05 21:47:34 +00006509 else
glennrpbb4f99d2011-05-22 11:13:17 +00006510 {
cristy4c08aed2011-07-01 19:47:50 +00006511 SetPixelAlpha(image,
6512 GetPixelAlpha(image,n)+0,q);
glennrpbb4f99d2011-05-22 11:13:17 +00006513 }
cristy3ed852e2009-09-05 21:47:34 +00006514 }
6515 }
glennrp47b9dd52010-11-24 18:12:06 +00006516
cristy3ed852e2009-09-05 21:47:34 +00006517 else /* if (magn_methx == 3 || magn_methx == 5) */
6518 {
6519 /* Replicate nearest */
6520 if (i <= ((m+1) << 1))
glennrpbb4f99d2011-05-22 11:13:17 +00006521 {
cristy4c08aed2011-07-01 19:47:50 +00006522 SetPixelRed(image,GetPixelRed(image,pixels),q);
6523 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6524 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6525 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006526 }
glennrp47b9dd52010-11-24 18:12:06 +00006527
cristy3ed852e2009-09-05 21:47:34 +00006528 else
glennrpbb4f99d2011-05-22 11:13:17 +00006529 {
cristy4c08aed2011-07-01 19:47:50 +00006530 SetPixelRed(image,GetPixelRed(image,n),q);
6531 SetPixelGreen(image,GetPixelGreen(image,n),q);
6532 SetPixelBlue(image,GetPixelBlue(image,n),q);
6533 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
glennrpbb4f99d2011-05-22 11:13:17 +00006534 }
glennrp47b9dd52010-11-24 18:12:06 +00006535
cristy3ed852e2009-09-05 21:47:34 +00006536 if (magn_methx == 5)
6537 {
6538 /* Interpolate */
cristy4c08aed2011-07-01 19:47:50 +00006539 SetPixelAlpha(image,
6540 (QM) ((2*i*( GetPixelAlpha(image,n)
6541 -GetPixelAlpha(image,pixels))+m)/
glennrpbb4f99d2011-05-22 11:13:17 +00006542 ((ssize_t) (m*2))
cristy4c08aed2011-07-01 19:47:50 +00006543 +GetPixelAlpha(image,pixels)),q);
cristy3ed852e2009-09-05 21:47:34 +00006544 }
6545 }
cristy4c08aed2011-07-01 19:47:50 +00006546 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006547 }
cristy4c08aed2011-07-01 19:47:50 +00006548 n+=GetPixelChannels(image);
6549 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006550 }
glennrp47b9dd52010-11-24 18:12:06 +00006551
cristy3ed852e2009-09-05 21:47:34 +00006552 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6553 break;
6554 }
glennrp3faa9a32011-04-23 14:00:25 +00006555#if (MAGICKCORE_QUANTUM_DEPTH > 16)
cristy3ed852e2009-09-05 21:47:34 +00006556 if (magn_methx != 1 || magn_methy != 1)
6557 {
6558 /*
6559 Rescale pixels to Quantum
6560 */
cristybb503372010-05-27 20:51:26 +00006561 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00006562 {
6563 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006564
cristybb503372010-05-27 20:51:26 +00006565 for (x=(ssize_t) image->columns-1; x >= 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00006566 {
cristy4c08aed2011-07-01 19:47:50 +00006567 SetPixelRed(image,ScaleShortToQuantum(
6568 GetPixelRed(image,q)),q);
6569 SetPixelGreen(image,ScaleShortToQuantum(
6570 GetPixelGreen(image,q)),q);
6571 SetPixelBlue(image,ScaleShortToQuantum(
6572 GetPixelBlue(image,q)),q);
6573 SetPixelAlpha(image,ScaleShortToQuantum(
6574 GetPixelAlpha(image,q)),q);
6575 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00006576 }
glennrp47b9dd52010-11-24 18:12:06 +00006577
cristy3ed852e2009-09-05 21:47:34 +00006578 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6579 break;
6580 }
6581 }
6582#endif
6583 if (logging != MagickFalse)
6584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6585 " Finished MAGN processing");
6586 }
6587 }
6588
6589 /*
6590 Crop_box is with respect to the upper left corner of the MNG.
6591 */
6592 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6593 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6594 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6595 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6596 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6597 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6598 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6599 if ((crop_box.left != (mng_info->image_box.left
6600 +mng_info->x_off[object_id])) ||
6601 (crop_box.right != (mng_info->image_box.right
6602 +mng_info->x_off[object_id])) ||
6603 (crop_box.top != (mng_info->image_box.top
6604 +mng_info->y_off[object_id])) ||
6605 (crop_box.bottom != (mng_info->image_box.bottom
6606 +mng_info->y_off[object_id])))
6607 {
6608 if (logging != MagickFalse)
6609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6610 " Crop the PNG image");
glennrp47b9dd52010-11-24 18:12:06 +00006611
cristy3ed852e2009-09-05 21:47:34 +00006612 if ((crop_box.left < crop_box.right) &&
6613 (crop_box.top < crop_box.bottom))
6614 {
6615 Image
6616 *im;
6617
6618 RectangleInfo
6619 crop_info;
6620
6621 /*
6622 Crop_info is with respect to the upper left corner of
6623 the image.
6624 */
6625 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6626 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
cristybb503372010-05-27 20:51:26 +00006627 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6628 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
cristy3ed852e2009-09-05 21:47:34 +00006629 image->page.width=image->columns;
6630 image->page.height=image->rows;
6631 image->page.x=0;
6632 image->page.y=0;
6633 im=CropImage(image,&crop_info,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006634
cristy3ed852e2009-09-05 21:47:34 +00006635 if (im != (Image *) NULL)
6636 {
6637 image->columns=im->columns;
6638 image->rows=im->rows;
6639 im=DestroyImage(im);
6640 image->page.width=image->columns;
6641 image->page.height=image->rows;
6642 image->page.x=crop_box.left;
6643 image->page.y=crop_box.top;
6644 }
6645 }
glennrp47b9dd52010-11-24 18:12:06 +00006646
cristy3ed852e2009-09-05 21:47:34 +00006647 else
6648 {
6649 /*
6650 No pixels in crop area. The MNG spec still requires
6651 a layer, though, so make a single transparent pixel in
6652 the top left corner.
6653 */
6654 image->columns=1;
6655 image->rows=1;
6656 image->colors=2;
6657 (void) SetImageBackgroundColor(image);
6658 image->page.width=1;
6659 image->page.height=1;
6660 image->page.x=0;
6661 image->page.y=0;
6662 }
6663 }
6664#ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
6665 image=mng_info->image;
6666#endif
6667 }
6668
glennrp2b013e42010-11-24 16:55:50 +00006669#if (MAGICKCORE_QUANTUM_DEPTH > 16)
6670 /* PNG does not handle depths greater than 16 so reduce it even
6671 * if lossy
6672 */
6673 if (image->depth > 16)
6674 image->depth=16;
6675#endif
6676
glennrp3faa9a32011-04-23 14:00:25 +00006677#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrp8640fb52010-11-23 15:48:26 +00006678 if (LosslessReduceDepthOK(image) != MagickFalse)
6679 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +00006680#endif
glennrpd6afd542010-11-19 01:53:05 +00006681
cristy3ed852e2009-09-05 21:47:34 +00006682 GetImageException(image,exception);
glennrp47b9dd52010-11-24 18:12:06 +00006683
cristy3ed852e2009-09-05 21:47:34 +00006684 if (image_info->number_scenes != 0)
6685 {
6686 if (mng_info->scenes_found >
cristybb503372010-05-27 20:51:26 +00006687 (ssize_t) (image_info->first_scene+image_info->number_scenes))
cristy3ed852e2009-09-05 21:47:34 +00006688 break;
6689 }
glennrpd6afd542010-11-19 01:53:05 +00006690
cristy3ed852e2009-09-05 21:47:34 +00006691 if (logging != MagickFalse)
6692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6693 " Finished reading image datastream.");
glennrpd6afd542010-11-19 01:53:05 +00006694
cristy3ed852e2009-09-05 21:47:34 +00006695 } while (LocaleCompare(image_info->magick,"MNG") == 0);
glennrp47b9dd52010-11-24 18:12:06 +00006696
cristy3ed852e2009-09-05 21:47:34 +00006697 (void) CloseBlob(image);
glennrp47b9dd52010-11-24 18:12:06 +00006698
cristy3ed852e2009-09-05 21:47:34 +00006699 if (logging != MagickFalse)
6700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6701 " Finished reading all image datastreams.");
glennrp47b9dd52010-11-24 18:12:06 +00006702
cristy3ed852e2009-09-05 21:47:34 +00006703#if defined(MNG_INSERT_LAYERS)
6704 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
6705 (mng_info->mng_height))
6706 {
6707 /*
6708 Insert a background layer if nothing else was found.
6709 */
6710 if (logging != MagickFalse)
6711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6712 " No images found. Inserting a background layer.");
glennrp0fe50b42010-11-16 03:52:51 +00006713
cristy4c08aed2011-07-01 19:47:50 +00006714 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00006715 {
6716 /*
6717 Allocate next image structure.
6718 */
6719 AcquireNextImage(image_info,image);
6720 if (GetNextImageInList(image) == (Image *) NULL)
6721 {
6722 image=DestroyImageList(image);
6723 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp47b9dd52010-11-24 18:12:06 +00006724
cristy3ed852e2009-09-05 21:47:34 +00006725 if (logging != MagickFalse)
6726 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6727 " Allocation failed, returning NULL.");
glennrp47b9dd52010-11-24 18:12:06 +00006728
cristy3ed852e2009-09-05 21:47:34 +00006729 return((Image *) NULL);
6730 }
6731 image=SyncNextImageInList(image);
6732 }
6733 image->columns=mng_info->mng_width;
6734 image->rows=mng_info->mng_height;
6735 image->page.width=mng_info->mng_width;
6736 image->page.height=mng_info->mng_height;
6737 image->page.x=0;
6738 image->page.y=0;
6739 image->background_color=mng_background_color;
6740 image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00006741
cristy3ed852e2009-09-05 21:47:34 +00006742 if (image_info->ping == MagickFalse)
6743 (void) SetImageBackgroundColor(image);
glennrp0fe50b42010-11-16 03:52:51 +00006744
cristy3ed852e2009-09-05 21:47:34 +00006745 mng_info->image_found++;
6746 }
6747#endif
6748 image->iterations=mng_iterations;
glennrp0fe50b42010-11-16 03:52:51 +00006749
cristy3ed852e2009-09-05 21:47:34 +00006750 if (mng_iterations == 1)
6751 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006752
cristy3ed852e2009-09-05 21:47:34 +00006753 while (GetPreviousImageInList(image) != (Image *) NULL)
6754 {
6755 image_count++;
6756 if (image_count > 10*mng_info->image_found)
6757 {
6758 if (logging != MagickFalse)
6759 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
glennrp47b9dd52010-11-24 18:12:06 +00006760
cristy3ed852e2009-09-05 21:47:34 +00006761 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6762 CoderError,"Linked list is corrupted, beginning of list not found",
6763 "`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006764
cristy3ed852e2009-09-05 21:47:34 +00006765 return((Image *) NULL);
6766 }
glennrp0fe50b42010-11-16 03:52:51 +00006767
cristy3ed852e2009-09-05 21:47:34 +00006768 image=GetPreviousImageInList(image);
glennrp0fe50b42010-11-16 03:52:51 +00006769
cristy3ed852e2009-09-05 21:47:34 +00006770 if (GetNextImageInList(image) == (Image *) NULL)
6771 {
6772 if (logging != MagickFalse)
6773 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
glennrp47b9dd52010-11-24 18:12:06 +00006774
cristy3ed852e2009-09-05 21:47:34 +00006775 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6776 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
6777 image_info->filename);
6778 }
6779 }
glennrp47b9dd52010-11-24 18:12:06 +00006780
cristy3ed852e2009-09-05 21:47:34 +00006781 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
6782 GetNextImageInList(image) ==
6783 (Image *) NULL)
6784 {
6785 if (logging != MagickFalse)
6786 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6787 " First image null");
glennrp47b9dd52010-11-24 18:12:06 +00006788
cristy3ed852e2009-09-05 21:47:34 +00006789 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6790 CoderError,"image->next for first image is NULL but shouldn't be.",
6791 "`%s'",image_info->filename);
6792 }
glennrp47b9dd52010-11-24 18:12:06 +00006793
cristy3ed852e2009-09-05 21:47:34 +00006794 if (mng_info->image_found == 0)
6795 {
6796 if (logging != MagickFalse)
6797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6798 " No visible images found.");
glennrp47b9dd52010-11-24 18:12:06 +00006799
cristy3ed852e2009-09-05 21:47:34 +00006800 (void) ThrowMagickException(&image->exception,GetMagickModule(),
6801 CoderError,"No visible images in file","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006802
cristy3ed852e2009-09-05 21:47:34 +00006803 if (image != (Image *) NULL)
6804 image=DestroyImageList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006805
cristy3ed852e2009-09-05 21:47:34 +00006806 MngInfoFreeStruct(mng_info,&have_mng_structure);
6807 return((Image *) NULL);
6808 }
6809
6810 if (mng_info->ticks_per_second)
6811 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
6812 final_delay/mng_info->ticks_per_second;
glennrp0fe50b42010-11-16 03:52:51 +00006813
cristy3ed852e2009-09-05 21:47:34 +00006814 else
6815 image->start_loop=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +00006816
cristy3ed852e2009-09-05 21:47:34 +00006817 /* Find final nonzero image delay */
6818 final_image_delay=0;
glennrp0fe50b42010-11-16 03:52:51 +00006819
cristy3ed852e2009-09-05 21:47:34 +00006820 while (GetNextImageInList(image) != (Image *) NULL)
6821 {
6822 if (image->delay)
6823 final_image_delay=image->delay;
glennrp47b9dd52010-11-24 18:12:06 +00006824
cristy3ed852e2009-09-05 21:47:34 +00006825 image=GetNextImageInList(image);
6826 }
glennrp0fe50b42010-11-16 03:52:51 +00006827
cristy3ed852e2009-09-05 21:47:34 +00006828 if (final_delay < final_image_delay)
6829 final_delay=final_image_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006830
cristy3ed852e2009-09-05 21:47:34 +00006831 image->delay=final_delay;
glennrp0fe50b42010-11-16 03:52:51 +00006832
cristy3ed852e2009-09-05 21:47:34 +00006833 if (logging != MagickFalse)
6834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006835 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
6836 (double) final_delay);
glennrp0fe50b42010-11-16 03:52:51 +00006837
cristy3ed852e2009-09-05 21:47:34 +00006838 if (logging != MagickFalse)
6839 {
6840 int
6841 scene;
6842
6843 scene=0;
6844 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006845
cristy3ed852e2009-09-05 21:47:34 +00006846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6847 " Before coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006848
cristy3ed852e2009-09-05 21:47:34 +00006849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006850 " scene 0 delay=%.20g",(double) image->delay);
glennrp47b9dd52010-11-24 18:12:06 +00006851
cristy3ed852e2009-09-05 21:47:34 +00006852 while (GetNextImageInList(image) != (Image *) NULL)
6853 {
6854 image=GetNextImageInList(image);
6855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006856 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
cristy3ed852e2009-09-05 21:47:34 +00006857 }
6858 }
6859
6860 image=GetFirstImageInList(image);
6861#ifdef MNG_COALESCE_LAYERS
6862 if (insert_layers)
6863 {
6864 Image
6865 *next_image,
6866 *next;
6867
cristybb503372010-05-27 20:51:26 +00006868 size_t
cristy3ed852e2009-09-05 21:47:34 +00006869 scene;
6870
6871 if (logging != MagickFalse)
6872 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
glennrp47b9dd52010-11-24 18:12:06 +00006873
cristy3ed852e2009-09-05 21:47:34 +00006874 scene=image->scene;
6875 next_image=CoalesceImages(image,&image->exception);
glennrp47b9dd52010-11-24 18:12:06 +00006876
cristy3ed852e2009-09-05 21:47:34 +00006877 if (next_image == (Image *) NULL)
6878 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
glennrp47b9dd52010-11-24 18:12:06 +00006879
cristy3ed852e2009-09-05 21:47:34 +00006880 image=DestroyImageList(image);
6881 image=next_image;
glennrp47b9dd52010-11-24 18:12:06 +00006882
cristy3ed852e2009-09-05 21:47:34 +00006883 for (next=image; next != (Image *) NULL; next=next_image)
6884 {
6885 next->page.width=mng_info->mng_width;
6886 next->page.height=mng_info->mng_height;
6887 next->page.x=0;
6888 next->page.y=0;
6889 next->scene=scene++;
6890 next_image=GetNextImageInList(next);
glennrp47b9dd52010-11-24 18:12:06 +00006891
cristy3ed852e2009-09-05 21:47:34 +00006892 if (next_image == (Image *) NULL)
6893 break;
glennrp47b9dd52010-11-24 18:12:06 +00006894
cristy3ed852e2009-09-05 21:47:34 +00006895 if (next->delay == 0)
6896 {
6897 scene--;
6898 next_image->previous=GetPreviousImageInList(next);
6899 if (GetPreviousImageInList(next) == (Image *) NULL)
6900 image=next_image;
6901 else
6902 next->previous->next=next_image;
6903 next=DestroyImage(next);
6904 }
6905 }
6906 }
6907#endif
6908
6909 while (GetNextImageInList(image) != (Image *) NULL)
6910 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006911
cristy3ed852e2009-09-05 21:47:34 +00006912 image->dispose=BackgroundDispose;
6913
6914 if (logging != MagickFalse)
6915 {
6916 int
6917 scene;
6918
6919 scene=0;
6920 image=GetFirstImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006921
cristy3ed852e2009-09-05 21:47:34 +00006922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6923 " After coalesce:");
glennrp47b9dd52010-11-24 18:12:06 +00006924
cristy3ed852e2009-09-05 21:47:34 +00006925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006926 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
6927 (double) image->dispose);
glennrp47b9dd52010-11-24 18:12:06 +00006928
cristy3ed852e2009-09-05 21:47:34 +00006929 while (GetNextImageInList(image) != (Image *) NULL)
cristyf2faecf2010-05-28 19:19:36 +00006930 {
6931 image=GetNextImageInList(image);
glennrp47b9dd52010-11-24 18:12:06 +00006932
cristyf2faecf2010-05-28 19:19:36 +00006933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00006934 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
6935 (double) image->delay,(double) image->dispose);
cristyf2faecf2010-05-28 19:19:36 +00006936 }
6937 }
glennrp47b9dd52010-11-24 18:12:06 +00006938
cristy3ed852e2009-09-05 21:47:34 +00006939 image=GetFirstImageInList(image);
6940 MngInfoFreeStruct(mng_info,&have_mng_structure);
6941 have_mng_structure=MagickFalse;
glennrp47b9dd52010-11-24 18:12:06 +00006942
cristy3ed852e2009-09-05 21:47:34 +00006943 if (logging != MagickFalse)
6944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
glennrp47b9dd52010-11-24 18:12:06 +00006945
cristy3ed852e2009-09-05 21:47:34 +00006946 return(GetFirstImageInList(image));
6947}
glennrp25c1e2b2010-03-25 01:39:56 +00006948#else /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006949static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6950{
6951 printf("Your PNG library is too old: You have libpng-%s\n",
6952 PNG_LIBPNG_VER_STRING);
glennrp47b9dd52010-11-24 18:12:06 +00006953
cristy3ed852e2009-09-05 21:47:34 +00006954 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
6955 "PNG library is too old","`%s'",image_info->filename);
glennrp47b9dd52010-11-24 18:12:06 +00006956
cristy3ed852e2009-09-05 21:47:34 +00006957 return(Image *) NULL;
6958}
glennrp47b9dd52010-11-24 18:12:06 +00006959
cristy3ed852e2009-09-05 21:47:34 +00006960static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
6961{
6962 return(ReadPNGImage(image_info,exception));
6963}
glennrp25c1e2b2010-03-25 01:39:56 +00006964#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +00006965#endif
6966
6967/*
6968%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6969% %
6970% %
6971% %
6972% R e g i s t e r P N G I m a g e %
6973% %
6974% %
6975% %
6976%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6977%
6978% RegisterPNGImage() adds properties for the PNG image format to
6979% the list of supported formats. The properties include the image format
6980% tag, a method to read and/or write the format, whether the format
6981% supports the saving of more than one frame to the same file or blob,
6982% whether the format supports native in-memory I/O, and a brief
6983% description of the format.
6984%
6985% The format of the RegisterPNGImage method is:
6986%
cristybb503372010-05-27 20:51:26 +00006987% size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006988%
6989*/
cristybb503372010-05-27 20:51:26 +00006990ModuleExport size_t RegisterPNGImage(void)
cristy3ed852e2009-09-05 21:47:34 +00006991{
6992 char
6993 version[MaxTextExtent];
6994
6995 MagickInfo
6996 *entry;
6997
6998 static const char
6999 *PNGNote=
7000 {
7001 "See http://www.libpng.org/ for details about the PNG format."
7002 },
glennrp47b9dd52010-11-24 18:12:06 +00007003
cristy3ed852e2009-09-05 21:47:34 +00007004 *JNGNote=
7005 {
7006 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7007 "format."
7008 },
glennrp47b9dd52010-11-24 18:12:06 +00007009
cristy3ed852e2009-09-05 21:47:34 +00007010 *MNGNote=
7011 {
7012 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7013 "format."
7014 };
7015
7016 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007017
cristy3ed852e2009-09-05 21:47:34 +00007018#if defined(PNG_LIBPNG_VER_STRING)
7019 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7020 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007021
cristy3ed852e2009-09-05 21:47:34 +00007022 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7023 {
7024 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7025 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7026 MaxTextExtent);
7027 }
7028#endif
glennrp47b9dd52010-11-24 18:12:06 +00007029
cristy3ed852e2009-09-05 21:47:34 +00007030 entry=SetMagickInfo("MNG");
7031 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
glennrp47b9dd52010-11-24 18:12:06 +00007032
cristy3ed852e2009-09-05 21:47:34 +00007033#if defined(MAGICKCORE_PNG_DELEGATE)
7034 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7035 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7036#endif
glennrp47b9dd52010-11-24 18:12:06 +00007037
cristy3ed852e2009-09-05 21:47:34 +00007038 entry->magick=(IsImageFormatHandler *) IsMNG;
7039 entry->description=ConstantString("Multiple-image Network Graphics");
glennrp47b9dd52010-11-24 18:12:06 +00007040
cristy3ed852e2009-09-05 21:47:34 +00007041 if (*version != '\0')
7042 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007043
cristy3ed852e2009-09-05 21:47:34 +00007044 entry->module=ConstantString("PNG");
7045 entry->note=ConstantString(MNGNote);
7046 (void) RegisterMagickInfo(entry);
7047
7048 entry=SetMagickInfo("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007049
cristy3ed852e2009-09-05 21:47:34 +00007050#if defined(MAGICKCORE_PNG_DELEGATE)
7051 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7052 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7053#endif
glennrp47b9dd52010-11-24 18:12:06 +00007054
cristy3ed852e2009-09-05 21:47:34 +00007055 entry->magick=(IsImageFormatHandler *) IsPNG;
7056 entry->adjoin=MagickFalse;
7057 entry->description=ConstantString("Portable Network Graphics");
7058 entry->module=ConstantString("PNG");
glennrp47b9dd52010-11-24 18:12:06 +00007059
cristy3ed852e2009-09-05 21:47:34 +00007060 if (*version != '\0')
7061 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007062
cristy3ed852e2009-09-05 21:47:34 +00007063 entry->note=ConstantString(PNGNote);
7064 (void) RegisterMagickInfo(entry);
7065
7066 entry=SetMagickInfo("PNG8");
glennrp47b9dd52010-11-24 18:12:06 +00007067
cristy3ed852e2009-09-05 21:47:34 +00007068#if defined(MAGICKCORE_PNG_DELEGATE)
7069 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7070 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7071#endif
glennrp47b9dd52010-11-24 18:12:06 +00007072
cristy3ed852e2009-09-05 21:47:34 +00007073 entry->magick=(IsImageFormatHandler *) IsPNG;
7074 entry->adjoin=MagickFalse;
7075 entry->description=ConstantString(
7076 "8-bit indexed with optional binary transparency");
7077 entry->module=ConstantString("PNG");
7078 (void) RegisterMagickInfo(entry);
7079
7080 entry=SetMagickInfo("PNG24");
7081 *version='\0';
glennrp47b9dd52010-11-24 18:12:06 +00007082
cristy3ed852e2009-09-05 21:47:34 +00007083#if defined(ZLIB_VERSION)
7084 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7085 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
glennrp47b9dd52010-11-24 18:12:06 +00007086
cristy3ed852e2009-09-05 21:47:34 +00007087 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7088 {
7089 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7090 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7091 }
7092#endif
glennrp47b9dd52010-11-24 18:12:06 +00007093
cristy3ed852e2009-09-05 21:47:34 +00007094 if (*version != '\0')
7095 entry->version=ConstantString(version);
glennrp47b9dd52010-11-24 18:12:06 +00007096
cristy3ed852e2009-09-05 21:47:34 +00007097#if defined(MAGICKCORE_PNG_DELEGATE)
7098 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7099 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7100#endif
glennrp47b9dd52010-11-24 18:12:06 +00007101
cristy3ed852e2009-09-05 21:47:34 +00007102 entry->magick=(IsImageFormatHandler *) IsPNG;
7103 entry->adjoin=MagickFalse;
7104 entry->description=ConstantString("opaque 24-bit RGB");
7105 entry->module=ConstantString("PNG");
7106 (void) RegisterMagickInfo(entry);
7107
7108 entry=SetMagickInfo("PNG32");
glennrp47b9dd52010-11-24 18:12:06 +00007109
cristy3ed852e2009-09-05 21:47:34 +00007110#if defined(MAGICKCORE_PNG_DELEGATE)
7111 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7112 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7113#endif
glennrp47b9dd52010-11-24 18:12:06 +00007114
cristy3ed852e2009-09-05 21:47:34 +00007115 entry->magick=(IsImageFormatHandler *) IsPNG;
7116 entry->adjoin=MagickFalse;
7117 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7118 entry->module=ConstantString("PNG");
7119 (void) RegisterMagickInfo(entry);
7120
7121 entry=SetMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007122
cristy3ed852e2009-09-05 21:47:34 +00007123#if defined(JNG_SUPPORTED)
7124#if defined(MAGICKCORE_PNG_DELEGATE)
7125 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7126 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7127#endif
7128#endif
glennrp47b9dd52010-11-24 18:12:06 +00007129
cristy3ed852e2009-09-05 21:47:34 +00007130 entry->magick=(IsImageFormatHandler *) IsJNG;
7131 entry->adjoin=MagickFalse;
7132 entry->description=ConstantString("JPEG Network Graphics");
7133 entry->module=ConstantString("PNG");
7134 entry->note=ConstantString(JNGNote);
7135 (void) RegisterMagickInfo(entry);
glennrp47b9dd52010-11-24 18:12:06 +00007136
cristy18b17442009-10-25 18:36:48 +00007137#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007138 ping_semaphore=AllocateSemaphoreInfo();
cristy18b17442009-10-25 18:36:48 +00007139#endif
glennrp47b9dd52010-11-24 18:12:06 +00007140
cristy3ed852e2009-09-05 21:47:34 +00007141 return(MagickImageCoderSignature);
7142}
7143
7144/*
7145%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7146% %
7147% %
7148% %
7149% U n r e g i s t e r P N G I m a g e %
7150% %
7151% %
7152% %
7153%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7154%
7155% UnregisterPNGImage() removes format registrations made by the
7156% PNG module from the list of supported formats.
7157%
7158% The format of the UnregisterPNGImage method is:
7159%
7160% UnregisterPNGImage(void)
7161%
7162*/
7163ModuleExport void UnregisterPNGImage(void)
7164{
7165 (void) UnregisterMagickInfo("MNG");
7166 (void) UnregisterMagickInfo("PNG");
7167 (void) UnregisterMagickInfo("PNG8");
7168 (void) UnregisterMagickInfo("PNG24");
7169 (void) UnregisterMagickInfo("PNG32");
7170 (void) UnregisterMagickInfo("JNG");
glennrp47b9dd52010-11-24 18:12:06 +00007171
cristy3ed852e2009-09-05 21:47:34 +00007172#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007173 if (ping_semaphore != (SemaphoreInfo *) NULL)
7174 DestroySemaphoreInfo(&ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007175#endif
7176}
7177
7178#if defined(MAGICKCORE_PNG_DELEGATE)
glennrp25c1e2b2010-03-25 01:39:56 +00007179#if PNG_LIBPNG_VER > 10011
cristy3ed852e2009-09-05 21:47:34 +00007180/*
7181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7182% %
7183% %
7184% %
7185% W r i t e M N G I m a g e %
7186% %
7187% %
7188% %
7189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7190%
7191% WriteMNGImage() writes an image in the Portable Network Graphics
7192% Group's "Multiple-image Network Graphics" encoded image format.
7193%
7194% MNG support written by Glenn Randers-Pehrson, glennrp@image...
7195%
7196% The format of the WriteMNGImage method is:
7197%
7198% MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
7199%
7200% A description of each parameter follows.
7201%
7202% o image_info: the image info.
7203%
7204% o image: The image.
7205%
7206%
7207% To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7208% "To do" under ReadPNGImage):
7209%
cristy3ed852e2009-09-05 21:47:34 +00007210% Preserve all unknown and not-yet-handled known chunks found in input
7211% PNG file and copy them into output PNG files according to the PNG
7212% copying rules.
7213%
7214% Write the iCCP chunk at MNG level when (icc profile length > 0)
7215%
7216% Improve selection of color type (use indexed-colour or indexed-colour
7217% with tRNS when 256 or fewer unique RGBA values are present).
7218%
7219% Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7220% This will be complicated if we limit ourselves to generating MNG-LC
7221% files. For now we ignore disposal method 3 and simply overlay the next
7222% image on it.
7223%
7224% Check for identical PLTE's or PLTE/tRNS combinations and use a
7225% global MNG PLTE or PLTE/tRNS combination when appropriate.
7226% [mostly done 15 June 1999 but still need to take care of tRNS]
7227%
7228% Check for identical sRGB and replace with a global sRGB (and remove
7229% gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7230% replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7231% local gAMA/cHRM with local sRGB if appropriate).
7232%
7233% Check for identical sBIT chunks and write global ones.
7234%
7235% Provide option to skip writing the signature tEXt chunks.
7236%
7237% Use signatures to detect identical objects and reuse the first
7238% instance of such objects instead of writing duplicate objects.
7239%
7240% Use a smaller-than-32k value of compression window size when
7241% appropriate.
7242%
7243% Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7244% ancillary text chunks and save profiles.
7245%
7246% Provide an option to force LC files (to ensure exact framing rate)
7247% instead of VLC.
7248%
7249% Provide an option to force VLC files instead of LC, even when offsets
7250% are present. This will involve expanding the embedded images with a
7251% transparent region at the top and/or left.
7252*/
7253
cristy3ed852e2009-09-05 21:47:34 +00007254static void
glennrpcf002022011-01-30 02:38:15 +00007255Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
cristy3ed852e2009-09-05 21:47:34 +00007256 png_info *ping_info, unsigned char *profile_type, unsigned char
7257 *profile_description, unsigned char *profile_data, png_uint_32 length)
7258{
cristy3ed852e2009-09-05 21:47:34 +00007259 png_textp
7260 text;
7261
cristybb503372010-05-27 20:51:26 +00007262 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007263 i;
7264
7265 unsigned char
7266 *sp;
7267
7268 png_charp
7269 dp;
7270
7271 png_uint_32
7272 allocated_length,
7273 description_length;
7274
7275 unsigned char
7276 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
cristy3ed852e2009-09-05 21:47:34 +00007277
cristy3ed852e2009-09-05 21:47:34 +00007278 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7279 return;
7280
7281 if (image_info->verbose)
7282 {
glennrp0fe50b42010-11-16 03:52:51 +00007283 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7284 (char *) profile_type, (double) length);
cristy3ed852e2009-09-05 21:47:34 +00007285 }
glennrp0fe50b42010-11-16 03:52:51 +00007286
cristy3ed852e2009-09-05 21:47:34 +00007287 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
7288 description_length=(png_uint_32) strlen((const char *) profile_description);
7289 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7290 + description_length);
7291 text[0].text=(png_charp) png_malloc(ping,allocated_length);
7292 text[0].key=(png_charp) png_malloc(ping, (png_uint_32) 80);
7293 text[0].key[0]='\0';
7294 (void) ConcatenateMagickString(text[0].key,
7295 "Raw profile type ",MaxTextExtent);
7296 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7297 sp=profile_data;
7298 dp=text[0].text;
7299 *dp++='\n';
7300 (void) CopyMagickString(dp,(const char *) profile_description,
7301 allocated_length);
7302 dp+=description_length;
7303 *dp++='\n';
cristy3b6fd2e2011-05-20 12:53:50 +00007304 (void) FormatLocaleString(dp,allocated_length-
cristyf2faecf2010-05-28 19:19:36 +00007305 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
cristy3ed852e2009-09-05 21:47:34 +00007306 dp+=8;
glennrp47b9dd52010-11-24 18:12:06 +00007307
cristybb503372010-05-27 20:51:26 +00007308 for (i=0; i < (ssize_t) length; i++)
cristy3ed852e2009-09-05 21:47:34 +00007309 {
7310 if (i%36 == 0)
7311 *dp++='\n';
7312 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7313 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7314 }
glennrp47b9dd52010-11-24 18:12:06 +00007315
cristy3ed852e2009-09-05 21:47:34 +00007316 *dp++='\n';
7317 *dp='\0';
7318 text[0].text_length=(png_size_t) (dp-text[0].text);
7319 text[0].compression=image_info->compression == NoCompression ||
7320 (image_info->compression == UndefinedCompression &&
7321 text[0].text_length < 128) ? -1 : 0;
glennrp47b9dd52010-11-24 18:12:06 +00007322
cristy3ed852e2009-09-05 21:47:34 +00007323 if (text[0].text_length <= allocated_length)
7324 png_set_text(ping,ping_info,text,1);
glennrp47b9dd52010-11-24 18:12:06 +00007325
cristy3ed852e2009-09-05 21:47:34 +00007326 png_free(ping,text[0].text);
7327 png_free(ping,text[0].key);
7328 png_free(ping,text);
cristy3ed852e2009-09-05 21:47:34 +00007329}
7330
glennrpcf002022011-01-30 02:38:15 +00007331static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
cristy4383ec82011-01-05 15:42:32 +00007332 const char *string, MagickBooleanType logging)
cristy3ed852e2009-09-05 21:47:34 +00007333{
7334 char
7335 *name;
7336
7337 const StringInfo
7338 *profile;
7339
7340 unsigned char
7341 *data;
7342
7343 png_uint_32 length;
7344
7345 ResetImageProfileIterator(image);
glennrp47b9dd52010-11-24 18:12:06 +00007346
7347 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7348 {
cristy3ed852e2009-09-05 21:47:34 +00007349 profile=GetImageProfile(image,name);
glennrp47b9dd52010-11-24 18:12:06 +00007350
cristy3ed852e2009-09-05 21:47:34 +00007351 if (profile != (const StringInfo *) NULL)
7352 {
7353 StringInfo
glennrpcf002022011-01-30 02:38:15 +00007354 *ping_profile;
cristy3ed852e2009-09-05 21:47:34 +00007355
glennrp47b9dd52010-11-24 18:12:06 +00007356 if (LocaleNCompare(name,string,11) == 0)
7357 {
7358 if (logging != MagickFalse)
7359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7360 " Found %s profile",name);
cristy3ed852e2009-09-05 21:47:34 +00007361
glennrpcf002022011-01-30 02:38:15 +00007362 ping_profile=CloneStringInfo(profile);
7363 data=GetStringInfoDatum(ping_profile),
7364 length=(png_uint_32) GetStringInfoLength(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007365 data[4]=data[3];
7366 data[3]=data[2];
7367 data[2]=data[1];
7368 data[1]=data[0];
7369 (void) WriteBlobMSBULong(image,length-5); /* data length */
7370 (void) WriteBlob(image,length-1,data+1);
7371 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
glennrpcf002022011-01-30 02:38:15 +00007372 ping_profile=DestroyStringInfo(ping_profile);
glennrp47b9dd52010-11-24 18:12:06 +00007373 }
cristy3ed852e2009-09-05 21:47:34 +00007374 }
glennrp47b9dd52010-11-24 18:12:06 +00007375
cristy3ed852e2009-09-05 21:47:34 +00007376 name=GetNextImageProfile(image);
7377 }
glennrp47b9dd52010-11-24 18:12:06 +00007378
cristy3ed852e2009-09-05 21:47:34 +00007379 return(MagickTrue);
7380}
7381
glennrpb9cfe272010-12-21 15:08:06 +00007382
cristy3ed852e2009-09-05 21:47:34 +00007383/* Write one PNG image */
glennrpb9cfe272010-12-21 15:08:06 +00007384static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7385 const ImageInfo *IMimage_info,Image *IMimage)
7386{
7387 Image
7388 *image;
7389
7390 ImageInfo
7391 *image_info;
7392
cristy3ed852e2009-09-05 21:47:34 +00007393 char
7394 s[2];
7395
7396 const char
7397 *name,
7398 *property,
7399 *value;
7400
7401 const StringInfo
7402 *profile;
7403
cristy3ed852e2009-09-05 21:47:34 +00007404 int
cristy3ed852e2009-09-05 21:47:34 +00007405 num_passes,
glennrpcecd5762010-03-23 12:07:49 +00007406 pass;
7407
glennrpe9c26dc2010-05-30 01:56:35 +00007408 png_byte
7409 ping_trans_alpha[256];
glennrp5af765f2010-03-30 11:12:18 +00007410
glennrp39992b42010-11-14 00:03:43 +00007411 png_color
7412 palette[257];
cristy3ed852e2009-09-05 21:47:34 +00007413
glennrp5af765f2010-03-30 11:12:18 +00007414 png_color_16
7415 ping_background,
7416 ping_trans_color;
7417
cristy3ed852e2009-09-05 21:47:34 +00007418 png_info
7419 *ping_info;
7420
7421 png_struct
7422 *ping;
7423
glennrp5af765f2010-03-30 11:12:18 +00007424 png_uint_32
7425 ping_height,
7426 ping_width;
7427
cristybb503372010-05-27 20:51:26 +00007428 ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007429 y;
7430
7431 MagickBooleanType
glennrp58e01762011-01-07 15:28:54 +00007432 image_matte,
glennrp21f0e622011-01-07 16:20:57 +00007433 logging,
glennrp58e01762011-01-07 15:28:54 +00007434 matte,
7435
glennrpda8f3a72011-02-27 23:54:12 +00007436 ping_have_blob,
glennrpfd05d622011-02-25 04:10:33 +00007437 ping_have_cheap_transparency,
glennrpd6bf1612010-12-17 17:28:54 +00007438 ping_have_color,
glennrp8d579662011-02-23 02:05:02 +00007439 ping_have_non_bw,
glennrp39992b42010-11-14 00:03:43 +00007440 ping_have_PLTE,
glennrp991d11d2010-11-12 21:55:28 +00007441 ping_have_bKGD,
7442 ping_have_pHYs,
7443 ping_have_tRNS,
glennrp26f37912010-12-23 16:22:42 +00007444
7445 ping_exclude_bKGD,
7446 ping_exclude_cHRM,
glennrpa0ed0092011-04-18 16:36:29 +00007447 ping_exclude_date,
glennrpe4e2d792011-02-21 12:11:27 +00007448 /* ping_exclude_EXIF, */
glennrp26f37912010-12-23 16:22:42 +00007449 ping_exclude_gAMA,
7450 ping_exclude_iCCP,
7451 /* ping_exclude_iTXt, */
7452 ping_exclude_oFFs,
7453 ping_exclude_pHYs,
7454 ping_exclude_sRGB,
7455 ping_exclude_tEXt,
glennrpe4e2d792011-02-21 12:11:27 +00007456 /* ping_exclude_tRNS, */
glennrp26f37912010-12-23 16:22:42 +00007457 ping_exclude_vpAg,
7458 ping_exclude_zCCP, /* hex-encoded iCCP */
7459 ping_exclude_zTXt,
7460
glennrp8d3d6e52011-04-19 04:39:51 +00007461 ping_preserve_colormap,
glennrp0e8ea192010-12-24 18:00:33 +00007462 ping_need_colortype_warning,
7463
glennrp82b3c532011-03-22 19:20:54 +00007464 status,
glennrp8ca51ad2011-05-12 21:22:32 +00007465 tried_332,
glennrpd3371642011-03-22 19:42:23 +00007466 tried_333,
7467 tried_444;
cristy3ed852e2009-09-05 21:47:34 +00007468
7469 QuantumInfo
7470 *quantum_info;
7471
cristybb503372010-05-27 20:51:26 +00007472 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00007473 i,
7474 x;
7475
7476 unsigned char
glennrpcf002022011-01-30 02:38:15 +00007477 *ping_pixels;
cristy3ed852e2009-09-05 21:47:34 +00007478
glennrp5af765f2010-03-30 11:12:18 +00007479 volatile int
glennrpf09bded2011-01-08 01:15:59 +00007480 image_colors,
glennrp0fe50b42010-11-16 03:52:51 +00007481 ping_bit_depth,
glennrp5af765f2010-03-30 11:12:18 +00007482 ping_color_type,
7483 ping_interlace_method,
7484 ping_compression_method,
7485 ping_filter_method,
7486 ping_num_trans;
7487
cristybb503372010-05-27 20:51:26 +00007488 volatile size_t
glennrp5af765f2010-03-30 11:12:18 +00007489 image_depth,
7490 old_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00007491
cristybb503372010-05-27 20:51:26 +00007492 size_t
cristy3ed852e2009-09-05 21:47:34 +00007493 quality,
7494 rowbytes,
7495 save_image_depth;
7496
glennrpdfd70802010-11-14 01:23:35 +00007497 int
glennrpfd05d622011-02-25 04:10:33 +00007498 j,
glennrpf09bded2011-01-08 01:15:59 +00007499 number_colors,
glennrp8bb3a022010-12-13 20:40:04 +00007500 number_opaque,
7501 number_semitransparent,
7502 number_transparent,
glennrpdfd70802010-11-14 01:23:35 +00007503 ping_pHYs_unit_type;
7504
7505 png_uint_32
7506 ping_pHYs_x_resolution,
7507 ping_pHYs_y_resolution;
7508
cristy3ed852e2009-09-05 21:47:34 +00007509 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +00007510 " Enter WriteOnePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +00007511
glennrpb9cfe272010-12-21 15:08:06 +00007512 image = CloneImage(IMimage,0,0,MagickFalse,&IMimage->exception);
7513 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
glennrp8d3d6e52011-04-19 04:39:51 +00007514 if (image_info == (ImageInfo *) NULL)
glennrp7b2cc792011-04-18 16:46:02 +00007515 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
glennrpb9cfe272010-12-21 15:08:06 +00007516
cristy3ed852e2009-09-05 21:47:34 +00007517#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00007518 LockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00007519#endif
7520
glennrp5af765f2010-03-30 11:12:18 +00007521 /* Initialize some stuff */
glennrp0fe50b42010-11-16 03:52:51 +00007522 ping_bit_depth=0,
glennrp5af765f2010-03-30 11:12:18 +00007523 ping_color_type=0,
7524 ping_interlace_method=0,
7525 ping_compression_method=0,
7526 ping_filter_method=0,
7527 ping_num_trans = 0;
7528
7529 ping_background.red = 0;
7530 ping_background.green = 0;
7531 ping_background.blue = 0;
7532 ping_background.gray = 0;
7533 ping_background.index = 0;
7534
7535 ping_trans_color.red=0;
7536 ping_trans_color.green=0;
7537 ping_trans_color.blue=0;
7538 ping_trans_color.gray=0;
7539
glennrpdfd70802010-11-14 01:23:35 +00007540 ping_pHYs_unit_type = 0;
7541 ping_pHYs_x_resolution = 0;
7542 ping_pHYs_y_resolution = 0;
7543
glennrpda8f3a72011-02-27 23:54:12 +00007544 ping_have_blob=MagickFalse;
glennrpd6bf1612010-12-17 17:28:54 +00007545 ping_have_color=MagickTrue;
glennrp8d579662011-02-23 02:05:02 +00007546 ping_have_non_bw=MagickTrue;
glennrp39992b42010-11-14 00:03:43 +00007547 ping_have_PLTE=MagickFalse;
glennrp991d11d2010-11-12 21:55:28 +00007548 ping_have_bKGD=MagickFalse;
7549 ping_have_pHYs=MagickFalse;
7550 ping_have_tRNS=MagickFalse;
7551
glennrp0e8ea192010-12-24 18:00:33 +00007552 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
7553 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
glennrpa0ed0092011-04-18 16:36:29 +00007554 ping_exclude_date=mng_info->ping_exclude_date;
glennrpdde35db2011-02-21 12:06:32 +00007555 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
glennrp0e8ea192010-12-24 18:00:33 +00007556 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
glennrp0e8ea192010-12-24 18:00:33 +00007557 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
7558 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
7559 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
7560 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
7561 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
7562 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
glennrpdde35db2011-02-21 12:06:32 +00007563 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
glennrp0e8ea192010-12-24 18:00:33 +00007564 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
7565 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
7566 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
7567
glennrp8d3d6e52011-04-19 04:39:51 +00007568 ping_preserve_colormap = mng_info->ping_preserve_colormap;
glennrp0e8ea192010-12-24 18:00:33 +00007569 ping_need_colortype_warning = MagickFalse;
7570
glennrp8bb3a022010-12-13 20:40:04 +00007571 number_opaque = 0;
7572 number_semitransparent = 0;
7573 number_transparent = 0;
7574
glennrpfd05d622011-02-25 04:10:33 +00007575 if (logging != MagickFalse)
7576 {
7577 if (image->storage_class == UndefinedClass)
7578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7579 " storage_class=UndefinedClass");
7580 if (image->storage_class == DirectClass)
7581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7582 " storage_class=DirectClass");
7583 if (image->storage_class == PseudoClass)
7584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7585 " storage_class=PseudoClass");
7586 }
glennrp28af3712011-04-06 18:07:30 +00007587
glennrpc6c391a2011-04-27 02:23:56 +00007588 if (ping_preserve_colormap == MagickFalse)
glennrp28af3712011-04-06 18:07:30 +00007589 {
glennrpc6c391a2011-04-27 02:23:56 +00007590 if (image->storage_class != PseudoClass && image->colormap != NULL)
7591 {
7592 /* Free the bogus colormap; it can cause trouble later */
7593 if (logging != MagickFalse)
7594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7595 " Freeing bogus colormap");
7596 (void *) RelinquishMagickMemory(image->colormap);
7597 image->colormap=NULL;
7598 }
glennrp28af3712011-04-06 18:07:30 +00007599 }
glennrpbb4f99d2011-05-22 11:13:17 +00007600
cristy3ed852e2009-09-05 21:47:34 +00007601 if (image->colorspace != RGBColorspace)
7602 (void) TransformImageColorspace(image,RGBColorspace);
glennrp0fe50b42010-11-16 03:52:51 +00007603
glennrp3241bd02010-12-12 04:36:28 +00007604 /*
7605 Sometimes we get PseudoClass images whose RGB values don't match
7606 the colors in the colormap. This code syncs the RGB values.
7607 */
7608 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
7609 (void) SyncImage(image);
7610
glennrpa6a06632011-01-19 15:15:34 +00007611#if (MAGICKCORE_QUANTUM_DEPTH == 8)
7612 if (image->depth > 8)
7613 {
7614 if (logging != MagickFalse)
7615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7616 " Reducing PNG bit depth to 8 since this is a Q8 build.");
7617
7618 image->depth=8;
7619 }
7620#endif
7621
glennrp8e58efd2011-05-20 12:16:29 +00007622 /* Respect the -depth option */
glennrpcc95c3f2011-04-18 16:46:48 +00007623 if (image->depth < MAGICKCORE_QUANTUM_DEPTH)
7624 {
cristy4c08aed2011-07-01 19:47:50 +00007625 register Quantum
glennrp8e58efd2011-05-20 12:16:29 +00007626 *r;
7627
7628 ExceptionInfo
7629 *exception;
7630
7631 exception=(&image->exception);
7632
7633 if (image->depth > 8)
7634 {
7635#if MAGICKCORE_QUANTUM_DEPTH > 16
7636 /* Scale to 16-bit */
glennrp91d99252011-06-25 14:30:13 +00007637 LBR16PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007638
7639 for (y=0; y < (ssize_t) image->rows; y++)
7640 {
7641 r=GetAuthenticPixels(image,0,y,image->columns,1,
7642 exception);
7643
cristy4c08aed2011-07-01 19:47:50 +00007644 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007645 break;
7646
7647 for (x=0; x < (ssize_t) image->columns; x++)
7648 {
cristy4c08aed2011-07-01 19:47:50 +00007649 LBR16RGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007650 r++;
7651 }
glennrpbb4f99d2011-05-22 11:13:17 +00007652
glennrp8e58efd2011-05-20 12:16:29 +00007653 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7654 break;
7655 }
7656
7657 if (image->storage_class == PseudoClass && image->colormap != NULL)
7658 {
cristy3e08f112011-05-24 13:19:30 +00007659 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007660 {
glennrp91d99252011-06-25 14:30:13 +00007661 LBR16PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007662 }
7663 }
7664#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
7665 }
7666
7667 else if (image->depth > 4)
7668 {
7669#if MAGICKCORE_QUANTUM_DEPTH > 8
7670 /* Scale to 8-bit */
glennrp91d99252011-06-25 14:30:13 +00007671 LBR08PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007672
7673 for (y=0; y < (ssize_t) image->rows; y++)
7674 {
7675 r=GetAuthenticPixels(image,0,y,image->columns,1,
7676 exception);
7677
cristy4c08aed2011-07-01 19:47:50 +00007678 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007679 break;
7680
7681 for (x=0; x < (ssize_t) image->columns; x++)
7682 {
cristy4c08aed2011-07-01 19:47:50 +00007683 LBR08RGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007684 r++;
7685 }
glennrpbb4f99d2011-05-22 11:13:17 +00007686
glennrp8e58efd2011-05-20 12:16:29 +00007687 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7688 break;
7689 }
7690
7691 if (image->storage_class == PseudoClass && image->colormap != NULL)
7692 {
cristy3e08f112011-05-24 13:19:30 +00007693 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007694 {
glennrp91d99252011-06-25 14:30:13 +00007695 LBR08PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007696 }
7697 }
7698#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
7699 }
7700 else
7701 if (image->depth > 2)
7702 {
7703 /* Scale to 4-bit */
glennrp91d99252011-06-25 14:30:13 +00007704 LBR04PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007705
7706 for (y=0; y < (ssize_t) image->rows; y++)
7707 {
7708 r=GetAuthenticPixels(image,0,y,image->columns,1,
7709 exception);
7710
cristy4c08aed2011-07-01 19:47:50 +00007711 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007712 break;
7713
7714 for (x=0; x < (ssize_t) image->columns; x++)
7715 {
cristy4c08aed2011-07-01 19:47:50 +00007716 LBR04RGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007717 r++;
7718 }
glennrpbb4f99d2011-05-22 11:13:17 +00007719
glennrp8e58efd2011-05-20 12:16:29 +00007720 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7721 break;
7722 }
7723
7724 if (image->storage_class == PseudoClass && image->colormap != NULL)
7725 {
cristy3e08f112011-05-24 13:19:30 +00007726 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007727 {
glennrp91d99252011-06-25 14:30:13 +00007728 LBR04PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007729 }
7730 }
7731 }
7732
7733 else if (image->depth > 1)
7734 {
7735 /* Scale to 2-bit */
glennrp91d99252011-06-25 14:30:13 +00007736 LBR02PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007737
7738 for (y=0; y < (ssize_t) image->rows; y++)
7739 {
7740 r=GetAuthenticPixels(image,0,y,image->columns,1,
7741 exception);
7742
cristy4c08aed2011-07-01 19:47:50 +00007743 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007744 break;
7745
7746 for (x=0; x < (ssize_t) image->columns; x++)
7747 {
cristy4c08aed2011-07-01 19:47:50 +00007748 LBR02RGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007749 r++;
7750 }
glennrpbb4f99d2011-05-22 11:13:17 +00007751
glennrp8e58efd2011-05-20 12:16:29 +00007752 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7753 break;
7754 }
7755
7756 if (image->storage_class == PseudoClass && image->colormap != NULL)
7757 {
cristy3e08f112011-05-24 13:19:30 +00007758 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007759 {
glennrp91d99252011-06-25 14:30:13 +00007760 LBR02PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007761 }
7762 }
7763 }
7764 else
7765 {
7766 /* Scale to 1-bit */
glennrp91d99252011-06-25 14:30:13 +00007767 LBR01PacketRGBO(image->background_color);
glennrp8e58efd2011-05-20 12:16:29 +00007768
7769 for (y=0; y < (ssize_t) image->rows; y++)
7770 {
7771 r=GetAuthenticPixels(image,0,y,image->columns,1,
7772 exception);
7773
cristy4c08aed2011-07-01 19:47:50 +00007774 if (r == (Quantum *) NULL)
glennrp8e58efd2011-05-20 12:16:29 +00007775 break;
7776
7777 for (x=0; x < (ssize_t) image->columns; x++)
7778 {
cristy4c08aed2011-07-01 19:47:50 +00007779 LBR01RGBA(r);
glennrp8e58efd2011-05-20 12:16:29 +00007780 r++;
7781 }
glennrpbb4f99d2011-05-22 11:13:17 +00007782
glennrp8e58efd2011-05-20 12:16:29 +00007783 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7784 break;
7785 }
7786
7787 if (image->storage_class == PseudoClass && image->colormap != NULL)
7788 {
cristy3e08f112011-05-24 13:19:30 +00007789 for (i=0; i < (ssize_t) image->colors; i++)
glennrp8e58efd2011-05-20 12:16:29 +00007790 {
glennrp91d99252011-06-25 14:30:13 +00007791 LBR01PacketRGBO(image->colormap[i]);
glennrp8e58efd2011-05-20 12:16:29 +00007792 }
7793 }
7794 }
glennrp9d0ea4d2011-04-22 18:35:57 +00007795 }
7796
glennrp67b9c1a2011-04-22 18:47:36 +00007797 /* To do: set to next higher multiple of 8 */
7798 if (image->depth < 8)
glennrp70e68a82011-04-01 22:51:14 +00007799 image->depth=8;
glennrpa6a06632011-01-19 15:15:34 +00007800
glennrp2b013e42010-11-24 16:55:50 +00007801#if (MAGICKCORE_QUANTUM_DEPTH > 16)
7802 /* PNG does not handle depths greater than 16 so reduce it even
7803 * if lossy
7804 */
glennrp8e58efd2011-05-20 12:16:29 +00007805 if (image->depth > 8)
glennrp2b013e42010-11-24 16:55:50 +00007806 image->depth=16;
7807#endif
7808
glennrp3faa9a32011-04-23 14:00:25 +00007809#if (MAGICKCORE_QUANTUM_DEPTH > 8)
glennrpc722dd82011-02-24 05:13:21 +00007810 if (image->depth == 16 && mng_info->write_png_depth != 16)
glennrp8bb3a022010-12-13 20:40:04 +00007811 if (mng_info->write_png8 || LosslessReduceDepthOK(image) != MagickFalse)
glennrp8640fb52010-11-23 15:48:26 +00007812 image->depth = 8;
7813#endif
7814
glennrpc8c2f062011-02-25 19:00:33 +00007815 /* Normally we run this just once, but in the case of writing PNG8
glennrpe9637cb2011-03-24 16:34:37 +00007816 * we reduce the transparency to binary and run again, then if there
7817 * 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 +00007818 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
7819 * palette. Then (To do) we take care of a final reduction that is only
7820 * needed if there are still 256 colors present and one of them has both
7821 * transparent and opaque instances.
glennrpc8c2f062011-02-25 19:00:33 +00007822 */
glennrp82b3c532011-03-22 19:20:54 +00007823
glennrp8ca51ad2011-05-12 21:22:32 +00007824 tried_332 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007825 tried_333 = MagickFalse;
glennrpd3371642011-03-22 19:42:23 +00007826 tried_444 = MagickFalse;
glennrp82b3c532011-03-22 19:20:54 +00007827
glennrp8ca51ad2011-05-12 21:22:32 +00007828 for (j=0; j<6; j++)
glennrpd71e86a2011-02-24 01:28:37 +00007829 {
7830 /* BUILD_PALETTE
7831 *
7832 * Sometimes we get DirectClass images that have 256 colors or fewer.
7833 * This code will build a colormap.
7834 *
7835 * Also, sometimes we get PseudoClass images with an out-of-date
7836 * colormap. This code will replace the colormap with a new one.
7837 * Sometimes we get PseudoClass images that have more than 256 colors.
7838 * This code will delete the colormap and change the image to
7839 * DirectClass.
7840 *
cristy4c08aed2011-07-01 19:47:50 +00007841 * If image->matte is MagickFalse, we ignore the alpha channel
glennrpd71e86a2011-02-24 01:28:37 +00007842 * even though it sometimes contains left-over non-opaque values.
7843 *
7844 * Also we gather some information (number of opaque, transparent,
7845 * and semitransparent pixels, and whether the image has any non-gray
7846 * pixels or only black-and-white pixels) that we might need later.
7847 *
7848 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
7849 * we need to check for bogus non-opaque values, at least.
7850 */
glennrp3c218112010-11-27 15:31:26 +00007851
glennrpd71e86a2011-02-24 01:28:37 +00007852 ExceptionInfo
7853 *exception;
glennrp3c218112010-11-27 15:31:26 +00007854
glennrpd71e86a2011-02-24 01:28:37 +00007855 int
7856 n;
glennrp3c218112010-11-27 15:31:26 +00007857
glennrpd71e86a2011-02-24 01:28:37 +00007858 PixelPacket
7859 opaque[260],
7860 semitransparent[260],
7861 transparent[260];
glennrp8bb3a022010-12-13 20:40:04 +00007862
cristy4c08aed2011-07-01 19:47:50 +00007863 register const Quantum
7864 *s;
glennrp8bb3a022010-12-13 20:40:04 +00007865
cristy4c08aed2011-07-01 19:47:50 +00007866 register Quantum
7867 *q,
glennrpfd05d622011-02-25 04:10:33 +00007868 *r;
7869
glennrpd71e86a2011-02-24 01:28:37 +00007870 if (logging != MagickFalse)
7871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7872 " Enter BUILD_PALETTE:");
7873
7874 if (logging != MagickFalse)
7875 {
glennrp03812ae2010-12-24 01:31:34 +00007876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007877 " image->columns=%.20g",(double) image->columns);
7878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7879 " image->rows=%.20g",(double) image->rows);
7880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7881 " image->matte=%.20g",(double) image->matte);
7882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7883 " image->depth=%.20g",(double) image->depth);
glennrp8bb3a022010-12-13 20:40:04 +00007884
glennrpfd05d622011-02-25 04:10:33 +00007885 if (image->storage_class == PseudoClass && image->colormap != NULL)
glennrp7ddcc222010-12-11 05:01:05 +00007886 {
7887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd71e86a2011-02-24 01:28:37 +00007888 " Original colormap:");
glennrp8bb3a022010-12-13 20:40:04 +00007889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00007890 " i (red,green,blue,alpha)");
glennrp2cc891a2010-12-24 13:44:32 +00007891
glennrpd71e86a2011-02-24 01:28:37 +00007892 for (i=0; i < 256; i++)
glennrp7ddcc222010-12-11 05:01:05 +00007893 {
glennrpd71e86a2011-02-24 01:28:37 +00007894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7895 " %d (%d,%d,%d,%d)",
7896 (int) i,
7897 (int) image->colormap[i].red,
7898 (int) image->colormap[i].green,
7899 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00007900 (int) image->colormap[i].alpha);
glennrp7ddcc222010-12-11 05:01:05 +00007901 }
glennrp2cc891a2010-12-24 13:44:32 +00007902
glennrpd71e86a2011-02-24 01:28:37 +00007903 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
7904 {
7905 if (i > 255)
7906 {
7907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7908 " %d (%d,%d,%d,%d)",
7909 (int) i,
7910 (int) image->colormap[i].red,
7911 (int) image->colormap[i].green,
7912 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00007913 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00007914 }
7915 }
glennrp03812ae2010-12-24 01:31:34 +00007916 }
glennrp7ddcc222010-12-11 05:01:05 +00007917
glennrpd71e86a2011-02-24 01:28:37 +00007918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7919 " image->colors=%d",(int) image->colors);
glennrp7ddcc222010-12-11 05:01:05 +00007920
glennrpd71e86a2011-02-24 01:28:37 +00007921 if (image->colors == 0)
7922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7923 " (zero means unknown)");
glennrpd6bf1612010-12-17 17:28:54 +00007924
glennrp8d3d6e52011-04-19 04:39:51 +00007925 if (ping_preserve_colormap == MagickFalse)
7926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7927 " Regenerate the colormap");
glennrpd71e86a2011-02-24 01:28:37 +00007928 }
7929
7930 exception=(&image->exception);
7931
7932 image_colors=0;
glennrpfd05d622011-02-25 04:10:33 +00007933 number_opaque = 0;
7934 number_semitransparent = 0;
7935 number_transparent = 0;
glennrpd71e86a2011-02-24 01:28:37 +00007936
7937 for (y=0; y < (ssize_t) image->rows; y++)
7938 {
7939 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7940
cristy4c08aed2011-07-01 19:47:50 +00007941 if (q == (const Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00007942 break;
7943
7944 for (x=0; x < (ssize_t) image->columns; x++)
glennrp8bb3a022010-12-13 20:40:04 +00007945 {
glennrp4737d522011-04-29 03:33:42 +00007946 if (image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00007947 GetPixelAlpha(image,q) == OpaqueAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00007948 {
7949 if (number_opaque < 259)
7950 {
7951 if (number_opaque == 0)
7952 {
cristy4c08aed2011-07-01 19:47:50 +00007953 GetPixelPacket(image, q, opaque);
7954 opaque[0].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00007955 number_opaque=1;
7956 }
glennrp2cc891a2010-12-24 13:44:32 +00007957
glennrpd71e86a2011-02-24 01:28:37 +00007958 for (i=0; i< (ssize_t) number_opaque; i++)
7959 {
cristy4c08aed2011-07-01 19:47:50 +00007960 if (IsPixelEquivalent(image,q, opaque+i))
glennrpd71e86a2011-02-24 01:28:37 +00007961 break;
7962 }
glennrp7ddcc222010-12-11 05:01:05 +00007963
glennrpd71e86a2011-02-24 01:28:37 +00007964 if (i == (ssize_t) number_opaque &&
7965 number_opaque < 259)
7966 {
7967 number_opaque++;
cristy4c08aed2011-07-01 19:47:50 +00007968 GetPixelPacket(image, q, opaque+i);
7969 opaque[i].alpha=OpaqueAlpha;
glennrpd71e86a2011-02-24 01:28:37 +00007970 }
7971 }
7972 }
cristy4c08aed2011-07-01 19:47:50 +00007973 else if (GetPixelAlpha(image,q) == TransparentAlpha)
glennrpd71e86a2011-02-24 01:28:37 +00007974 {
7975 if (number_transparent < 259)
7976 {
7977 if (number_transparent == 0)
7978 {
cristy4c08aed2011-07-01 19:47:50 +00007979 GetPixelPacket(image, q, transparent);
7980 ping_trans_color.red=(unsigned short)
7981 GetPixelRed(image,q);
7982 ping_trans_color.green=(unsigned short)
7983 GetPixelGreen(image,q);
7984 ping_trans_color.blue=(unsigned short)
7985 GetPixelBlue(image,q);
7986 ping_trans_color.gray=(unsigned short)
7987 GetPixelRed(image,q);
glennrpd71e86a2011-02-24 01:28:37 +00007988 number_transparent = 1;
7989 }
7990
7991 for (i=0; i< (ssize_t) number_transparent; i++)
7992 {
cristy4c08aed2011-07-01 19:47:50 +00007993 if (IsPixelEquivalent(image,q, transparent+i))
glennrpd71e86a2011-02-24 01:28:37 +00007994 break;
7995 }
7996
7997 if (i == (ssize_t) number_transparent &&
7998 number_transparent < 259)
7999 {
8000 number_transparent++;
cristy4c08aed2011-07-01 19:47:50 +00008001 GetPixelPacket(image,q,transparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008002 }
8003 }
8004 }
8005 else
8006 {
8007 if (number_semitransparent < 259)
8008 {
8009 if (number_semitransparent == 0)
8010 {
cristy4c08aed2011-07-01 19:47:50 +00008011 GetPixelPacket(image,q,semitransparent);
glennrpd71e86a2011-02-24 01:28:37 +00008012 number_semitransparent = 1;
8013 }
8014
8015 for (i=0; i< (ssize_t) number_semitransparent; i++)
8016 {
cristy4c08aed2011-07-01 19:47:50 +00008017 if (IsPixelEquivalent(image,q, semitransparent+i)
8018 && GetPixelAlpha(image,q) ==
8019 semitransparent[i].alpha)
glennrpd71e86a2011-02-24 01:28:37 +00008020 break;
8021 }
8022
8023 if (i == (ssize_t) number_semitransparent &&
8024 number_semitransparent < 259)
8025 {
8026 number_semitransparent++;
cristy4c08aed2011-07-01 19:47:50 +00008027 GetPixelPacket(image, q, semitransparent+i);
glennrpd71e86a2011-02-24 01:28:37 +00008028 }
8029 }
8030 }
cristy4c08aed2011-07-01 19:47:50 +00008031 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008032 }
8033 }
8034
8035 if (ping_exclude_bKGD == MagickFalse)
8036 {
8037 /* Add the background color to the palette, if it
8038 * isn't already there.
8039 */
glennrpc6c391a2011-04-27 02:23:56 +00008040 if (logging != MagickFalse)
8041 {
8042 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8043 " Check colormap for background (%d,%d,%d)",
8044 (int) image->background_color.red,
8045 (int) image->background_color.green,
8046 (int) image->background_color.blue);
8047 }
glennrpd71e86a2011-02-24 01:28:37 +00008048 for (i=0; i<number_opaque; i++)
8049 {
glennrpca7ad3a2011-04-26 04:44:54 +00008050 if (opaque[i].red == image->background_color.red &&
8051 opaque[i].green == image->background_color.green &&
8052 opaque[i].blue == image->background_color.blue)
8053 break;
glennrpd71e86a2011-02-24 01:28:37 +00008054 }
glennrpd71e86a2011-02-24 01:28:37 +00008055 if (number_opaque < 259 && i == number_opaque)
8056 {
glennrp8e045c82011-04-27 16:40:27 +00008057 opaque[i] = image->background_color;
glennrpc6c391a2011-04-27 02:23:56 +00008058 ping_background.index = i;
8059 if (logging != MagickFalse)
8060 {
8061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8062 " background_color index is %d",(int) i);
8063 }
8064
glennrpd71e86a2011-02-24 01:28:37 +00008065 }
glennrpa080bc32011-03-11 18:03:44 +00008066 else if (logging != MagickFalse)
8067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8068 " No room in the colormap to add background color");
glennrpd71e86a2011-02-24 01:28:37 +00008069 }
8070
8071 image_colors=number_opaque+number_transparent+number_semitransparent;
8072
glennrpa080bc32011-03-11 18:03:44 +00008073 if (mng_info->write_png8 != MagickFalse && image_colors > 256)
8074 {
8075 /* No room for the background color; remove it. */
8076 number_opaque--;
8077 image_colors--;
8078 }
8079
glennrpd71e86a2011-02-24 01:28:37 +00008080 if (logging != MagickFalse)
8081 {
8082 if (image_colors > 256)
8083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8084 " image has more than 256 colors");
8085
8086 else
8087 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8088 " image has %d colors",image_colors);
8089 }
8090
glennrp8d3d6e52011-04-19 04:39:51 +00008091 if (ping_preserve_colormap != MagickFalse)
8092 break;
glennrp8d3d6e52011-04-19 04:39:51 +00008093
glennrpfd05d622011-02-25 04:10:33 +00008094 if (mng_info->write_png_colortype != 7) /* We won't need this info */
glennrpd71e86a2011-02-24 01:28:37 +00008095 {
8096 ping_have_color=MagickFalse;
8097 ping_have_non_bw=MagickFalse;
8098
8099 if(image_colors > 256)
8100 {
8101 for (y=0; y < (ssize_t) image->rows; y++)
8102 {
8103 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8104
cristy4c08aed2011-07-01 19:47:50 +00008105 if (q == (const Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008106 break;
8107
8108 /* Worst case is black-and-white; we are looking at every
8109 * pixel twice.
8110 */
8111
8112 if (ping_have_color == MagickFalse)
8113 {
8114 s=q;
8115 for (x=0; x < (ssize_t) image->columns; x++)
8116 {
cristy4c08aed2011-07-01 19:47:50 +00008117 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8118 GetPixelRed(image,s) != GetPixelBlue(image,s))
glennrpd71e86a2011-02-24 01:28:37 +00008119 {
8120 ping_have_color=MagickTrue;
8121 ping_have_non_bw=MagickTrue;
8122 break;
8123 }
cristy4c08aed2011-07-01 19:47:50 +00008124 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008125 }
8126 }
8127
8128 if (ping_have_non_bw == MagickFalse)
8129 {
8130 s=q;
8131 for (x=0; x < (ssize_t) image->columns; x++)
8132 {
cristy4c08aed2011-07-01 19:47:50 +00008133 if (GetPixelRed(image,s) != 0 &&
8134 GetPixelRed(image,s) != QuantumRange)
glennrpd71e86a2011-02-24 01:28:37 +00008135 {
8136 ping_have_non_bw=MagickTrue;
8137 }
cristy4c08aed2011-07-01 19:47:50 +00008138 s+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008139 }
8140 }
8141 }
glennrpbb4f99d2011-05-22 11:13:17 +00008142 }
8143 }
glennrpd71e86a2011-02-24 01:28:37 +00008144
8145 if (image_colors < 257)
8146 {
8147 PixelPacket
8148 colormap[260];
glennrpbb4f99d2011-05-22 11:13:17 +00008149
glennrpd71e86a2011-02-24 01:28:37 +00008150 /*
8151 * Initialize image colormap.
glennrp97fd3d02011-02-23 14:58:06 +00008152 */
8153
glennrpd71e86a2011-02-24 01:28:37 +00008154 if (logging != MagickFalse)
8155 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8156 " Sort the new colormap");
glennrp8d579662011-02-23 02:05:02 +00008157
glennrpd71e86a2011-02-24 01:28:37 +00008158 /* Sort palette, transparent first */;
8159
8160 n = 0;
8161
8162 for (i=0; i<number_transparent; i++)
8163 colormap[n++] = transparent[i];
8164
8165 for (i=0; i<number_semitransparent; i++)
8166 colormap[n++] = semitransparent[i];
8167
8168 for (i=0; i<number_opaque; i++)
8169 colormap[n++] = opaque[i];
8170
glennrpc6c391a2011-04-27 02:23:56 +00008171 ping_background.index +=
8172 (number_transparent + number_semitransparent);
glennrpbb4f99d2011-05-22 11:13:17 +00008173
glennrpd71e86a2011-02-24 01:28:37 +00008174 /* image_colors < 257; search the colormap instead of the pixels
8175 * to get ping_have_color and ping_have_non_bw
8176 */
8177 for (i=0; i<n; i++)
8178 {
8179 if (ping_have_color == MagickFalse)
glennrp8d579662011-02-23 02:05:02 +00008180 {
glennrpd71e86a2011-02-24 01:28:37 +00008181 if (colormap[i].red != colormap[i].green ||
8182 colormap[i].red != colormap[i].blue)
8183 {
8184 ping_have_color=MagickTrue;
8185 ping_have_non_bw=MagickTrue;
8186 break;
8187 }
8188 }
8189
8190 if (ping_have_non_bw == MagickFalse)
8191 {
8192 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
glennrp8d579662011-02-23 02:05:02 +00008193 ping_have_non_bw=MagickTrue;
glennrp8bb3a022010-12-13 20:40:04 +00008194 }
glennrp8bb3a022010-12-13 20:40:04 +00008195 }
8196
glennrpd71e86a2011-02-24 01:28:37 +00008197 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8198 (number_transparent == 0 && number_semitransparent == 0)) &&
8199 (((mng_info->write_png_colortype-1) ==
8200 PNG_COLOR_TYPE_PALETTE) ||
8201 (mng_info->write_png_colortype == 0)))
8202 {
glennrp6185c532011-01-14 17:58:40 +00008203 if (logging != MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008204 {
glennrpd71e86a2011-02-24 01:28:37 +00008205 if (n != (ssize_t) image_colors)
8206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8207 " image_colors (%d) and n (%d) don't match",
8208 image_colors, n);
glennrp6185c532011-01-14 17:58:40 +00008209
glennrpd71e86a2011-02-24 01:28:37 +00008210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8211 " AcquireImageColormap");
glennrp03812ae2010-12-24 01:31:34 +00008212 }
glennrp03812ae2010-12-24 01:31:34 +00008213
glennrpd71e86a2011-02-24 01:28:37 +00008214 image->colors = image_colors;
8215
8216 if (AcquireImageColormap(image,image_colors) ==
8217 MagickFalse)
glennrp3faa9a32011-04-23 14:00:25 +00008218 ThrowWriterException(ResourceLimitError,
8219 "MemoryAllocationFailed");
glennrpd71e86a2011-02-24 01:28:37 +00008220
8221 for (i=0; i< (ssize_t) image_colors; i++)
8222 image->colormap[i] = colormap[i];
8223
8224 if (logging != MagickFalse)
glennrp6185c532011-01-14 17:58:40 +00008225 {
glennrpd71e86a2011-02-24 01:28:37 +00008226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8227 " image->colors=%d (%d)",
8228 (int) image->colors, image_colors);
glennrpbb4f99d2011-05-22 11:13:17 +00008229
glennrpd71e86a2011-02-24 01:28:37 +00008230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8231 " Update the pixel indexes");
8232 }
glennrp6185c532011-01-14 17:58:40 +00008233
glennrpfd05d622011-02-25 04:10:33 +00008234 /* Sync the pixel indices with the new colormap */
8235
glennrpd71e86a2011-02-24 01:28:37 +00008236 for (y=0; y < (ssize_t) image->rows; y++)
8237 {
8238 q=GetAuthenticPixels(image,0,y,image->columns,1,
8239 exception);
glennrp6185c532011-01-14 17:58:40 +00008240
cristy4c08aed2011-07-01 19:47:50 +00008241 if (q == (const Quantum *) NULL)
glennrpd71e86a2011-02-24 01:28:37 +00008242 break;
glennrp6185c532011-01-14 17:58:40 +00008243
glennrpbb4f99d2011-05-22 11:13:17 +00008244
glennrpd71e86a2011-02-24 01:28:37 +00008245 for (x=0; x < (ssize_t) image->columns; x++)
glennrp6185c532011-01-14 17:58:40 +00008246 {
glennrpd71e86a2011-02-24 01:28:37 +00008247 for (i=0; i< (ssize_t) image_colors; i++)
glennrp6185c532011-01-14 17:58:40 +00008248 {
glennrpd71e86a2011-02-24 01:28:37 +00008249 if ((image->matte == MagickFalse ||
cristy4c08aed2011-07-01 19:47:50 +00008250 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8251 image->colormap[i].red == GetPixelRed(image,q) &&
8252 image->colormap[i].green == GetPixelGreen(image,q) &&
8253 image->colormap[i].blue == GetPixelBlue(image,q))
glennrp6185c532011-01-14 17:58:40 +00008254 {
cristy4c08aed2011-07-01 19:47:50 +00008255 SetPixelIndex(image,i,q);
glennrpd71e86a2011-02-24 01:28:37 +00008256 break;
glennrp6185c532011-01-14 17:58:40 +00008257 }
glennrp6185c532011-01-14 17:58:40 +00008258 }
cristy4c08aed2011-07-01 19:47:50 +00008259 q+=GetPixelChannels(image);
glennrpd71e86a2011-02-24 01:28:37 +00008260 }
glennrp6185c532011-01-14 17:58:40 +00008261
glennrpd71e86a2011-02-24 01:28:37 +00008262 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8263 break;
8264 }
8265 }
8266 }
8267
8268 if (logging != MagickFalse)
8269 {
8270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8271 " image->colors=%d", (int) image->colors);
8272
8273 if (image->colormap != NULL)
8274 {
8275 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +00008276 " i (red,green,blue,alpha)");
glennrpd71e86a2011-02-24 01:28:37 +00008277
8278 for (i=0; i < (ssize_t) image->colors; i++)
8279 {
cristy72988482011-03-29 16:34:38 +00008280 if (i < 300 || i >= (ssize_t) image->colors - 10)
glennrpd71e86a2011-02-24 01:28:37 +00008281 {
8282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8283 " %d (%d,%d,%d,%d)",
8284 (int) i,
8285 (int) image->colormap[i].red,
8286 (int) image->colormap[i].green,
8287 (int) image->colormap[i].blue,
cristy4c08aed2011-07-01 19:47:50 +00008288 (int) image->colormap[i].alpha);
glennrpd71e86a2011-02-24 01:28:37 +00008289 }
glennrp6185c532011-01-14 17:58:40 +00008290 }
8291 }
glennrp03812ae2010-12-24 01:31:34 +00008292
glennrpd71e86a2011-02-24 01:28:37 +00008293 if (number_transparent < 257)
8294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8295 " number_transparent = %d",
8296 number_transparent);
8297 else
glennrp03812ae2010-12-24 01:31:34 +00008298
glennrpd71e86a2011-02-24 01:28:37 +00008299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8300 " number_transparent > 256");
glennrp03812ae2010-12-24 01:31:34 +00008301
glennrpd71e86a2011-02-24 01:28:37 +00008302 if (number_opaque < 257)
8303 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8304 " number_opaque = %d",
8305 number_opaque);
glennrp03812ae2010-12-24 01:31:34 +00008306
glennrpd71e86a2011-02-24 01:28:37 +00008307 else
8308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8309 " number_opaque > 256");
glennrp6185c532011-01-14 17:58:40 +00008310
glennrpd71e86a2011-02-24 01:28:37 +00008311 if (number_semitransparent < 257)
8312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8313 " number_semitransparent = %d",
8314 number_semitransparent);
glennrp6185c532011-01-14 17:58:40 +00008315
glennrpd71e86a2011-02-24 01:28:37 +00008316 else
8317 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8318 " number_semitransparent > 256");
glennrpa6a06632011-01-19 15:15:34 +00008319
glennrpd71e86a2011-02-24 01:28:37 +00008320 if (ping_have_non_bw == MagickFalse)
8321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8322 " All pixels and the background are black or white");
glennrpa6a06632011-01-19 15:15:34 +00008323
glennrpd71e86a2011-02-24 01:28:37 +00008324 else if (ping_have_color == MagickFalse)
8325 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8326 " All pixels and the background are gray");
8327
8328 else
8329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8330 " At least one pixel or the background is non-gray");
glennrp6185c532011-01-14 17:58:40 +00008331
glennrp03812ae2010-12-24 01:31:34 +00008332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8333 " Exit BUILD_PALETTE:");
glennrpd71e86a2011-02-24 01:28:37 +00008334 }
glennrpfd05d622011-02-25 04:10:33 +00008335
glennrpc8c2f062011-02-25 19:00:33 +00008336 if (mng_info->write_png8 == MagickFalse)
8337 break;
glennrpfd05d622011-02-25 04:10:33 +00008338
glennrpc8c2f062011-02-25 19:00:33 +00008339 /* Make any reductions necessary for the PNG8 format */
8340 if (image_colors <= 256 &&
8341 image_colors != 0 && image->colormap != NULL &&
8342 number_semitransparent == 0 &&
8343 number_transparent <= 1)
8344 break;
8345
8346 /* PNG8 can't have semitransparent colors so we threshold the
cristy4c08aed2011-07-01 19:47:50 +00008347 * alpha to 0 or OpaqueAlpha
glennrpc8c2f062011-02-25 19:00:33 +00008348 */
8349 if (number_semitransparent != 0)
8350 {
8351 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8352 " Thresholding the alpha channel to binary");
8353
8354 for (y=0; y < (ssize_t) image->rows; y++)
8355 {
8356 r=GetAuthenticPixels(image,0,y,image->columns,1,
8357 exception);
8358
cristy4c08aed2011-07-01 19:47:50 +00008359 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008360 break;
8361
8362 for (x=0; x < (ssize_t) image->columns; x++)
8363 {
cristy4c08aed2011-07-01 19:47:50 +00008364 if (GetPixelAlpha(image,r) > TransparentAlpha/2)
glennrp8ca51ad2011-05-12 21:22:32 +00008365 {
cristy4c08aed2011-07-01 19:47:50 +00008366 SetPixelPacket(image,&image->background_color,r);
8367 SetPixelAlpha(image,TransparentAlpha,r);
glennrp8ca51ad2011-05-12 21:22:32 +00008368 }
8369 else
cristy4c08aed2011-07-01 19:47:50 +00008370 SetPixelAlpha(image,OpaqueAlpha,r);
8371 r+=GetPixelChannels(image);
glennrpc8c2f062011-02-25 19:00:33 +00008372 }
glennrpbb4f99d2011-05-22 11:13:17 +00008373
glennrpc8c2f062011-02-25 19:00:33 +00008374 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8375 break;
8376
8377 if (image_colors != 0 && image_colors <= 256 &&
8378 image->colormap != NULL)
8379 for (i=0; i<image_colors; i++)
cristy4c08aed2011-07-01 19:47:50 +00008380 image->colormap[i].alpha =
8381 (image->colormap[i].alpha > TransparentAlpha/2 ?
8382 TransparentAlpha : OpaqueAlpha);
glennrpc8c2f062011-02-25 19:00:33 +00008383 }
8384 continue;
8385 }
8386
8387 /* PNG8 can't have more than 256 colors so we quantize the pixels and
glennrpe9637cb2011-03-24 16:34:37 +00008388 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8389 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8390 * colors or less.
glennrpc8c2f062011-02-25 19:00:33 +00008391 */
glennrpd3371642011-03-22 19:42:23 +00008392 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8393 {
8394 if (logging != MagickFalse)
8395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8396 " Quantizing the background color to 4-4-4");
8397
8398 tried_444 = MagickTrue;
8399
glennrp91d99252011-06-25 14:30:13 +00008400 LBR04PacketRGB(image->background_color);
glennrpd3371642011-03-22 19:42:23 +00008401
8402 if (logging != MagickFalse)
8403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8404 " Quantizing the pixel colors to 4-4-4");
8405
8406 if (image->colormap == NULL)
8407 {
8408 for (y=0; y < (ssize_t) image->rows; y++)
8409 {
8410 r=GetAuthenticPixels(image,0,y,image->columns,1,
8411 exception);
8412
cristy4c08aed2011-07-01 19:47:50 +00008413 if (r == (Quantum *) NULL)
glennrpd3371642011-03-22 19:42:23 +00008414 break;
8415
8416 for (x=0; x < (ssize_t) image->columns; x++)
8417 {
cristy4c08aed2011-07-01 19:47:50 +00008418 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8419 LBR04RGB(r);
glennrpd3371642011-03-22 19:42:23 +00008420 r++;
8421 }
glennrpbb4f99d2011-05-22 11:13:17 +00008422
glennrpd3371642011-03-22 19:42:23 +00008423 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8424 break;
8425 }
8426 }
8427
8428 else /* Should not reach this; colormap already exists and
8429 must be <= 256 */
8430 {
8431 if (logging != MagickFalse)
8432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8433 " Quantizing the colormap to 4-4-4");
glennrp8e58efd2011-05-20 12:16:29 +00008434
glennrpd3371642011-03-22 19:42:23 +00008435 for (i=0; i<image_colors; i++)
8436 {
glennrp91d99252011-06-25 14:30:13 +00008437 LBR04PacketRGB(image->colormap[i]);
glennrpd3371642011-03-22 19:42:23 +00008438 }
8439 }
8440 continue;
8441 }
8442
glennrp82b3c532011-03-22 19:20:54 +00008443 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
8444 {
8445 if (logging != MagickFalse)
8446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8447 " Quantizing the background color to 3-3-3");
8448
8449 tried_333 = MagickTrue;
8450
glennrp91d99252011-06-25 14:30:13 +00008451 LBR03PacketRGB(image->background_color);
glennrp82b3c532011-03-22 19:20:54 +00008452
8453 if (logging != MagickFalse)
8454 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008455 " Quantizing the pixel colors to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008456
8457 if (image->colormap == NULL)
8458 {
8459 for (y=0; y < (ssize_t) image->rows; y++)
8460 {
8461 r=GetAuthenticPixels(image,0,y,image->columns,1,
8462 exception);
8463
cristy4c08aed2011-07-01 19:47:50 +00008464 if (r == (Quantum *) NULL)
glennrp82b3c532011-03-22 19:20:54 +00008465 break;
8466
8467 for (x=0; x < (ssize_t) image->columns; x++)
8468 {
cristy4c08aed2011-07-01 19:47:50 +00008469 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8470 LBR03RGB(r);
glennrp82b3c532011-03-22 19:20:54 +00008471 r++;
8472 }
glennrpbb4f99d2011-05-22 11:13:17 +00008473
glennrp82b3c532011-03-22 19:20:54 +00008474 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8475 break;
8476 }
8477 }
8478
8479 else /* Should not reach this; colormap already exists and
8480 must be <= 256 */
8481 {
8482 if (logging != MagickFalse)
8483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008484 " Quantizing the colormap to 3-3-3-1");
glennrp82b3c532011-03-22 19:20:54 +00008485 for (i=0; i<image_colors; i++)
8486 {
glennrp91d99252011-06-25 14:30:13 +00008487 LBR03PacketRGB(image->colormap[i]);
glennrp82b3c532011-03-22 19:20:54 +00008488 }
glennrpd3371642011-03-22 19:42:23 +00008489 }
8490 continue;
glennrp82b3c532011-03-22 19:20:54 +00008491 }
glennrpc8c2f062011-02-25 19:00:33 +00008492
glennrp8ca51ad2011-05-12 21:22:32 +00008493 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
glennrpc8c2f062011-02-25 19:00:33 +00008494 {
8495 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8c2f062011-02-25 19:00:33 +00008497 " Quantizing the background color to 3-3-2");
glennrpfd05d622011-02-25 04:10:33 +00008498
glennrp8ca51ad2011-05-12 21:22:32 +00008499 tried_332 = MagickTrue;
8500
glennrp3faa9a32011-04-23 14:00:25 +00008501 /* Red and green were already done so we only quantize the blue
8502 * channel
8503 */
8504
glennrp91d99252011-06-25 14:30:13 +00008505 LBR02PacketBlue(image->background_color);
glennrpfd05d622011-02-25 04:10:33 +00008506
glennrpc8c2f062011-02-25 19:00:33 +00008507 if (logging != MagickFalse)
8508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008509 " Quantizing the pixel colors to 3-3-2-1");
glennrpfd05d622011-02-25 04:10:33 +00008510
glennrpc8c2f062011-02-25 19:00:33 +00008511 if (image->colormap == NULL)
8512 {
8513 for (y=0; y < (ssize_t) image->rows; y++)
8514 {
8515 r=GetAuthenticPixels(image,0,y,image->columns,1,
8516 exception);
8517
cristy4c08aed2011-07-01 19:47:50 +00008518 if (r == (Quantum *) NULL)
glennrpc8c2f062011-02-25 19:00:33 +00008519 break;
8520
8521 for (x=0; x < (ssize_t) image->columns; x++)
8522 {
cristy4c08aed2011-07-01 19:47:50 +00008523 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8524 LBR02Blue(r);
glennrp52a479c2011-02-26 21:14:38 +00008525 r++;
glennrpc8c2f062011-02-25 19:00:33 +00008526 }
glennrpbb4f99d2011-05-22 11:13:17 +00008527
glennrpc8c2f062011-02-25 19:00:33 +00008528 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8529 break;
8530 }
8531 }
glennrpfd05d622011-02-25 04:10:33 +00008532
glennrpc8c2f062011-02-25 19:00:33 +00008533 else /* Should not reach this; colormap already exists and
8534 must be <= 256 */
8535 {
8536 if (logging != MagickFalse)
glennrpfd05d622011-02-25 04:10:33 +00008537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpe9637cb2011-03-24 16:34:37 +00008538 " Quantizing the colormap to 3-3-2-1");
glennrpc8c2f062011-02-25 19:00:33 +00008539 for (i=0; i<image_colors; i++)
8540 {
glennrp91d99252011-06-25 14:30:13 +00008541 LBR02PacketBlue(image->colormap[i]);
glennrpc8c2f062011-02-25 19:00:33 +00008542 }
8543 }
8544 continue;
8545 }
8546 break;
glennrp8ca51ad2011-05-12 21:22:32 +00008547
8548 if (image_colors == 0 || image_colors > 256)
8549 {
8550 /* Take care of special case with 256 colors + 1 transparent
8551 * color. We don't need to quantize to 2-3-2-1; we only need to
8552 * eliminate one color, so we'll merge the two darkest red
8553 * colors (0x49, 0, 0) -> (0x24, 0, 0).
8554 */
8555 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
8556 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
8557 ScaleQuantumToChar(image->background_color.blue) == 0x00)
8558 {
8559 image->background_color.red=ScaleCharToQuantum(0x24);
8560 }
glennrpbb4f99d2011-05-22 11:13:17 +00008561
glennrp8ca51ad2011-05-12 21:22:32 +00008562 if (image->colormap == NULL)
8563 {
8564 for (y=0; y < (ssize_t) image->rows; y++)
8565 {
8566 r=GetAuthenticPixels(image,0,y,image->columns,1,
8567 exception);
glennrpbb4f99d2011-05-22 11:13:17 +00008568
cristy4c08aed2011-07-01 19:47:50 +00008569 if (r == (Quantum *) NULL)
glennrp8ca51ad2011-05-12 21:22:32 +00008570 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008571
glennrp8ca51ad2011-05-12 21:22:32 +00008572 for (x=0; x < (ssize_t) image->columns; x++)
8573 {
cristy4c08aed2011-07-01 19:47:50 +00008574 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
8575 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
8576 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
8577 GetPixelAlpha(image,r) == OpaqueAlpha)
glennrp8ca51ad2011-05-12 21:22:32 +00008578 {
cristy4c08aed2011-07-01 19:47:50 +00008579 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
glennrp8ca51ad2011-05-12 21:22:32 +00008580 }
cristy4c08aed2011-07-01 19:47:50 +00008581 r+=GetPixelChannels(image);
glennrp8ca51ad2011-05-12 21:22:32 +00008582 }
glennrpbb4f99d2011-05-22 11:13:17 +00008583
glennrp8ca51ad2011-05-12 21:22:32 +00008584 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8585 break;
glennrpbb4f99d2011-05-22 11:13:17 +00008586
glennrp8ca51ad2011-05-12 21:22:32 +00008587 }
8588 }
8589
8590 else
8591 {
8592 for (i=0; i<image_colors; i++)
8593 {
8594 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
8595 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
8596 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
8597 {
8598 image->colormap[i].red=ScaleCharToQuantum(0x24);
8599 }
8600 }
8601 }
8602 }
glennrpd71e86a2011-02-24 01:28:37 +00008603 }
glennrpfd05d622011-02-25 04:10:33 +00008604 /* END OF BUILD_PALETTE */
glennrp3c218112010-11-27 15:31:26 +00008605
glennrpfd05d622011-02-25 04:10:33 +00008606 /* If we are excluding the tRNS chunk and there is transparency,
8607 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
8608 * PNG.
glennrp8d579662011-02-23 02:05:02 +00008609 */
glennrp0e8ea192010-12-24 18:00:33 +00008610 if (mng_info->ping_exclude_tRNS != MagickFalse &&
8611 (number_transparent != 0 || number_semitransparent != 0))
8612 {
glennrpd17915c2011-04-29 14:24:22 +00008613 unsigned int colortype=mng_info->write_png_colortype;
glennrp0e8ea192010-12-24 18:00:33 +00008614
8615 if (ping_have_color == MagickFalse)
8616 mng_info->write_png_colortype = 5;
8617
8618 else
8619 mng_info->write_png_colortype = 7;
8620
glennrp8d579662011-02-23 02:05:02 +00008621 if (colortype != 0 &&
glennrpd17915c2011-04-29 14:24:22 +00008622 mng_info->write_png_colortype != colortype)
glennrp0e8ea192010-12-24 18:00:33 +00008623 ping_need_colortype_warning=MagickTrue;
glennrp0b206f52011-01-07 04:55:32 +00008624
glennrp0e8ea192010-12-24 18:00:33 +00008625 }
8626
glennrpfd05d622011-02-25 04:10:33 +00008627 /* See if cheap transparency is possible. It is only possible
8628 * when there is a single transparent color, no semitransparent
8629 * color, and no opaque color that has the same RGB components
glennrp5a39f372011-02-25 04:52:16 +00008630 * as the transparent color. We only need this information if
8631 * we are writing a PNG with colortype 0 or 2, and we have not
8632 * excluded the tRNS chunk.
glennrpfd05d622011-02-25 04:10:33 +00008633 */
glennrp5a39f372011-02-25 04:52:16 +00008634 if (number_transparent == 1 &&
8635 mng_info->write_png_colortype < 4)
glennrpfd05d622011-02-25 04:10:33 +00008636 {
8637 ping_have_cheap_transparency = MagickTrue;
8638
8639 if (number_semitransparent != 0)
8640 ping_have_cheap_transparency = MagickFalse;
8641
8642 else if (image_colors == 0 || image_colors > 256 ||
8643 image->colormap == NULL)
8644 {
8645 ExceptionInfo
8646 *exception;
8647
cristy4c08aed2011-07-01 19:47:50 +00008648 register const Quantum
glennrpfd05d622011-02-25 04:10:33 +00008649 *q;
8650
8651 exception=(&image->exception);
8652
8653 for (y=0; y < (ssize_t) image->rows; y++)
8654 {
8655 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
8656
cristy4c08aed2011-07-01 19:47:50 +00008657 if (q == (const Quantum *) NULL)
glennrpfd05d622011-02-25 04:10:33 +00008658 break;
8659
8660 for (x=0; x < (ssize_t) image->columns; x++)
8661 {
cristy4c08aed2011-07-01 19:47:50 +00008662 if (GetPixelAlpha(image,q) != TransparentAlpha &&
8663 (unsigned short) GetPixelRed(image,q) == ping_trans_color.red &&
8664 (unsigned short) GetPixelGreen(image,q) == ping_trans_color.green &&
8665 (unsigned short) GetPixelBlue(image,q) == ping_trans_color.blue)
glennrpfd05d622011-02-25 04:10:33 +00008666 {
8667 ping_have_cheap_transparency = MagickFalse;
8668 break;
8669 }
8670
cristy4c08aed2011-07-01 19:47:50 +00008671 q+=GetPixelChannels(image);
glennrpfd05d622011-02-25 04:10:33 +00008672 }
glennrpbb4f99d2011-05-22 11:13:17 +00008673
glennrpfd05d622011-02-25 04:10:33 +00008674 if (ping_have_cheap_transparency == MagickFalse)
8675 break;
8676 }
8677 }
8678 else
8679 {
glennrp67b9c1a2011-04-22 18:47:36 +00008680 /* Assuming that image->colormap[0] is the one transparent color
8681 * and that all others are opaque.
8682 */
glennrpfd05d622011-02-25 04:10:33 +00008683 if (image_colors > 1)
glennrp67b9c1a2011-04-22 18:47:36 +00008684 for (i=1; i<image_colors; i++)
8685 if (image->colormap[i].red == image->colormap[0].red &&
8686 image->colormap[i].green == image->colormap[0].green &&
8687 image->colormap[i].blue == image->colormap[0].blue)
glennrpfd05d622011-02-25 04:10:33 +00008688 {
glennrp67b9c1a2011-04-22 18:47:36 +00008689 ping_have_cheap_transparency = MagickFalse;
8690 break;
glennrpfd05d622011-02-25 04:10:33 +00008691 }
8692 }
glennrpbb4f99d2011-05-22 11:13:17 +00008693
glennrpfd05d622011-02-25 04:10:33 +00008694 if (logging != MagickFalse)
8695 {
8696 if (ping_have_cheap_transparency == MagickFalse)
8697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8698 " Cheap transparency is not possible.");
8699
8700 else
8701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8702 " Cheap transparency is possible.");
8703 }
8704 }
8705 else
8706 ping_have_cheap_transparency = MagickFalse;
8707
glennrp8640fb52010-11-23 15:48:26 +00008708 image_depth=image->depth;
8709
glennrp26c990a2010-11-23 02:23:20 +00008710 quantum_info = (QuantumInfo *) NULL;
8711 number_colors=0;
glennrpf09bded2011-01-08 01:15:59 +00008712 image_colors=(int) image->colors;
glennrp26c990a2010-11-23 02:23:20 +00008713 image_matte=image->matte;
8714
glennrp0fe50b42010-11-16 03:52:51 +00008715 mng_info->IsPalette=image->storage_class == PseudoClass &&
glennrp1273f7b2011-02-24 03:20:30 +00008716 image_colors <= 256 && image->colormap != NULL;
cristy3ed852e2009-09-05 21:47:34 +00008717
glennrp52a479c2011-02-26 21:14:38 +00008718 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
8719 (image->colors == 0 || image->colormap == NULL))
8720 {
glennrp52a479c2011-02-26 21:14:38 +00008721 image_info=DestroyImageInfo(image_info);
8722 image=DestroyImage(image);
glennrp15e01552011-03-06 23:29:17 +00008723 (void) ThrowMagickException(&IMimage->exception,
8724 GetMagickModule(),CoderError,
8725 "Cannot write PNG8 or color-type 3; colormap is NULL",
8726 "`%s'",IMimage->filename);
glennrp52a479c2011-02-26 21:14:38 +00008727#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
8728 UnlockSemaphoreInfo(ping_semaphore);
8729#endif
8730 return(MagickFalse);
8731 }
8732
cristy3ed852e2009-09-05 21:47:34 +00008733 /*
8734 Allocate the PNG structures
8735 */
8736#ifdef PNG_USER_MEM_SUPPORTED
8737 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008738 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
8739 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
glennrp0fe50b42010-11-16 03:52:51 +00008740
cristy3ed852e2009-09-05 21:47:34 +00008741#else
8742 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,image,
glennrpcf002022011-01-30 02:38:15 +00008743 MagickPNGErrorHandler,MagickPNGWarningHandler);
glennrp0fe50b42010-11-16 03:52:51 +00008744
cristy3ed852e2009-09-05 21:47:34 +00008745#endif
8746 if (ping == (png_struct *) NULL)
8747 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +00008748
cristy3ed852e2009-09-05 21:47:34 +00008749 ping_info=png_create_info_struct(ping);
glennrp0fe50b42010-11-16 03:52:51 +00008750
cristy3ed852e2009-09-05 21:47:34 +00008751 if (ping_info == (png_info *) NULL)
8752 {
8753 png_destroy_write_struct(&ping,(png_info **) NULL);
8754 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
8755 }
glennrp0fe50b42010-11-16 03:52:51 +00008756
cristy3ed852e2009-09-05 21:47:34 +00008757 png_set_write_fn(ping,image,png_put_data,png_flush_data);
glennrpcf002022011-01-30 02:38:15 +00008758 ping_pixels=(unsigned char *) NULL;
cristy3ed852e2009-09-05 21:47:34 +00008759
glennrp5af765f2010-03-30 11:12:18 +00008760 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +00008761 {
8762 /*
8763 PNG write failed.
8764 */
8765#ifdef PNG_DEBUG
8766 if (image_info->verbose)
8767 (void) printf("PNG write has failed.\n");
8768#endif
8769 png_destroy_write_struct(&ping,&ping_info);
8770#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +00008771 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +00008772#endif
glennrpda8f3a72011-02-27 23:54:12 +00008773 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +00008774 (void) CloseBlob(image);
8775 image_info=DestroyImageInfo(image_info);
8776 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +00008777 return(MagickFalse);
8778 }
8779 /*
8780 Prepare PNG for writing.
8781 */
8782#if defined(PNG_MNG_FEATURES_SUPPORTED)
8783 if (mng_info->write_mng)
8784 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
glennrp2b013e42010-11-24 16:55:50 +00008785
cristy3ed852e2009-09-05 21:47:34 +00008786#else
8787# ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
8788 if (mng_info->write_mng)
8789 png_permit_empty_plte(ping,MagickTrue);
glennrp2b013e42010-11-24 16:55:50 +00008790
cristy3ed852e2009-09-05 21:47:34 +00008791# endif
8792#endif
glennrp2b013e42010-11-24 16:55:50 +00008793
cristy3ed852e2009-09-05 21:47:34 +00008794 x=0;
glennrp2b013e42010-11-24 16:55:50 +00008795
cristy4e5bc842010-06-09 13:56:01 +00008796 ping_width=(png_uint_32) image->columns;
8797 ping_height=(png_uint_32) image->rows;
glennrp2b013e42010-11-24 16:55:50 +00008798
cristy3ed852e2009-09-05 21:47:34 +00008799 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
8800 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008801
cristy3ed852e2009-09-05 21:47:34 +00008802 if (mng_info->write_png_depth != 0)
8803 image_depth=mng_info->write_png_depth;
glennrp0fe50b42010-11-16 03:52:51 +00008804
cristy3ed852e2009-09-05 21:47:34 +00008805 /* Adjust requested depth to next higher valid depth if necessary */
8806 if (image_depth > 8)
8807 image_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00008808
cristy3ed852e2009-09-05 21:47:34 +00008809 if ((image_depth > 4) && (image_depth < 8))
8810 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00008811
cristy3ed852e2009-09-05 21:47:34 +00008812 if (image_depth == 3)
8813 image_depth=4;
glennrp0fe50b42010-11-16 03:52:51 +00008814
cristy3ed852e2009-09-05 21:47:34 +00008815 if (logging != MagickFalse)
8816 {
8817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008818 " width=%.20g",(double) ping_width);
cristy3ed852e2009-09-05 21:47:34 +00008819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008820 " height=%.20g",(double) ping_height);
cristy3ed852e2009-09-05 21:47:34 +00008821 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00008822 " image_matte=%.20g",(double) image->matte);
cristy3ed852e2009-09-05 21:47:34 +00008823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008824 " image->depth=%.20g",(double) image->depth);
cristy3ed852e2009-09-05 21:47:34 +00008825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00008826 " Tentative ping_bit_depth=%.20g",(double) image_depth);
cristy3ed852e2009-09-05 21:47:34 +00008827 }
glennrp8640fb52010-11-23 15:48:26 +00008828
cristy3ed852e2009-09-05 21:47:34 +00008829 save_image_depth=image_depth;
glennrp5af765f2010-03-30 11:12:18 +00008830 ping_bit_depth=(png_byte) save_image_depth;
glennrpdfd70802010-11-14 01:23:35 +00008831
glennrp26f37912010-12-23 16:22:42 +00008832
cristy3ed852e2009-09-05 21:47:34 +00008833#if defined(PNG_pHYs_SUPPORTED)
glennrp26f37912010-12-23 16:22:42 +00008834 if (ping_exclude_pHYs == MagickFalse)
8835 {
cristy3ed852e2009-09-05 21:47:34 +00008836 if ((image->x_resolution != 0) && (image->y_resolution != 0) &&
8837 (!mng_info->write_mng || !mng_info->equal_physs))
8838 {
glennrp0fe50b42010-11-16 03:52:51 +00008839 if (logging != MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00008840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8841 " Setting up pHYs chunk");
cristy3ed852e2009-09-05 21:47:34 +00008842
8843 if (image->units == PixelsPerInchResolution)
8844 {
glennrpdfd70802010-11-14 01:23:35 +00008845 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008846 ping_pHYs_x_resolution=
8847 (png_uint_32) ((100.0*image->x_resolution+0.5)/2.54);
8848 ping_pHYs_y_resolution=
8849 (png_uint_32) ((100.0*image->y_resolution+0.5)/2.54);
cristy3ed852e2009-09-05 21:47:34 +00008850 }
glennrpdfd70802010-11-14 01:23:35 +00008851
cristy3ed852e2009-09-05 21:47:34 +00008852 else if (image->units == PixelsPerCentimeterResolution)
8853 {
glennrpdfd70802010-11-14 01:23:35 +00008854 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
glennrp823b55c2011-03-14 18:46:46 +00008855 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->x_resolution+0.5);
8856 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->y_resolution+0.5);
cristy3ed852e2009-09-05 21:47:34 +00008857 }
glennrp991d11d2010-11-12 21:55:28 +00008858
cristy3ed852e2009-09-05 21:47:34 +00008859 else
8860 {
glennrpdfd70802010-11-14 01:23:35 +00008861 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
8862 ping_pHYs_x_resolution=(png_uint_32) image->x_resolution;
8863 ping_pHYs_y_resolution=(png_uint_32) image->y_resolution;
cristy3ed852e2009-09-05 21:47:34 +00008864 }
glennrp991d11d2010-11-12 21:55:28 +00008865
glennrp823b55c2011-03-14 18:46:46 +00008866 if (logging != MagickFalse)
8867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8868 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
8869 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
8870 (int) ping_pHYs_unit_type);
glennrp991d11d2010-11-12 21:55:28 +00008871 ping_have_pHYs = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00008872 }
glennrp26f37912010-12-23 16:22:42 +00008873 }
cristy3ed852e2009-09-05 21:47:34 +00008874#endif
glennrpa521b2f2010-10-29 04:11:03 +00008875
glennrp26f37912010-12-23 16:22:42 +00008876 if (ping_exclude_bKGD == MagickFalse)
8877 {
glennrpa521b2f2010-10-29 04:11:03 +00008878 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
cristy3ed852e2009-09-05 21:47:34 +00008879 {
glennrpa521b2f2010-10-29 04:11:03 +00008880 unsigned int
8881 mask;
cristy3ed852e2009-09-05 21:47:34 +00008882
glennrpa521b2f2010-10-29 04:11:03 +00008883 mask=0xffff;
8884 if (ping_bit_depth == 8)
8885 mask=0x00ff;
glennrp0fe50b42010-11-16 03:52:51 +00008886
glennrpa521b2f2010-10-29 04:11:03 +00008887 if (ping_bit_depth == 4)
8888 mask=0x000f;
glennrp0fe50b42010-11-16 03:52:51 +00008889
glennrpa521b2f2010-10-29 04:11:03 +00008890 if (ping_bit_depth == 2)
8891 mask=0x0003;
glennrp0fe50b42010-11-16 03:52:51 +00008892
glennrpa521b2f2010-10-29 04:11:03 +00008893 if (ping_bit_depth == 1)
8894 mask=0x0001;
glennrp0fe50b42010-11-16 03:52:51 +00008895
glennrpa521b2f2010-10-29 04:11:03 +00008896 ping_background.red=(png_uint_16)
8897 (ScaleQuantumToShort(image->background_color.red) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008898
glennrpa521b2f2010-10-29 04:11:03 +00008899 ping_background.green=(png_uint_16)
8900 (ScaleQuantumToShort(image->background_color.green) & mask);
glennrp0fe50b42010-11-16 03:52:51 +00008901
glennrpa521b2f2010-10-29 04:11:03 +00008902 ping_background.blue=(png_uint_16)
8903 (ScaleQuantumToShort(image->background_color.blue) & mask);
glennrpc6c391a2011-04-27 02:23:56 +00008904
8905 ping_background.gray=(png_uint_16) ping_background.green;
glennrp0fe50b42010-11-16 03:52:51 +00008906 }
cristy3ed852e2009-09-05 21:47:34 +00008907
glennrp0fe50b42010-11-16 03:52:51 +00008908 if (logging != MagickFalse)
glennrp3b51f0e2010-11-27 18:14:08 +00008909 {
8910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8911 " Setting up bKGD chunk (1)");
glennrpc6c391a2011-04-27 02:23:56 +00008912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8913 " background_color index is %d",
8914 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00008915
8916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8917 " ping_bit_depth=%d",ping_bit_depth);
8918 }
glennrp0fe50b42010-11-16 03:52:51 +00008919
8920 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00008921 }
glennrp0fe50b42010-11-16 03:52:51 +00008922
cristy3ed852e2009-09-05 21:47:34 +00008923 /*
8924 Select the color type.
8925 */
8926 matte=image_matte;
8927 old_bit_depth=0;
glennrp0fe50b42010-11-16 03:52:51 +00008928
glennrp1273f7b2011-02-24 03:20:30 +00008929 if (mng_info->IsPalette && mng_info->write_png8)
cristy3ed852e2009-09-05 21:47:34 +00008930 {
glennrp0fe50b42010-11-16 03:52:51 +00008931
glennrpfd05d622011-02-25 04:10:33 +00008932 /* To do: make this a function cause it's used twice, except
glennrp0fe50b42010-11-16 03:52:51 +00008933 for reducing the sample depth from 8. */
8934
glennrp0fe50b42010-11-16 03:52:51 +00008935 number_colors=image_colors;
glennrp8bb3a022010-12-13 20:40:04 +00008936
glennrp8bb3a022010-12-13 20:40:04 +00008937 ping_have_tRNS=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00008938
8939 /*
8940 Set image palette.
8941 */
8942 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
8943
glennrp0fe50b42010-11-16 03:52:51 +00008944 if (logging != MagickFalse)
8945 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8946 " Setting up PLTE chunk with %d colors (%d)",
glennrpf09bded2011-01-08 01:15:59 +00008947 number_colors, image_colors);
glennrp0fe50b42010-11-16 03:52:51 +00008948
8949 for (i=0; i < (ssize_t) number_colors; i++)
8950 {
8951 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
8952 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
8953 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
8954 if (logging != MagickFalse)
8955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp67b9c1a2011-04-22 18:47:36 +00008956#if MAGICKCORE_QUANTUM_DEPTH == 8
glennrp0fe50b42010-11-16 03:52:51 +00008957 " %3ld (%3d,%3d,%3d)",
glennrp67b9c1a2011-04-22 18:47:36 +00008958#else
8959 " %5ld (%5d,%5d,%5d)",
8960#endif
glennrp0fe50b42010-11-16 03:52:51 +00008961 (long) i,palette[i].red,palette[i].green,palette[i].blue);
8962
8963 }
glennrp2b013e42010-11-24 16:55:50 +00008964
glennrp8bb3a022010-12-13 20:40:04 +00008965 ping_have_PLTE=MagickTrue;
8966 image_depth=ping_bit_depth;
8967 ping_num_trans=0;
glennrp2b013e42010-11-24 16:55:50 +00008968
glennrp58e01762011-01-07 15:28:54 +00008969 if (matte != MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00008970 {
glennrp0fe50b42010-11-16 03:52:51 +00008971 /*
8972 Identify which colormap entry is transparent.
8973 */
8974 assert(number_colors <= 256);
glennrp8bb3a022010-12-13 20:40:04 +00008975 assert(image->colormap != NULL);
glennrp0fe50b42010-11-16 03:52:51 +00008976
glennrp8bb3a022010-12-13 20:40:04 +00008977 for (i=0; i < (ssize_t) number_transparent; i++)
8978 ping_trans_alpha[i]=0;
glennrp0fe50b42010-11-16 03:52:51 +00008979
glennrp0fe50b42010-11-16 03:52:51 +00008980
glennrp2cc891a2010-12-24 13:44:32 +00008981 ping_num_trans=(unsigned short) (number_transparent +
glennrp8bb3a022010-12-13 20:40:04 +00008982 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00008983
8984 if (ping_num_trans == 0)
8985 ping_have_tRNS=MagickFalse;
8986
glennrp8bb3a022010-12-13 20:40:04 +00008987 else
8988 ping_have_tRNS=MagickTrue;
8989 }
glennrp0fe50b42010-11-16 03:52:51 +00008990
glennrp1273f7b2011-02-24 03:20:30 +00008991 if (ping_exclude_bKGD == MagickFalse)
glennrp4f25bd02011-01-01 18:51:28 +00008992 {
glennrp1273f7b2011-02-24 03:20:30 +00008993 /*
8994 * Identify which colormap entry is the background color.
8995 */
8996
glennrp4f25bd02011-01-01 18:51:28 +00008997 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
8998 if (IsPNGColorEqual(ping_background,image->colormap[i]))
8999 break;
glennrp0fe50b42010-11-16 03:52:51 +00009000
glennrp4f25bd02011-01-01 18:51:28 +00009001 ping_background.index=(png_byte) i;
glennrpc6c391a2011-04-27 02:23:56 +00009002
9003 if (logging != MagickFalse)
9004 {
9005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9006 " background_color index is %d",
9007 (int) ping_background.index);
9008 }
glennrp4f25bd02011-01-01 18:51:28 +00009009 }
cristy3ed852e2009-09-05 21:47:34 +00009010 } /* end of write_png8 */
glennrp0fe50b42010-11-16 03:52:51 +00009011
cristy3ed852e2009-09-05 21:47:34 +00009012 else if (mng_info->write_png24)
9013 {
9014 image_matte=MagickFalse;
glennrp5af765f2010-03-30 11:12:18 +00009015 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009016 }
glennrp0fe50b42010-11-16 03:52:51 +00009017
cristy3ed852e2009-09-05 21:47:34 +00009018 else if (mng_info->write_png32)
9019 {
9020 image_matte=MagickTrue;
glennrp5af765f2010-03-30 11:12:18 +00009021 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009022 }
glennrp0fe50b42010-11-16 03:52:51 +00009023
glennrp8bb3a022010-12-13 20:40:04 +00009024 else /* mng_info->write_pngNN not specified */
cristy3ed852e2009-09-05 21:47:34 +00009025 {
glennrp5af765f2010-03-30 11:12:18 +00009026 image_depth=ping_bit_depth;
glennrp0fe50b42010-11-16 03:52:51 +00009027
glennrp8bb3a022010-12-13 20:40:04 +00009028 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009029 {
glennrp5af765f2010-03-30 11:12:18 +00009030 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
glennrp0fe50b42010-11-16 03:52:51 +00009031
glennrp5af765f2010-03-30 11:12:18 +00009032 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9033 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009034 image_matte=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +00009035
glennrp8bb3a022010-12-13 20:40:04 +00009036 else
9037 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009038
9039 if (logging != MagickFalse)
9040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9041 " PNG colortype %d was specified:",(int) ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009042 }
glennrp0fe50b42010-11-16 03:52:51 +00009043
glennrp7c4c9e62011-03-21 20:23:32 +00009044 else /* write_png_colortype not specified */
cristy3ed852e2009-09-05 21:47:34 +00009045 {
9046 if (logging != MagickFalse)
9047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009048 " Selecting PNG colortype:");
glennrp0fe50b42010-11-16 03:52:51 +00009049
glennrpd6bf1612010-12-17 17:28:54 +00009050 ping_color_type=(png_byte) ((matte != MagickFalse)?
glennrp8bb3a022010-12-13 20:40:04 +00009051 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
glennrp0fe50b42010-11-16 03:52:51 +00009052
glennrpd6bf1612010-12-17 17:28:54 +00009053 if (image_info->type == TrueColorType)
cristy3ed852e2009-09-05 21:47:34 +00009054 {
glennrp5af765f2010-03-30 11:12:18 +00009055 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
cristy3ed852e2009-09-05 21:47:34 +00009056 image_matte=MagickFalse;
9057 }
glennrp0fe50b42010-11-16 03:52:51 +00009058
glennrpd6bf1612010-12-17 17:28:54 +00009059 if (image_info->type == TrueColorMatteType)
cristy3ed852e2009-09-05 21:47:34 +00009060 {
glennrp5af765f2010-03-30 11:12:18 +00009061 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009062 image_matte=MagickTrue;
9063 }
glennrp0fe50b42010-11-16 03:52:51 +00009064
glennrp5aa37f62011-01-02 03:07:57 +00009065 if (image_info->type == PaletteType ||
9066 image_info->type == PaletteMatteType)
9067 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9068
glennrp7c4c9e62011-03-21 20:23:32 +00009069 if (mng_info->write_png_colortype == 0 &&
9070 (image_info->type == UndefinedType ||
9071 image_info->type == OptimizeType))
cristy3ed852e2009-09-05 21:47:34 +00009072 {
glennrp5aa37f62011-01-02 03:07:57 +00009073 if (ping_have_color == MagickFalse)
glennrp8bb3a022010-12-13 20:40:04 +00009074 {
glennrp5aa37f62011-01-02 03:07:57 +00009075 if (image_matte == MagickFalse)
9076 {
9077 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9078 image_matte=MagickFalse;
9079 }
glennrp0fe50b42010-11-16 03:52:51 +00009080
glennrp0b206f52011-01-07 04:55:32 +00009081 else
glennrp5aa37f62011-01-02 03:07:57 +00009082 {
9083 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9084 image_matte=MagickTrue;
9085 }
9086 }
9087 else
glennrp8bb3a022010-12-13 20:40:04 +00009088 {
glennrp5aa37f62011-01-02 03:07:57 +00009089 if (image_matte == MagickFalse)
9090 {
9091 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9092 image_matte=MagickFalse;
9093 }
glennrp8bb3a022010-12-13 20:40:04 +00009094
glennrp0b206f52011-01-07 04:55:32 +00009095 else
glennrp5aa37f62011-01-02 03:07:57 +00009096 {
9097 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9098 image_matte=MagickTrue;
9099 }
9100 }
glennrp0fe50b42010-11-16 03:52:51 +00009101 }
glennrp5aa37f62011-01-02 03:07:57 +00009102
cristy3ed852e2009-09-05 21:47:34 +00009103 }
glennrp0fe50b42010-11-16 03:52:51 +00009104
cristy3ed852e2009-09-05 21:47:34 +00009105 if (logging != MagickFalse)
9106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8640fb52010-11-23 15:48:26 +00009107 " Selected PNG colortype=%d",ping_color_type);
glennrp26c990a2010-11-23 02:23:20 +00009108
glennrp5af765f2010-03-30 11:12:18 +00009109 if (ping_bit_depth < 8)
glennrp0fe50b42010-11-16 03:52:51 +00009110 {
9111 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9112 ping_color_type == PNG_COLOR_TYPE_RGB ||
9113 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9114 ping_bit_depth=8;
9115 }
cristy3ed852e2009-09-05 21:47:34 +00009116
glennrpd6bf1612010-12-17 17:28:54 +00009117 old_bit_depth=ping_bit_depth;
9118
glennrp5af765f2010-03-30 11:12:18 +00009119 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +00009120 {
glennrp8d579662011-02-23 02:05:02 +00009121 if (image->matte == MagickFalse && ping_have_non_bw == MagickFalse)
9122 ping_bit_depth=1;
cristy3ed852e2009-09-05 21:47:34 +00009123 }
glennrp8640fb52010-11-23 15:48:26 +00009124
glennrp5af765f2010-03-30 11:12:18 +00009125 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009126 {
cristy35ef8242010-06-03 16:24:13 +00009127 size_t one = 1;
glennrp5af765f2010-03-30 11:12:18 +00009128 ping_bit_depth=1;
glennrp0f111982010-07-07 20:18:33 +00009129
9130 if (image->colors == 0)
9131 {
glennrp0fe50b42010-11-16 03:52:51 +00009132 /* DO SOMETHING */
glennrpc70af4a2011-03-07 00:08:23 +00009133 (void) ThrowMagickException(&image->exception,
glennrp0f111982010-07-07 20:18:33 +00009134 GetMagickModule(),CoderError,
glennrpc70af4a2011-03-07 00:08:23 +00009135 "image has 0 colors", "`%s'","");
glennrp0f111982010-07-07 20:18:33 +00009136 }
9137
cristy35ef8242010-06-03 16:24:13 +00009138 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009139 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009140 }
glennrp2b013e42010-11-24 16:55:50 +00009141
glennrpd6bf1612010-12-17 17:28:54 +00009142 if (logging != MagickFalse)
9143 {
9144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9145 " Number of colors: %.20g",(double) image_colors);
9146
9147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9148 " Tentative PNG bit depth: %d",ping_bit_depth);
9149 }
9150
9151 if (ping_bit_depth < (int) mng_info->write_png_depth)
9152 ping_bit_depth = mng_info->write_png_depth;
9153 }
glennrp2cc891a2010-12-24 13:44:32 +00009154
glennrp5af765f2010-03-30 11:12:18 +00009155 image_depth=ping_bit_depth;
glennrp2b013e42010-11-24 16:55:50 +00009156
cristy3ed852e2009-09-05 21:47:34 +00009157 if (logging != MagickFalse)
9158 {
9159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009160 " Tentative PNG color type: %.20g",(double) ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +00009161
cristy3ed852e2009-09-05 21:47:34 +00009162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009163 " image_info->type: %.20g",(double) image_info->type);
glennrp0fe50b42010-11-16 03:52:51 +00009164
cristy3ed852e2009-09-05 21:47:34 +00009165 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009166 " image_depth: %.20g",(double) image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009167
cristy3ed852e2009-09-05 21:47:34 +00009168 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009169
glennrp8640fb52010-11-23 15:48:26 +00009170 " image->depth: %.20g",(double) image->depth);
9171
9172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +00009173 " ping_bit_depth: %.20g",(double) ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009174 }
9175
glennrp58e01762011-01-07 15:28:54 +00009176 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009177 {
glennrp4f25bd02011-01-01 18:51:28 +00009178 if (mng_info->IsPalette)
cristy3ed852e2009-09-05 21:47:34 +00009179 {
glennrp7c4c9e62011-03-21 20:23:32 +00009180 if (mng_info->write_png_colortype == 0)
9181 {
9182 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp4f25bd02011-01-01 18:51:28 +00009183
glennrp7c4c9e62011-03-21 20:23:32 +00009184 if (ping_have_color != MagickFalse)
9185 ping_color_type=PNG_COLOR_TYPE_RGBA;
9186 }
glennrp4f25bd02011-01-01 18:51:28 +00009187
9188 /*
9189 * Determine if there is any transparent color.
9190 */
9191 if (number_transparent + number_semitransparent == 0)
9192 {
9193 /*
9194 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9195 */
glennrpa6a06632011-01-19 15:15:34 +00009196
glennrp4f25bd02011-01-01 18:51:28 +00009197 image_matte=MagickFalse;
glennrp7c4c9e62011-03-21 20:23:32 +00009198
9199 if (mng_info->write_png_colortype == 0)
9200 ping_color_type&=0x03;
glennrp4f25bd02011-01-01 18:51:28 +00009201 }
9202
9203 else
9204 {
9205 unsigned int
glennrpbb4f99d2011-05-22 11:13:17 +00009206 mask;
glennrp4f25bd02011-01-01 18:51:28 +00009207
9208 mask=0xffff;
9209
9210 if (ping_bit_depth == 8)
9211 mask=0x00ff;
9212
9213 if (ping_bit_depth == 4)
9214 mask=0x000f;
9215
9216 if (ping_bit_depth == 2)
9217 mask=0x0003;
9218
9219 if (ping_bit_depth == 1)
9220 mask=0x0001;
9221
9222 ping_trans_color.red=(png_uint_16)
9223 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9224
9225 ping_trans_color.green=(png_uint_16)
9226 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9227
9228 ping_trans_color.blue=(png_uint_16)
9229 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9230
9231 ping_trans_color.gray=(png_uint_16)
cristy4c08aed2011-07-01 19:47:50 +00009232 (ScaleQuantumToShort(GetPixelPacketIntensity(
glennrp4f25bd02011-01-01 18:51:28 +00009233 image->colormap)) & mask);
9234
9235 ping_trans_color.index=(png_byte) 0;
9236
9237 ping_have_tRNS=MagickTrue;
9238 }
9239
9240 if (ping_have_tRNS != MagickFalse)
9241 {
9242 /*
glennrpfd05d622011-02-25 04:10:33 +00009243 * Determine if there is one and only one transparent color
9244 * and if so if it is fully transparent.
9245 */
9246 if (ping_have_cheap_transparency == MagickFalse)
9247 ping_have_tRNS=MagickFalse;
glennrp4f25bd02011-01-01 18:51:28 +00009248 }
9249
9250 if (ping_have_tRNS != MagickFalse)
9251 {
glennrp7c4c9e62011-03-21 20:23:32 +00009252 if (mng_info->write_png_colortype == 0)
9253 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
glennrp4f25bd02011-01-01 18:51:28 +00009254
9255 if (image_depth == 8)
9256 {
9257 ping_trans_color.red&=0xff;
9258 ping_trans_color.green&=0xff;
9259 ping_trans_color.blue&=0xff;
9260 ping_trans_color.gray&=0xff;
9261 }
9262 }
9263 }
cristy3ed852e2009-09-05 21:47:34 +00009264 else
9265 {
cristy3ed852e2009-09-05 21:47:34 +00009266 if (image_depth == 8)
9267 {
glennrp5af765f2010-03-30 11:12:18 +00009268 ping_trans_color.red&=0xff;
9269 ping_trans_color.green&=0xff;
9270 ping_trans_color.blue&=0xff;
9271 ping_trans_color.gray&=0xff;
cristy3ed852e2009-09-05 21:47:34 +00009272 }
9273 }
9274 }
glennrp8640fb52010-11-23 15:48:26 +00009275
cristy3ed852e2009-09-05 21:47:34 +00009276 matte=image_matte;
glennrp0fe50b42010-11-16 03:52:51 +00009277
glennrp2e09f552010-11-14 00:38:48 +00009278 if (ping_have_tRNS != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009279 image_matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +00009280
glennrp39992b42010-11-14 00:03:43 +00009281 if ((mng_info->IsPalette) &&
cristy3ed852e2009-09-05 21:47:34 +00009282 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
glennrp8d579662011-02-23 02:05:02 +00009283 ping_have_color == MagickFalse &&
9284 (image_matte == MagickFalse || image_depth >= 8))
cristy3ed852e2009-09-05 21:47:34 +00009285 {
cristy35ef8242010-06-03 16:24:13 +00009286 size_t one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009287
cristy3ed852e2009-09-05 21:47:34 +00009288 if (image_matte != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009289 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
glennrp0fe50b42010-11-16 03:52:51 +00009290
glennrp7c4c9e62011-03-21 20:23:32 +00009291 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
cristy3ed852e2009-09-05 21:47:34 +00009292 {
glennrp5af765f2010-03-30 11:12:18 +00009293 ping_color_type=PNG_COLOR_TYPE_GRAY;
glennrp4f25bd02011-01-01 18:51:28 +00009294
cristy3ed852e2009-09-05 21:47:34 +00009295 if (save_image_depth == 16 && image_depth == 8)
glennrp4f25bd02011-01-01 18:51:28 +00009296 {
9297 if (logging != MagickFalse)
9298 {
9299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9300 " Scaling ping_trans_color (0)");
9301 }
9302 ping_trans_color.gray*=0x0101;
9303 }
cristy3ed852e2009-09-05 21:47:34 +00009304 }
glennrp0fe50b42010-11-16 03:52:51 +00009305
cristy3ed852e2009-09-05 21:47:34 +00009306 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9307 image_depth=MAGICKCORE_QUANTUM_DEPTH;
glennrp0fe50b42010-11-16 03:52:51 +00009308
glennrp136ee3a2011-04-27 15:47:45 +00009309 if ((image_colors == 0) ||
glennrpd17915c2011-04-29 14:24:22 +00009310 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
glennrpf09bded2011-01-08 01:15:59 +00009311 image_colors=(int) (one << image_depth);
glennrp0fe50b42010-11-16 03:52:51 +00009312
cristy3ed852e2009-09-05 21:47:34 +00009313 if (image_depth > 8)
glennrp5af765f2010-03-30 11:12:18 +00009314 ping_bit_depth=16;
glennrp0fe50b42010-11-16 03:52:51 +00009315
cristy3ed852e2009-09-05 21:47:34 +00009316 else
9317 {
glennrp5af765f2010-03-30 11:12:18 +00009318 ping_bit_depth=8;
9319 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
cristy3ed852e2009-09-05 21:47:34 +00009320 {
9321 if(!mng_info->write_png_depth)
9322 {
glennrp5af765f2010-03-30 11:12:18 +00009323 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009324
cristy35ef8242010-06-03 16:24:13 +00009325 while ((int) (one << ping_bit_depth)
cristybb503372010-05-27 20:51:26 +00009326 < (ssize_t) image_colors)
glennrp5af765f2010-03-30 11:12:18 +00009327 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009328 }
9329 }
glennrp2b013e42010-11-24 16:55:50 +00009330
glennrp0fe50b42010-11-16 03:52:51 +00009331 else if (ping_color_type ==
9332 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
cristy3ed852e2009-09-05 21:47:34 +00009333 mng_info->IsPalette)
9334 {
cristy3ed852e2009-09-05 21:47:34 +00009335 /* Check if grayscale is reducible */
glennrp1a0aaa62011-03-07 17:40:17 +00009336
cristy3ed852e2009-09-05 21:47:34 +00009337 int
9338 depth_4_ok=MagickTrue,
9339 depth_2_ok=MagickTrue,
9340 depth_1_ok=MagickTrue;
9341
cristybb503372010-05-27 20:51:26 +00009342 for (i=0; i < (ssize_t) image_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009343 {
9344 unsigned char
9345 intensity;
9346
9347 intensity=ScaleQuantumToChar(image->colormap[i].red);
9348
9349 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9350 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9351 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9352 depth_2_ok=depth_1_ok=MagickFalse;
glennrp4bf89732011-03-21 13:48:28 +00009353 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
cristy3ed852e2009-09-05 21:47:34 +00009354 depth_1_ok=MagickFalse;
9355 }
glennrp2b013e42010-11-24 16:55:50 +00009356
cristy3ed852e2009-09-05 21:47:34 +00009357 if (depth_1_ok && mng_info->write_png_depth <= 1)
glennrp9c1eb072010-06-06 22:19:15 +00009358 ping_bit_depth=1;
glennrp0fe50b42010-11-16 03:52:51 +00009359
cristy3ed852e2009-09-05 21:47:34 +00009360 else if (depth_2_ok && mng_info->write_png_depth <= 2)
glennrp9c1eb072010-06-06 22:19:15 +00009361 ping_bit_depth=2;
glennrp0fe50b42010-11-16 03:52:51 +00009362
cristy3ed852e2009-09-05 21:47:34 +00009363 else if (depth_4_ok && mng_info->write_png_depth <= 4)
glennrp9c1eb072010-06-06 22:19:15 +00009364 ping_bit_depth=4;
cristy3ed852e2009-09-05 21:47:34 +00009365 }
9366 }
glennrp2b013e42010-11-24 16:55:50 +00009367
glennrp5af765f2010-03-30 11:12:18 +00009368 image_depth=ping_bit_depth;
cristy3ed852e2009-09-05 21:47:34 +00009369 }
glennrp0fe50b42010-11-16 03:52:51 +00009370
cristy3ed852e2009-09-05 21:47:34 +00009371 else
glennrp0fe50b42010-11-16 03:52:51 +00009372
cristy3ed852e2009-09-05 21:47:34 +00009373 if (mng_info->IsPalette)
9374 {
glennrp17a14852010-05-10 03:01:59 +00009375 number_colors=image_colors;
9376
cristy3ed852e2009-09-05 21:47:34 +00009377 if (image_depth <= 8)
9378 {
cristy3ed852e2009-09-05 21:47:34 +00009379 /*
9380 Set image palette.
9381 */
glennrp5af765f2010-03-30 11:12:18 +00009382 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
glennrp0fe50b42010-11-16 03:52:51 +00009383
glennrp58e01762011-01-07 15:28:54 +00009384 if (mng_info->have_write_global_plte && matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009385 {
glennrp9c1eb072010-06-06 22:19:15 +00009386 png_set_PLTE(ping,ping_info,NULL,0);
glennrp0fe50b42010-11-16 03:52:51 +00009387
glennrp3b51f0e2010-11-27 18:14:08 +00009388 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +00009389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9390 " Setting up empty PLTE chunk");
cristy3ed852e2009-09-05 21:47:34 +00009391 }
glennrp0fe50b42010-11-16 03:52:51 +00009392
cristy3ed852e2009-09-05 21:47:34 +00009393 else
9394 {
cristybb503372010-05-27 20:51:26 +00009395 for (i=0; i < (ssize_t) number_colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00009396 {
9397 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9398 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9399 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9400 }
glennrp0fe50b42010-11-16 03:52:51 +00009401
glennrp3b51f0e2010-11-27 18:14:08 +00009402 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp98156a32009-12-09 15:32:44 +00009404 " Setting up PLTE chunk with %d colors",
glennrpf09bded2011-01-08 01:15:59 +00009405 number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009406
glennrp39992b42010-11-14 00:03:43 +00009407 ping_have_PLTE=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +00009408 }
glennrp0fe50b42010-11-16 03:52:51 +00009409
cristy3ed852e2009-09-05 21:47:34 +00009410 /* color_type is PNG_COLOR_TYPE_PALETTE */
glennrpd6bf1612010-12-17 17:28:54 +00009411 if (mng_info->write_png_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00009412 {
cristybefe4d22010-06-07 01:18:58 +00009413 size_t
9414 one;
9415
glennrp5af765f2010-03-30 11:12:18 +00009416 ping_bit_depth=1;
cristybefe4d22010-06-07 01:18:58 +00009417 one=1;
glennrp0fe50b42010-11-16 03:52:51 +00009418
glennrpd17915c2011-04-29 14:24:22 +00009419 while ((one << ping_bit_depth) < (ssize_t) number_colors)
glennrp5af765f2010-03-30 11:12:18 +00009420 ping_bit_depth <<= 1;
cristy3ed852e2009-09-05 21:47:34 +00009421 }
glennrp0fe50b42010-11-16 03:52:51 +00009422
glennrp5af765f2010-03-30 11:12:18 +00009423 ping_num_trans=0;
glennrp0fe50b42010-11-16 03:52:51 +00009424
glennrp58e01762011-01-07 15:28:54 +00009425 if (matte != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009426 {
glennrp0fe50b42010-11-16 03:52:51 +00009427 /*
glennrpd6bf1612010-12-17 17:28:54 +00009428 * Set up trans_colors array.
9429 */
glennrp0fe50b42010-11-16 03:52:51 +00009430 assert(number_colors <= 256);
9431
glennrpd6bf1612010-12-17 17:28:54 +00009432 ping_num_trans=(unsigned short) (number_transparent +
9433 number_semitransparent);
glennrp0fe50b42010-11-16 03:52:51 +00009434
9435 if (ping_num_trans == 0)
9436 ping_have_tRNS=MagickFalse;
9437
glennrpd6bf1612010-12-17 17:28:54 +00009438 else
glennrp0fe50b42010-11-16 03:52:51 +00009439 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009440 if (logging != MagickFalse)
9441 {
9442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9443 " Scaling ping_trans_color (1)");
9444 }
glennrpd6bf1612010-12-17 17:28:54 +00009445 ping_have_tRNS=MagickTrue;
9446
9447 for (i=0; i < ping_num_trans; i++)
9448 {
cristy4c08aed2011-07-01 19:47:50 +00009449 ping_trans_alpha[i]= (png_byte)
9450 ScaleQuantumToChar(image->colormap[i].alpha);
glennrpd6bf1612010-12-17 17:28:54 +00009451 }
glennrp0fe50b42010-11-16 03:52:51 +00009452 }
9453 }
cristy3ed852e2009-09-05 21:47:34 +00009454 }
9455 }
glennrp0fe50b42010-11-16 03:52:51 +00009456
cristy3ed852e2009-09-05 21:47:34 +00009457 else
9458 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009459
cristy3ed852e2009-09-05 21:47:34 +00009460 if (image_depth < 8)
9461 image_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +00009462
cristy3ed852e2009-09-05 21:47:34 +00009463 if ((save_image_depth == 16) && (image_depth == 8))
9464 {
glennrp4f25bd02011-01-01 18:51:28 +00009465 if (logging != MagickFalse)
9466 {
9467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9468 " Scaling ping_trans_color from (%d,%d,%d)",
9469 (int) ping_trans_color.red,
9470 (int) ping_trans_color.green,
9471 (int) ping_trans_color.blue);
9472 }
9473
glennrp5af765f2010-03-30 11:12:18 +00009474 ping_trans_color.red*=0x0101;
9475 ping_trans_color.green*=0x0101;
9476 ping_trans_color.blue*=0x0101;
9477 ping_trans_color.gray*=0x0101;
glennrp4f25bd02011-01-01 18:51:28 +00009478
9479 if (logging != MagickFalse)
9480 {
9481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9482 " to (%d,%d,%d)",
9483 (int) ping_trans_color.red,
9484 (int) ping_trans_color.green,
9485 (int) ping_trans_color.blue);
9486 }
cristy3ed852e2009-09-05 21:47:34 +00009487 }
9488 }
9489
cristy4383ec82011-01-05 15:42:32 +00009490 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
9491 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
glennrp2cc891a2010-12-24 13:44:32 +00009492
cristy3ed852e2009-09-05 21:47:34 +00009493 /*
9494 Adjust background and transparency samples in sub-8-bit grayscale files.
9495 */
glennrp5af765f2010-03-30 11:12:18 +00009496 if (ping_bit_depth < 8 && ping_color_type ==
cristy3ed852e2009-09-05 21:47:34 +00009497 PNG_COLOR_TYPE_GRAY)
9498 {
9499 png_uint_16
9500 maxval;
9501
cristy35ef8242010-06-03 16:24:13 +00009502 size_t
9503 one=1;
9504
cristy22ffd972010-06-03 16:51:47 +00009505 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
cristy3ed852e2009-09-05 21:47:34 +00009506
glennrp4f25bd02011-01-01 18:51:28 +00009507 if (ping_exclude_bKGD == MagickFalse)
glennrp26f37912010-12-23 16:22:42 +00009508 {
cristy3ed852e2009-09-05 21:47:34 +00009509
glennrpa521b2f2010-10-29 04:11:03 +00009510 ping_background.gray=(png_uint_16)
cristy4c08aed2011-07-01 19:47:50 +00009511 ((maxval/255.)*((GetPixelPacketIntensity(&image->background_color)))+.5);
cristy3ed852e2009-09-05 21:47:34 +00009512
9513 if (logging != MagickFalse)
9514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp3c218112010-11-27 15:31:26 +00009515 " Setting up bKGD chunk (2)");
glennrpc6c391a2011-04-27 02:23:56 +00009516 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9517 " background_color index is %d",
9518 (int) ping_background.index);
glennrp3b51f0e2010-11-27 18:14:08 +00009519
glennrp991d11d2010-11-12 21:55:28 +00009520 ping_have_bKGD = MagickTrue;
glennrp26f37912010-12-23 16:22:42 +00009521 }
cristy3ed852e2009-09-05 21:47:34 +00009522
glennrp3e3e20f2011-06-09 04:21:43 +00009523 if (logging != MagickFalse)
9524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9525 " Scaling ping_trans_color.gray from %d",
9526 (int)ping_trans_color.gray);
9527
glennrp9be9b1c2011-06-09 12:21:45 +00009528 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
glennrp3e3e20f2011-06-09 04:21:43 +00009529 ping_trans_color.gray)+.5);
9530
9531 if (logging != MagickFalse)
9532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9533 " to %d", (int)ping_trans_color.gray);
cristy3ed852e2009-09-05 21:47:34 +00009534 }
glennrp17a14852010-05-10 03:01:59 +00009535
glennrp26f37912010-12-23 16:22:42 +00009536 if (ping_exclude_bKGD == MagickFalse)
9537 {
glennrp1273f7b2011-02-24 03:20:30 +00009538 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
glennrp17a14852010-05-10 03:01:59 +00009539 {
9540 /*
9541 Identify which colormap entry is the background color.
9542 */
9543
glennrp17a14852010-05-10 03:01:59 +00009544 number_colors=image_colors;
9545
glennrpa521b2f2010-10-29 04:11:03 +00009546 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
9547 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
glennrp17a14852010-05-10 03:01:59 +00009548 break;
9549
9550 ping_background.index=(png_byte) i;
9551
glennrp3b51f0e2010-11-27 18:14:08 +00009552 if (logging != MagickFalse)
glennrpa521b2f2010-10-29 04:11:03 +00009553 {
9554 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp0fe50b42010-11-16 03:52:51 +00009555 " Setting up bKGD chunk with index=%d",(int) i);
glennrpa521b2f2010-10-29 04:11:03 +00009556 }
glennrp0fe50b42010-11-16 03:52:51 +00009557
cristy13d07042010-11-21 20:56:18 +00009558 if (i < (ssize_t) number_colors)
glennrp0fe50b42010-11-16 03:52:51 +00009559 {
9560 ping_have_bKGD = MagickTrue;
glennrp3b51f0e2010-11-27 18:14:08 +00009561
9562 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009563 {
9564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9565 " background =(%d,%d,%d)",
9566 (int) ping_background.red,
9567 (int) ping_background.green,
9568 (int) ping_background.blue);
9569 }
9570 }
glennrpa521b2f2010-10-29 04:11:03 +00009571
glennrpd6bf1612010-12-17 17:28:54 +00009572 else /* Can't happen */
glennrp3c218112010-11-27 15:31:26 +00009573 {
glennrp3b51f0e2010-11-27 18:14:08 +00009574 if (logging != MagickFalse)
9575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9576 " No room in PLTE to add bKGD color");
glennrp3c218112010-11-27 15:31:26 +00009577 ping_have_bKGD = MagickFalse;
9578 }
glennrp17a14852010-05-10 03:01:59 +00009579 }
glennrp26f37912010-12-23 16:22:42 +00009580 }
glennrp17a14852010-05-10 03:01:59 +00009581
cristy3ed852e2009-09-05 21:47:34 +00009582 if (logging != MagickFalse)
9583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +00009584 " PNG color type: %d",ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009585 /*
9586 Initialize compression level and filtering.
9587 */
9588 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +00009589 {
9590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9591 " Setting up deflate compression");
9592
9593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9594 " Compression buffer size: 32768");
9595 }
9596
cristy3ed852e2009-09-05 21:47:34 +00009597 png_set_compression_buffer_size(ping,32768L);
glennrp0fe50b42010-11-16 03:52:51 +00009598
cristy3ed852e2009-09-05 21:47:34 +00009599 if (logging != MagickFalse)
9600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9601 " Compression mem level: 9");
glennrp0fe50b42010-11-16 03:52:51 +00009602
glennrp10d739e2011-06-29 18:00:52 +00009603 /* Untangle the "-quality" setting:
9604
9605 Undefined is 0; the default is used.
9606 Default is 75
9607
9608 10's digit:
9609
9610 0: Use Z_HUFFMAN_ONLY strategy with the
9611 zlib default compression level
9612
9613 1-9: the zlib compression level
9614
9615 1's digit:
9616
9617 0-4: the PNG filter method
9618
9619 5: libpng adaptive filtering if compression level > 5
9620 libpng filter type "none" if compression level <= 5
9621 or if image is grayscale or palette
9622
9623 6: libpng adaptive filtering
9624
9625 7: "LOCO" filtering (intrapixel differing) if writing
9626 a MNG, othewise "none". Did not work in IM-6.7.0-9
9627 and earlier because of a missing "else".
9628
9629 8: Z_RLE strategy, all filters
glennrp18682582011-06-30 18:11:47 +00009630 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009631
9632 9: Z_RLE strategy, no PNG filters
glennrp18682582011-06-30 18:11:47 +00009633 Unused prior to IM-6.7.0-10, was same as 6
glennrp10d739e2011-06-29 18:00:52 +00009634
9635 Note that using the -quality option, not all combinations of
9636 PNG filter type, zlib compression level, and zlib compression
9637 strategy are possible. This will be addressed soon in a
9638 release that accomodates "-define PNG:compression-strategy",
9639 etc.
9640
9641 */
9642
cristy3ed852e2009-09-05 21:47:34 +00009643 quality=image->quality == UndefinedCompressionQuality ? 75UL :
9644 image->quality;
glennrp0fe50b42010-11-16 03:52:51 +00009645
glennrp18682582011-06-30 18:11:47 +00009646 if (quality <= 9)
9647 {
9648 if (mng_info->write_png_compression_strategy == 0)
9649 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
9650 }
9651
9652 else if (mng_info->write_png_compression_level == 0)
cristy3ed852e2009-09-05 21:47:34 +00009653 {
9654 int
9655 level;
9656
cristybb503372010-05-27 20:51:26 +00009657 level=(int) MagickMin((ssize_t) quality/10,9);
glennrp0fe50b42010-11-16 03:52:51 +00009658
glennrp18682582011-06-30 18:11:47 +00009659 mng_info->write_png_compression_level = level+1;
cristy3ed852e2009-09-05 21:47:34 +00009660 }
glennrp0fe50b42010-11-16 03:52:51 +00009661
glennrp18682582011-06-30 18:11:47 +00009662 if (mng_info->write_png_compression_strategy == 0)
cristy3ed852e2009-09-05 21:47:34 +00009663 {
glennrp18682582011-06-30 18:11:47 +00009664 if ((quality %10) == 8 || (quality %10) == 9)
9665 mng_info->write_png_compression_strategy=Z_RLE;
cristy3ed852e2009-09-05 21:47:34 +00009666 }
glennrp0fe50b42010-11-16 03:52:51 +00009667
glennrp18682582011-06-30 18:11:47 +00009668 if (mng_info->write_png_compression_filter == 0)
9669 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
9670
cristy3ed852e2009-09-05 21:47:34 +00009671 if (logging != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009672 {
glennrp18682582011-06-30 18:11:47 +00009673 if (mng_info->write_png_compression_level)
9674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9675 " Compression level: %d",
9676 (int) mng_info->write_png_compression_level-1);
glennrp0fe50b42010-11-16 03:52:51 +00009677
glennrp18682582011-06-30 18:11:47 +00009678 if (mng_info->write_png_compression_strategy)
9679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9680 " Compression strategy: %d",
9681 (int) mng_info->write_png_compression_strategy-1);
glennrp0fe50b42010-11-16 03:52:51 +00009682
glennrp18682582011-06-30 18:11:47 +00009683 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9684 " Setting up filtering");
cristy3ed852e2009-09-05 21:47:34 +00009685
glennrp18682582011-06-30 18:11:47 +00009686 if (mng_info->write_png_compression_filter == PNG_ALL_FILTERS+1)
cristy3ed852e2009-09-05 21:47:34 +00009687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9688 " Base filter method: ADAPTIVE");
glennrp18682582011-06-30 18:11:47 +00009689 else if (mng_info->write_png_compression_filter == PNG_NO_FILTERS+1)
cristy3ed852e2009-09-05 21:47:34 +00009690 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9691 " Base filter method: NONE");
glennrp18682582011-06-30 18:11:47 +00009692 else
9693 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9694 " Base filter method: %d",
9695 (int) mng_info->write_png_compression_filter-1);
9696 }
glennrp2b013e42010-11-24 16:55:50 +00009697
glennrp18682582011-06-30 18:11:47 +00009698 if (mng_info->write_png_compression_level != 0)
9699 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
9700
9701 if (mng_info->write_png_compression_filter == 6)
9702 {
9703 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
9704 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
9705 (quality < 50))
9706 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9707 else
9708 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9709 }
9710
9711 if (mng_info->write_png_compression_filter == 7 ||
9712 mng_info->write_png_compression_filter == 10)
9713 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
9714
9715 else if (mng_info->write_png_compression_filter == 8)
9716 {
9717#if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
9718 if (mng_info->write_mng)
9719 {
9720 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
9721 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
9722 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
9723 }
9724#endif
9725 png_set_filter(ping,PNG_FILTER_TYPE_BASE,0);
9726 }
9727
9728 else if (mng_info->write_png_compression_filter == 9)
9729 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
9730
9731 else if (mng_info->write_png_compression_filter != 0)
9732 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
9733 mng_info->write_png_compression_filter-1);
9734
9735 if (mng_info->write_png_compression_strategy != 0)
9736 png_set_compression_strategy(ping,
9737 mng_info->write_png_compression_strategy-1);
9738
cristy3ed852e2009-09-05 21:47:34 +00009739
glennrp823b55c2011-03-14 18:46:46 +00009740 if ((ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse) &&
9741 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
glennrpc8cbc5d2011-01-01 00:12:34 +00009742 {
9743 ResetImageProfileIterator(image);
9744 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
cristy3ed852e2009-09-05 21:47:34 +00009745 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009746 profile=GetImageProfile(image,name);
9747
9748 if (profile != (StringInfo *) NULL)
9749 {
glennrp5af765f2010-03-30 11:12:18 +00009750#ifdef PNG_WRITE_iCCP_SUPPORTED
glennrpc8cbc5d2011-01-01 00:12:34 +00009751 if ((LocaleCompare(name,"ICC") == 0) ||
9752 (LocaleCompare(name,"ICM") == 0))
glennrp26f37912010-12-23 16:22:42 +00009753 {
glennrpc8cbc5d2011-01-01 00:12:34 +00009754
9755 if (ping_exclude_iCCP == MagickFalse)
9756 {
9757 png_set_iCCP(ping,ping_info,(const png_charp) name,0,
glennrpe4017e32011-01-08 17:16:09 +00009758#if (PNG_LIBPNG_VER < 10500)
glennrpc8cbc5d2011-01-01 00:12:34 +00009759 (png_charp) GetStringInfoDatum(profile),
glennrpe4017e32011-01-08 17:16:09 +00009760#else
9761 (png_const_bytep) GetStringInfoDatum(profile),
9762#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009763 (png_uint_32) GetStringInfoLength(profile));
9764 }
glennrp26f37912010-12-23 16:22:42 +00009765 }
glennrp0fe50b42010-11-16 03:52:51 +00009766
glennrpc8cbc5d2011-01-01 00:12:34 +00009767 else
cristy3ed852e2009-09-05 21:47:34 +00009768#endif
glennrpc8cbc5d2011-01-01 00:12:34 +00009769 if (ping_exclude_zCCP == MagickFalse)
9770 {
glennrpcf002022011-01-30 02:38:15 +00009771 Magick_png_write_raw_profile(image_info,ping,ping_info,
glennrpc8cbc5d2011-01-01 00:12:34 +00009772 (unsigned char *) name,(unsigned char *) name,
9773 GetStringInfoDatum(profile),
9774 (png_uint_32) GetStringInfoLength(profile));
9775 }
9776 }
glennrp0b206f52011-01-07 04:55:32 +00009777
glennrpc8cbc5d2011-01-01 00:12:34 +00009778 if (logging != MagickFalse)
9779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9780 " Setting up text chunk with %s profile",name);
9781
9782 name=GetNextImageProfile(image);
cristy3ed852e2009-09-05 21:47:34 +00009783 }
cristy3ed852e2009-09-05 21:47:34 +00009784 }
9785
9786#if defined(PNG_WRITE_sRGB_SUPPORTED)
9787 if ((mng_info->have_write_global_srgb == 0) &&
9788 ((image->rendering_intent != UndefinedIntent) ||
9789 (image->colorspace == sRGBColorspace)))
9790 {
glennrp26f37912010-12-23 16:22:42 +00009791 if (ping_exclude_sRGB == MagickFalse)
9792 {
9793 /*
9794 Note image rendering intent.
9795 */
9796 if (logging != MagickFalse)
9797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9798 " Setting up sRGB chunk");
glennrp0fe50b42010-11-16 03:52:51 +00009799
glennrp26f37912010-12-23 16:22:42 +00009800 (void) png_set_sRGB(ping,ping_info,(
glennrpcf002022011-01-30 02:38:15 +00009801 Magick_RenderingIntent_to_PNG_RenderingIntent(
9802 image->rendering_intent)));
glennrp0fe50b42010-11-16 03:52:51 +00009803
glennrp26f37912010-12-23 16:22:42 +00009804 if (ping_exclude_gAMA == MagickFalse)
9805 png_set_gAMA(ping,ping_info,0.45455);
9806 }
cristy3ed852e2009-09-05 21:47:34 +00009807 }
glennrp26f37912010-12-23 16:22:42 +00009808
glennrp5af765f2010-03-30 11:12:18 +00009809 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
cristy3ed852e2009-09-05 21:47:34 +00009810#endif
9811 {
glennrp2cc891a2010-12-24 13:44:32 +00009812 if (ping_exclude_gAMA == MagickFalse &&
9813 (ping_exclude_sRGB == MagickFalse ||
glennrp26f37912010-12-23 16:22:42 +00009814 (image->gamma < .45 || image->gamma > .46)))
9815 {
cristy3ed852e2009-09-05 21:47:34 +00009816 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
9817 {
9818 /*
9819 Note image gamma.
9820 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9821 */
9822 if (logging != MagickFalse)
9823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9824 " Setting up gAMA chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009825
cristy3ed852e2009-09-05 21:47:34 +00009826 png_set_gAMA(ping,ping_info,image->gamma);
9827 }
glennrp26f37912010-12-23 16:22:42 +00009828 }
glennrp2b013e42010-11-24 16:55:50 +00009829
glennrp26f37912010-12-23 16:22:42 +00009830 if (ping_exclude_cHRM == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009831 {
glennrp26f37912010-12-23 16:22:42 +00009832 if ((mng_info->have_write_global_chrm == 0) &&
9833 (image->chromaticity.red_primary.x != 0.0))
9834 {
9835 /*
9836 Note image chromaticity.
9837 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
9838 */
9839 PrimaryInfo
9840 bp,
9841 gp,
9842 rp,
9843 wp;
cristy3ed852e2009-09-05 21:47:34 +00009844
glennrp26f37912010-12-23 16:22:42 +00009845 wp=image->chromaticity.white_point;
9846 rp=image->chromaticity.red_primary;
9847 gp=image->chromaticity.green_primary;
9848 bp=image->chromaticity.blue_primary;
cristy3ed852e2009-09-05 21:47:34 +00009849
glennrp26f37912010-12-23 16:22:42 +00009850 if (logging != MagickFalse)
9851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9852 " Setting up cHRM chunk");
glennrp3b51f0e2010-11-27 18:14:08 +00009853
glennrp26f37912010-12-23 16:22:42 +00009854 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
9855 bp.x,bp.y);
9856 }
9857 }
cristy3ed852e2009-09-05 21:47:34 +00009858 }
glennrpdfd70802010-11-14 01:23:35 +00009859
glennrp5af765f2010-03-30 11:12:18 +00009860 ping_interlace_method=image_info->interlace != NoInterlace;
cristy3ed852e2009-09-05 21:47:34 +00009861
9862 if (mng_info->write_mng)
9863 png_set_sig_bytes(ping,8);
9864
9865 /* Bail out if cannot meet defined PNG:bit-depth or PNG:color-type */
9866
glennrpd6bf1612010-12-17 17:28:54 +00009867 if (mng_info->write_png_colortype != 0)
cristy3ed852e2009-09-05 21:47:34 +00009868 {
9869 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
glennrp8d579662011-02-23 02:05:02 +00009870 if (ping_have_color != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009871 {
glennrp5af765f2010-03-30 11:12:18 +00009872 ping_color_type = PNG_COLOR_TYPE_RGB;
glennrp2b013e42010-11-24 16:55:50 +00009873
glennrp5af765f2010-03-30 11:12:18 +00009874 if (ping_bit_depth < 8)
9875 ping_bit_depth=8;
cristy3ed852e2009-09-05 21:47:34 +00009876 }
glennrp0fe50b42010-11-16 03:52:51 +00009877
cristy3ed852e2009-09-05 21:47:34 +00009878 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
glennrp8d579662011-02-23 02:05:02 +00009879 if (ping_have_color != MagickFalse)
glennrp5af765f2010-03-30 11:12:18 +00009880 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
cristy3ed852e2009-09-05 21:47:34 +00009881 }
9882
glennrp0e8ea192010-12-24 18:00:33 +00009883 if (ping_need_colortype_warning != MagickFalse ||
9884 ((mng_info->write_png_depth &&
glennrp5af765f2010-03-30 11:12:18 +00009885 (int) mng_info->write_png_depth != ping_bit_depth) ||
cristy3ed852e2009-09-05 21:47:34 +00009886 (mng_info->write_png_colortype &&
glennrp5af765f2010-03-30 11:12:18 +00009887 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
glennrp991e92a2010-01-28 03:09:00 +00009888 mng_info->write_png_colortype != 7 &&
glennrp0e8ea192010-12-24 18:00:33 +00009889 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
cristy3ed852e2009-09-05 21:47:34 +00009890 {
9891 if (logging != MagickFalse)
9892 {
glennrp0e8ea192010-12-24 18:00:33 +00009893 if (ping_need_colortype_warning != MagickFalse)
9894 {
9895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9896 " Image has transparency but tRNS chunk was excluded");
9897 }
9898
cristy3ed852e2009-09-05 21:47:34 +00009899 if (mng_info->write_png_depth)
9900 {
9901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9902 " Defined PNG:bit-depth=%u, Computed depth=%u",
9903 mng_info->write_png_depth,
glennrp5af765f2010-03-30 11:12:18 +00009904 ping_bit_depth);
cristy3ed852e2009-09-05 21:47:34 +00009905 }
glennrp0e8ea192010-12-24 18:00:33 +00009906
cristy3ed852e2009-09-05 21:47:34 +00009907 if (mng_info->write_png_colortype)
9908 {
9909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9910 " Defined PNG:color-type=%u, Computed color type=%u",
9911 mng_info->write_png_colortype-1,
glennrp5af765f2010-03-30 11:12:18 +00009912 ping_color_type);
cristy3ed852e2009-09-05 21:47:34 +00009913 }
9914 }
glennrp0e8ea192010-12-24 18:00:33 +00009915
glennrp3bd2e412010-08-10 13:34:52 +00009916 png_warning(ping,
cristy3ed852e2009-09-05 21:47:34 +00009917 "Cannot write image with defined PNG:bit-depth or PNG:color-type.");
9918 }
9919
glennrp58e01762011-01-07 15:28:54 +00009920 if (image_matte != MagickFalse && image->matte == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00009921 {
9922 /* Add an opaque matte channel */
9923 image->matte = MagickTrue;
9924 (void) SetImageOpacity(image,0);
glennrp0fe50b42010-11-16 03:52:51 +00009925
glennrpb4a13412010-05-05 12:47:19 +00009926 if (logging != MagickFalse)
9927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9928 " Added an opaque matte channel");
cristy3ed852e2009-09-05 21:47:34 +00009929 }
9930
glennrp0e319732011-01-25 21:53:13 +00009931 if (number_transparent != 0 || number_semitransparent != 0)
glennrpe9c26dc2010-05-30 01:56:35 +00009932 {
glennrp991d11d2010-11-12 21:55:28 +00009933 if (ping_color_type < 4)
glennrpc8cbc5d2011-01-01 00:12:34 +00009934 {
glennrp991d11d2010-11-12 21:55:28 +00009935 ping_have_tRNS=MagickTrue;
glennrpc8cbc5d2011-01-01 00:12:34 +00009936 if (logging != MagickFalse)
9937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9938 " Setting ping_have_tRNS=MagickTrue.");
9939 }
glennrpe9c26dc2010-05-30 01:56:35 +00009940 }
9941
cristy3ed852e2009-09-05 21:47:34 +00009942 if (logging != MagickFalse)
9943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9944 " Writing PNG header chunks");
9945
glennrp5af765f2010-03-30 11:12:18 +00009946 png_set_IHDR(ping,ping_info,ping_width,ping_height,
9947 ping_bit_depth,ping_color_type,
9948 ping_interlace_method,ping_compression_method,
9949 ping_filter_method);
9950
glennrp39992b42010-11-14 00:03:43 +00009951 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
9952 {
glennrpf09bded2011-01-08 01:15:59 +00009953 png_set_PLTE(ping,ping_info,palette,number_colors);
glennrp0fe50b42010-11-16 03:52:51 +00009954
glennrp3b51f0e2010-11-27 18:14:08 +00009955 if (logging != MagickFalse)
glennrp39992b42010-11-14 00:03:43 +00009956 {
glennrp8640fb52010-11-23 15:48:26 +00009957 for (i=0; i< (ssize_t) number_colors; i++)
glennrp0fe50b42010-11-16 03:52:51 +00009958 {
glennrpd6bf1612010-12-17 17:28:54 +00009959 if (i < ping_num_trans)
glennrp0fe50b42010-11-16 03:52:51 +00009960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009961 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
9962 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009963 (int) palette[i].red,
9964 (int) palette[i].green,
9965 (int) palette[i].blue,
glennrpd6bf1612010-12-17 17:28:54 +00009966 (int) i,
glennrp0fe50b42010-11-16 03:52:51 +00009967 (int) ping_trans_alpha[i]);
9968 else
9969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +00009970 " PLTE[%d] = (%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +00009971 (int) i,
9972 (int) palette[i].red,
9973 (int) palette[i].green,
9974 (int) palette[i].blue);
9975 }
glennrp39992b42010-11-14 00:03:43 +00009976 }
glennrp39992b42010-11-14 00:03:43 +00009977 }
9978
glennrp26f37912010-12-23 16:22:42 +00009979 if (ping_exclude_bKGD == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +00009980 {
glennrp26f37912010-12-23 16:22:42 +00009981 if (ping_have_bKGD != MagickFalse)
glennrpc6c391a2011-04-27 02:23:56 +00009982 {
glennrp26f37912010-12-23 16:22:42 +00009983 png_set_bKGD(ping,ping_info,&ping_background);
glennrpc6c391a2011-04-27 02:23:56 +00009984 if (logging)
9985 {
9986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9987 " Setting up bKGD chunk");
9988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9989 " background color = (%d,%d,%d)",
9990 (int) ping_background.red,
9991 (int) ping_background.green,
9992 (int) ping_background.blue);
9993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9994 " index = %d, gray=%d",
9995 (int) ping_background.index,
9996 (int) ping_background.gray);
9997 }
9998 }
glennrp26f37912010-12-23 16:22:42 +00009999 }
10000
10001 if (ping_exclude_pHYs == MagickFalse)
10002 {
10003 if (ping_have_pHYs != MagickFalse)
10004 {
10005 png_set_pHYs(ping,ping_info,
10006 ping_pHYs_x_resolution,
10007 ping_pHYs_y_resolution,
10008 ping_pHYs_unit_type);
glennrp823b55c2011-03-14 18:46:46 +000010009
10010 if (logging)
10011 {
10012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10013 " Setting up pHYs chunk");
10014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10015 " x_resolution=%lu",
10016 (unsigned long) ping_pHYs_x_resolution);
10017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10018 " y_resolution=%lu",
10019 (unsigned long) ping_pHYs_y_resolution);
10020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10021 " unit_type=%lu",
10022 (unsigned long) ping_pHYs_unit_type);
10023 }
glennrp26f37912010-12-23 16:22:42 +000010024 }
glennrpdfd70802010-11-14 01:23:35 +000010025 }
10026
10027#if defined(PNG_oFFs_SUPPORTED)
glennrp4f25bd02011-01-01 18:51:28 +000010028 if (ping_exclude_oFFs == MagickFalse)
glennrpdfd70802010-11-14 01:23:35 +000010029 {
glennrp26f37912010-12-23 16:22:42 +000010030 if (image->page.x || image->page.y)
10031 {
10032 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10033 (png_int_32) image->page.y, 0);
glennrpdfd70802010-11-14 01:23:35 +000010034
glennrp26f37912010-12-23 16:22:42 +000010035 if (logging != MagickFalse)
10036 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10037 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10038 (int) image->page.x, (int) image->page.y);
10039 }
glennrpdfd70802010-11-14 01:23:35 +000010040 }
10041#endif
10042
glennrpda8f3a72011-02-27 23:54:12 +000010043 if (mng_info->need_blob != MagickFalse)
10044 {
10045 if (OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception) ==
10046 MagickFalse)
10047 png_error(ping,"WriteBlob Failed");
10048
10049 ping_have_blob=MagickTrue;
10050 }
10051
cristy3ed852e2009-09-05 21:47:34 +000010052 png_write_info_before_PLTE(ping, ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010053
glennrp39992b42010-11-14 00:03:43 +000010054 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
glennrp991d11d2010-11-12 21:55:28 +000010055 {
glennrp3b51f0e2010-11-27 18:14:08 +000010056 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010057 {
10058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10059 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10060 }
10061
10062 if (ping_color_type == 3)
10063 (void) png_set_tRNS(ping, ping_info,
10064 ping_trans_alpha,
10065 ping_num_trans,
10066 NULL);
10067
10068 else
10069 {
10070 (void) png_set_tRNS(ping, ping_info,
10071 NULL,
10072 0,
10073 &ping_trans_color);
10074
glennrp3b51f0e2010-11-27 18:14:08 +000010075 if (logging != MagickFalse)
glennrp0fe50b42010-11-16 03:52:51 +000010076 {
10077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpc8cbc5d2011-01-01 00:12:34 +000010078 " tRNS color =(%d,%d,%d)",
glennrp0fe50b42010-11-16 03:52:51 +000010079 (int) ping_trans_color.red,
10080 (int) ping_trans_color.green,
10081 (int) ping_trans_color.blue);
10082 }
10083 }
glennrp991d11d2010-11-12 21:55:28 +000010084 }
10085
cristy3ed852e2009-09-05 21:47:34 +000010086 /* write any png-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000010087 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
glennrpda8f3a72011-02-27 23:54:12 +000010088
cristy3ed852e2009-09-05 21:47:34 +000010089 png_write_info(ping,ping_info);
glennrp991d11d2010-11-12 21:55:28 +000010090
cristy3ed852e2009-09-05 21:47:34 +000010091 /* write any PNG-chunk-m profiles */
glennrpcf002022011-01-30 02:38:15 +000010092 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
cristy3ed852e2009-09-05 21:47:34 +000010093
glennrp26f37912010-12-23 16:22:42 +000010094 if (ping_exclude_vpAg == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010095 {
glennrp4f25bd02011-01-01 18:51:28 +000010096 if ((image->page.width != 0 && image->page.width != image->columns) ||
10097 (image->page.height != 0 && image->page.height != image->rows))
glennrp26f37912010-12-23 16:22:42 +000010098 {
10099 unsigned char
10100 chunk[14];
cristy3ed852e2009-09-05 21:47:34 +000010101
glennrp26f37912010-12-23 16:22:42 +000010102 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10103 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000010104 LogPNGChunk(logging,mng_vpAg,9L);
glennrp26f37912010-12-23 16:22:42 +000010105 PNGLong(chunk+4,(png_uint_32) image->page.width);
10106 PNGLong(chunk+8,(png_uint_32) image->page.height);
10107 chunk[12]=0; /* unit = pixels */
10108 (void) WriteBlob(image,13,chunk);
10109 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10110 }
cristy3ed852e2009-09-05 21:47:34 +000010111 }
10112
10113#if (PNG_LIBPNG_VER == 10206)
glennrp9c1eb072010-06-06 22:19:15 +000010114 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
cristy3ed852e2009-09-05 21:47:34 +000010115#define PNG_HAVE_IDAT 0x04
glennrp9c1eb072010-06-06 22:19:15 +000010116 ping->mode |= PNG_HAVE_IDAT;
cristy3ed852e2009-09-05 21:47:34 +000010117#undef PNG_HAVE_IDAT
10118#endif
10119
10120 png_set_packing(ping);
10121 /*
10122 Allocate memory.
10123 */
10124 rowbytes=image->columns;
glennrpb4a13412010-05-05 12:47:19 +000010125 if (image_depth > 8)
10126 rowbytes*=2;
cristy7202c102010-05-05 19:18:28 +000010127 switch (ping_color_type)
cristy3ed852e2009-09-05 21:47:34 +000010128 {
glennrpb4a13412010-05-05 12:47:19 +000010129 case PNG_COLOR_TYPE_RGB:
cristy3ed852e2009-09-05 21:47:34 +000010130 rowbytes*=3;
glennrpb4a13412010-05-05 12:47:19 +000010131 break;
glennrp0fe50b42010-11-16 03:52:51 +000010132
glennrpb4a13412010-05-05 12:47:19 +000010133 case PNG_COLOR_TYPE_GRAY_ALPHA:
10134 rowbytes*=2;
10135 break;
glennrp0fe50b42010-11-16 03:52:51 +000010136
glennrpb4a13412010-05-05 12:47:19 +000010137 case PNG_COLOR_TYPE_RGBA:
cristy3ed852e2009-09-05 21:47:34 +000010138 rowbytes*=4;
glennrpb4a13412010-05-05 12:47:19 +000010139 break;
glennrp0fe50b42010-11-16 03:52:51 +000010140
glennrpb4a13412010-05-05 12:47:19 +000010141 default:
10142 break;
cristy3ed852e2009-09-05 21:47:34 +000010143 }
glennrp3b51f0e2010-11-27 18:14:08 +000010144
10145 if (logging != MagickFalse)
glennrpb4a13412010-05-05 12:47:19 +000010146 {
10147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10148 " Writing PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010149
glennrpb4a13412010-05-05 12:47:19 +000010150 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010151 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
glennrpb4a13412010-05-05 12:47:19 +000010152 }
glennrpcf002022011-01-30 02:38:15 +000010153 ping_pixels=(unsigned char *) AcquireQuantumMemory(rowbytes,
10154 sizeof(*ping_pixels));
glennrp0fe50b42010-11-16 03:52:51 +000010155
glennrpcf002022011-01-30 02:38:15 +000010156 if (ping_pixels == (unsigned char *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010157 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010158
cristy3ed852e2009-09-05 21:47:34 +000010159 /*
10160 Initialize image scanlines.
10161 */
glennrp5af765f2010-03-30 11:12:18 +000010162 if (setjmp(png_jmpbuf(ping)))
cristy3ed852e2009-09-05 21:47:34 +000010163 {
10164 /*
10165 PNG write failed.
10166 */
10167#ifdef PNG_DEBUG
10168 if (image_info->verbose)
10169 (void) printf("PNG write has failed.\n");
10170#endif
10171 png_destroy_write_struct(&ping,&ping_info);
10172 if (quantum_info != (QuantumInfo *) NULL)
10173 quantum_info=DestroyQuantumInfo(quantum_info);
glennrpcf002022011-01-30 02:38:15 +000010174 if (ping_pixels != (unsigned char *) NULL)
10175 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010176#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010177 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010178#endif
glennrpda8f3a72011-02-27 23:54:12 +000010179 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010180 (void) CloseBlob(image);
10181 image_info=DestroyImageInfo(image_info);
10182 image=DestroyImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010183 return(MagickFalse);
10184 }
cristyed552522009-10-16 14:04:35 +000010185 quantum_info=AcquireQuantumInfo(image_info,image);
10186 if (quantum_info == (QuantumInfo *) NULL)
10187 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +000010188 quantum_info->format=UndefinedQuantumFormat;
10189 quantum_info->depth=image_depth;
10190 num_passes=png_set_interlace_handling(ping);
glennrp8bb3a022010-12-13 20:40:04 +000010191
cristy3ed852e2009-09-05 21:47:34 +000010192 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
glennrp8bb3a022010-12-13 20:40:04 +000010193 !mng_info->write_png32) &&
10194 (mng_info->IsPalette ||
cristy3ed852e2009-09-05 21:47:34 +000010195 (image_info->type == BilevelType)) &&
glennrp8d579662011-02-23 02:05:02 +000010196 image_matte == MagickFalse &&
10197 ping_have_non_bw == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010198 {
glennrp8bb3a022010-12-13 20:40:04 +000010199 /* Palette, Bilevel, or Opaque Monochrome */
cristy4c08aed2011-07-01 19:47:50 +000010200 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +000010201 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010202
cristy3ed852e2009-09-05 21:47:34 +000010203 quantum_info->depth=8;
10204 for (pass=0; pass < num_passes; pass++)
10205 {
10206 /*
10207 Convert PseudoClass image to a PNG monochrome image.
10208 */
cristybb503372010-05-27 20:51:26 +000010209 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010210 {
glennrpd71e86a2011-02-24 01:28:37 +000010211 if (logging != MagickFalse && y == 0)
glennrp3b51f0e2010-11-27 18:14:08 +000010212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10213 " Writing row of pixels (0)");
glennrpa521b2f2010-10-29 04:11:03 +000010214
cristy3ed852e2009-09-05 21:47:34 +000010215 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000010216
cristy4c08aed2011-07-01 19:47:50 +000010217 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010218 break;
glennrp0fe50b42010-11-16 03:52:51 +000010219
cristy3ed852e2009-09-05 21:47:34 +000010220 if (mng_info->IsPalette)
10221 {
cristy4c08aed2011-07-01 19:47:50 +000010222 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010223 quantum_info,GrayQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010224 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10225 mng_info->write_png_depth &&
10226 mng_info->write_png_depth != old_bit_depth)
10227 {
10228 /* Undo pixel scaling */
cristybb503372010-05-27 20:51:26 +000010229 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010230 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
cristy3ed852e2009-09-05 21:47:34 +000010231 >> (8-old_bit_depth));
10232 }
10233 }
glennrp0fe50b42010-11-16 03:52:51 +000010234
cristy3ed852e2009-09-05 21:47:34 +000010235 else
10236 {
cristy4c08aed2011-07-01 19:47:50 +000010237 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010238 quantum_info,RedQuantum,ping_pixels,&image->exception);
cristy3ed852e2009-09-05 21:47:34 +000010239 }
glennrp0fe50b42010-11-16 03:52:51 +000010240
cristy3ed852e2009-09-05 21:47:34 +000010241 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
cristybb503372010-05-27 20:51:26 +000010242 for (i=0; i < (ssize_t) image->columns; i++)
glennrpcf002022011-01-30 02:38:15 +000010243 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
cristy3ed852e2009-09-05 21:47:34 +000010244 255 : 0);
glennrp0fe50b42010-11-16 03:52:51 +000010245
glennrp3b51f0e2010-11-27 18:14:08 +000010246 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10248 " Writing row of pixels (1)");
glennrp0fe50b42010-11-16 03:52:51 +000010249
glennrpcf002022011-01-30 02:38:15 +000010250 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010251 }
10252 if (image->previous == (Image *) NULL)
10253 {
10254 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10255 if (status == MagickFalse)
10256 break;
10257 }
10258 }
10259 }
glennrp0fe50b42010-11-16 03:52:51 +000010260
glennrp8bb3a022010-12-13 20:40:04 +000010261 else /* Not Palette, Bilevel, or Opaque Monochrome */
cristy3ed852e2009-09-05 21:47:34 +000010262 {
glennrp0fe50b42010-11-16 03:52:51 +000010263 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
cristy3ed852e2009-09-05 21:47:34 +000010264 !mng_info->write_png32) &&
glennrp58e01762011-01-07 15:28:54 +000010265 (image_matte != MagickFalse ||
glennrp5af765f2010-03-30 11:12:18 +000010266 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
glennrp8d579662011-02-23 02:05:02 +000010267 (mng_info->IsPalette) && ping_have_color == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010268 {
cristy4c08aed2011-07-01 19:47:50 +000010269 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010270 *p;
glennrp0fe50b42010-11-16 03:52:51 +000010271
glennrp8bb3a022010-12-13 20:40:04 +000010272 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010273 {
glennrp8bb3a022010-12-13 20:40:04 +000010274
cristybb503372010-05-27 20:51:26 +000010275 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +000010276 {
10277 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010278
cristy4c08aed2011-07-01 19:47:50 +000010279 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010280 break;
glennrp2cc891a2010-12-24 13:44:32 +000010281
glennrp5af765f2010-03-30 11:12:18 +000010282 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
cristy3ed852e2009-09-05 21:47:34 +000010283 {
glennrp8bb3a022010-12-13 20:40:04 +000010284 if (mng_info->IsPalette)
cristy4c08aed2011-07-01 19:47:50 +000010285 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010286 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010287
glennrp8bb3a022010-12-13 20:40:04 +000010288 else
cristy4c08aed2011-07-01 19:47:50 +000010289 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010290 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010291
glennrp3b51f0e2010-11-27 18:14:08 +000010292 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010294 " Writing GRAY PNG pixels (2)");
glennrpb4a13412010-05-05 12:47:19 +000010295 }
glennrp2cc891a2010-12-24 13:44:32 +000010296
glennrp8bb3a022010-12-13 20:40:04 +000010297 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10298 {
10299 if (logging != MagickFalse && y == 0)
10300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10301 " Writing GRAY_ALPHA PNG pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010302
cristy4c08aed2011-07-01 19:47:50 +000010303 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010304 quantum_info,GrayAlphaQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010305 }
glennrp2cc891a2010-12-24 13:44:32 +000010306
glennrp3b51f0e2010-11-27 18:14:08 +000010307 if (logging != MagickFalse && y == 0)
glennrpb4a13412010-05-05 12:47:19 +000010308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp8bb3a022010-12-13 20:40:04 +000010309 " Writing row of pixels (2)");
glennrp2cc891a2010-12-24 13:44:32 +000010310
glennrpcf002022011-01-30 02:38:15 +000010311 png_write_row(ping,ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010312 }
glennrp2cc891a2010-12-24 13:44:32 +000010313
glennrp8bb3a022010-12-13 20:40:04 +000010314 if (image->previous == (Image *) NULL)
cristy3ed852e2009-09-05 21:47:34 +000010315 {
glennrp8bb3a022010-12-13 20:40:04 +000010316 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10317 if (status == MagickFalse)
10318 break;
cristy3ed852e2009-09-05 21:47:34 +000010319 }
cristy3ed852e2009-09-05 21:47:34 +000010320 }
10321 }
glennrp8bb3a022010-12-13 20:40:04 +000010322
10323 else
10324 {
cristy4c08aed2011-07-01 19:47:50 +000010325 register const Quantum
glennrp8bb3a022010-12-13 20:40:04 +000010326 *p;
10327
10328 for (pass=0; pass < num_passes; pass++)
cristy3ed852e2009-09-05 21:47:34 +000010329 {
glennrp8bb3a022010-12-13 20:40:04 +000010330 if ((image_depth > 8) || (mng_info->write_png24 ||
10331 mng_info->write_png32 ||
10332 (!mng_info->write_png8 && !mng_info->IsPalette)))
10333 {
10334 for (y=0; y < (ssize_t) image->rows; y++)
10335 {
10336 p=GetVirtualPixels(image,0,y,image->columns,1,
10337 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010338
cristy4c08aed2011-07-01 19:47:50 +000010339 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010340 break;
glennrp2cc891a2010-12-24 13:44:32 +000010341
glennrp8bb3a022010-12-13 20:40:04 +000010342 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10343 {
10344 if (image->storage_class == DirectClass)
cristy4c08aed2011-07-01 19:47:50 +000010345 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010346 quantum_info,RedQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010347
glennrp8bb3a022010-12-13 20:40:04 +000010348 else
cristy4c08aed2011-07-01 19:47:50 +000010349 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010350 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010351 }
glennrp2cc891a2010-12-24 13:44:32 +000010352
glennrp8bb3a022010-12-13 20:40:04 +000010353 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10354 {
cristy4c08aed2011-07-01 19:47:50 +000010355 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010356 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrp8bb3a022010-12-13 20:40:04 +000010357 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010358
glennrp8bb3a022010-12-13 20:40:04 +000010359 if (logging != MagickFalse && y == 0)
10360 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10361 " Writing GRAY_ALPHA PNG pixels (3)");
10362 }
glennrp2cc891a2010-12-24 13:44:32 +000010363
glennrp8bb3a022010-12-13 20:40:04 +000010364 else if (image_matte != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +000010365 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010366 quantum_info,RGBAQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010367
glennrp8bb3a022010-12-13 20:40:04 +000010368 else
cristy4c08aed2011-07-01 19:47:50 +000010369 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010370 quantum_info,RGBQuantum,ping_pixels,&image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010371
glennrp8bb3a022010-12-13 20:40:04 +000010372 if (logging != MagickFalse && y == 0)
10373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10374 " Writing row of pixels (3)");
glennrp2cc891a2010-12-24 13:44:32 +000010375
glennrpcf002022011-01-30 02:38:15 +000010376 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010377 }
10378 }
glennrp2cc891a2010-12-24 13:44:32 +000010379
glennrp8bb3a022010-12-13 20:40:04 +000010380 else
10381 /* not ((image_depth > 8) || (mng_info->write_png24 ||
10382 mng_info->write_png32 ||
10383 (!mng_info->write_png8 && !mng_info->IsPalette))) */
10384 {
10385 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
10386 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
10387 {
10388 if (logging != MagickFalse)
10389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10390 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010391
glennrp8bb3a022010-12-13 20:40:04 +000010392 quantum_info->depth=8;
10393 image_depth=8;
10394 }
glennrp2cc891a2010-12-24 13:44:32 +000010395
glennrp8bb3a022010-12-13 20:40:04 +000010396 for (y=0; y < (ssize_t) image->rows; y++)
10397 {
10398 if (logging != MagickFalse && y == 0)
10399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10400 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
glennrp2cc891a2010-12-24 13:44:32 +000010401
glennrp770d1932011-03-06 22:11:17 +000010402 p=GetVirtualPixels(image,0,y,image->columns,1,
10403 &image->exception);
glennrp2cc891a2010-12-24 13:44:32 +000010404
cristy4c08aed2011-07-01 19:47:50 +000010405 if (p == (const Quantum *) NULL)
glennrp8bb3a022010-12-13 20:40:04 +000010406 break;
glennrp2cc891a2010-12-24 13:44:32 +000010407
glennrp8bb3a022010-12-13 20:40:04 +000010408 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
glennrp44757ab2011-03-17 12:57:03 +000010409 {
glennrp4bf89732011-03-21 13:48:28 +000010410 quantum_info->depth=image->depth;
10411
cristy4c08aed2011-07-01 19:47:50 +000010412 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010413 quantum_info,GrayQuantum,ping_pixels,&image->exception);
glennrp44757ab2011-03-17 12:57:03 +000010414 }
glennrp2cc891a2010-12-24 13:44:32 +000010415
glennrp8bb3a022010-12-13 20:40:04 +000010416 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10417 {
10418 if (logging != MagickFalse && y == 0)
10419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10420 " Writing GRAY_ALPHA PNG pixels (4)");
glennrp2cc891a2010-12-24 13:44:32 +000010421
cristy4c08aed2011-07-01 19:47:50 +000010422 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrpcf002022011-01-30 02:38:15 +000010423 quantum_info,GrayAlphaQuantum,ping_pixels,
glennrpd6bf1612010-12-17 17:28:54 +000010424 &image->exception);
glennrp8bb3a022010-12-13 20:40:04 +000010425 }
glennrp2cc891a2010-12-24 13:44:32 +000010426
glennrp8bb3a022010-12-13 20:40:04 +000010427 else
glennrp8bb3a022010-12-13 20:40:04 +000010428 {
cristy4c08aed2011-07-01 19:47:50 +000010429 (void) ExportQuantumPixels(image,(CacheView *) NULL,
glennrp5eae7602011-02-22 15:21:32 +000010430 quantum_info,IndexQuantum,ping_pixels,&image->exception);
10431
10432 if (logging != MagickFalse && y <= 2)
10433 {
10434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp1a7d6db2011-03-17 13:24:10 +000010435 " Writing row of non-gray pixels (4)");
glennrp5eae7602011-02-22 15:21:32 +000010436
10437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10438 " ping_pixels[0]=%d,ping_pixels[1]=%d",
10439 (int)ping_pixels[0],(int)ping_pixels[1]);
10440 }
glennrp8bb3a022010-12-13 20:40:04 +000010441 }
glennrpcf002022011-01-30 02:38:15 +000010442 png_write_row(ping,ping_pixels);
glennrp8bb3a022010-12-13 20:40:04 +000010443 }
10444 }
glennrp2cc891a2010-12-24 13:44:32 +000010445
glennrp8bb3a022010-12-13 20:40:04 +000010446 if (image->previous == (Image *) NULL)
10447 {
10448 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10449 if (status == MagickFalse)
10450 break;
10451 }
cristy3ed852e2009-09-05 21:47:34 +000010452 }
glennrp8bb3a022010-12-13 20:40:04 +000010453 }
10454 }
10455
cristyb32b90a2009-09-07 21:45:48 +000010456 if (quantum_info != (QuantumInfo *) NULL)
10457 quantum_info=DestroyQuantumInfo(quantum_info);
cristy3ed852e2009-09-05 21:47:34 +000010458
10459 if (logging != MagickFalse)
10460 {
10461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpb4a13412010-05-05 12:47:19 +000010462 " Wrote PNG image data");
glennrp0fe50b42010-11-16 03:52:51 +000010463
cristy3ed852e2009-09-05 21:47:34 +000010464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010465 " Width: %.20g",(double) ping_width);
glennrp0fe50b42010-11-16 03:52:51 +000010466
cristy3ed852e2009-09-05 21:47:34 +000010467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000010468 " Height: %.20g",(double) ping_height);
glennrp0fe50b42010-11-16 03:52:51 +000010469
cristy3ed852e2009-09-05 21:47:34 +000010470 if (mng_info->write_png_depth)
10471 {
10472 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10473 " Defined PNG:bit-depth: %d",mng_info->write_png_depth);
10474 }
glennrp0fe50b42010-11-16 03:52:51 +000010475
cristy3ed852e2009-09-05 21:47:34 +000010476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010477 " PNG bit-depth written: %d",ping_bit_depth);
glennrp0fe50b42010-11-16 03:52:51 +000010478
cristy3ed852e2009-09-05 21:47:34 +000010479 if (mng_info->write_png_colortype)
10480 {
10481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10482 " Defined PNG:color-type: %d",mng_info->write_png_colortype-1);
10483 }
glennrp0fe50b42010-11-16 03:52:51 +000010484
cristy3ed852e2009-09-05 21:47:34 +000010485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010486 " PNG color-type written: %d",ping_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000010487
cristy3ed852e2009-09-05 21:47:34 +000010488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrp5af765f2010-03-30 11:12:18 +000010489 " PNG Interlace method: %d",ping_interlace_method);
cristy3ed852e2009-09-05 21:47:34 +000010490 }
10491 /*
glennrpa0ed0092011-04-18 16:36:29 +000010492 Generate text chunks after IDAT.
cristy3ed852e2009-09-05 21:47:34 +000010493 */
glennrp823b55c2011-03-14 18:46:46 +000010494 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +000010495 {
glennrp26f37912010-12-23 16:22:42 +000010496 ResetImagePropertyIterator(image);
cristy3ed852e2009-09-05 21:47:34 +000010497 property=GetNextImageProperty(image);
glennrp26f37912010-12-23 16:22:42 +000010498 while (property != (const char *) NULL)
10499 {
10500 png_textp
10501 text;
glennrp2cc891a2010-12-24 13:44:32 +000010502
glennrp26f37912010-12-23 16:22:42 +000010503 value=GetImageProperty(image,property);
glennrpa0ed0092011-04-18 16:36:29 +000010504
10505 /* Don't write any "png:" properties; those are just for "identify" */
10506 if (LocaleNCompare(property,"png:",4) != 0 &&
10507
10508 /* Suppress density and units if we wrote a pHYs chunk */
10509 (ping_exclude_pHYs != MagickFalse ||
glennrp823b55c2011-03-14 18:46:46 +000010510 LocaleCompare(property,"density") != 0 ||
glennrpa0ed0092011-04-18 16:36:29 +000010511 LocaleCompare(property,"units") != 0) &&
10512
10513 /* Suppress the IM-generated Date:create and Date:modify */
10514 (ping_exclude_date == MagickFalse ||
10515 LocaleNCompare(property, "Date:",5) != 0))
glennrp26f37912010-12-23 16:22:42 +000010516 {
glennrpc70af4a2011-03-07 00:08:23 +000010517 if (value != (const char *) NULL)
glennrp26f37912010-12-23 16:22:42 +000010518 {
glennrpc70af4a2011-03-07 00:08:23 +000010519 text=(png_textp) png_malloc(ping,(png_uint_32) sizeof(png_text));
10520 text[0].key=(char *) property;
10521 text[0].text=(char *) value;
10522 text[0].text_length=strlen(value);
glennrp2cc891a2010-12-24 13:44:32 +000010523
glennrpc70af4a2011-03-07 00:08:23 +000010524 if (ping_exclude_tEXt != MagickFalse)
10525 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
10526
10527 else if (ping_exclude_zTXt != MagickFalse)
10528 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
10529
10530 else
glennrp26f37912010-12-23 16:22:42 +000010531 {
glennrpc70af4a2011-03-07 00:08:23 +000010532 text[0].compression=image_info->compression == NoCompression ||
10533 (image_info->compression == UndefinedCompression &&
10534 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
10535 PNG_TEXT_COMPRESSION_zTXt ;
glennrp26f37912010-12-23 16:22:42 +000010536 }
glennrp2cc891a2010-12-24 13:44:32 +000010537
glennrpc70af4a2011-03-07 00:08:23 +000010538 if (logging != MagickFalse)
10539 {
10540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10541 " Setting up text chunk");
10542
10543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10544 " keyword: %s",text[0].key);
10545 }
10546
10547 png_set_text(ping,ping_info,text,1);
10548 png_free(ping,text);
10549 }
glennrp26f37912010-12-23 16:22:42 +000010550 }
10551 property=GetNextImageProperty(image);
10552 }
cristy3ed852e2009-09-05 21:47:34 +000010553 }
10554
10555 /* write any PNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000010556 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000010557
10558 if (logging != MagickFalse)
10559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10560 " Writing PNG end info");
glennrp0fe50b42010-11-16 03:52:51 +000010561
cristy3ed852e2009-09-05 21:47:34 +000010562 png_write_end(ping,ping_info);
glennrp0fe50b42010-11-16 03:52:51 +000010563
cristy3ed852e2009-09-05 21:47:34 +000010564 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
10565 {
10566 if (mng_info->page.x || mng_info->page.y ||
glennrp5af765f2010-03-30 11:12:18 +000010567 (ping_width != mng_info->page.width) ||
10568 (ping_height != mng_info->page.height))
cristy3ed852e2009-09-05 21:47:34 +000010569 {
10570 unsigned char
10571 chunk[32];
10572
10573 /*
10574 Write FRAM 4 with clipping boundaries followed by FRAM 1.
10575 */
10576 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
10577 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000010578 LogPNGChunk(logging,mng_FRAM,27L);
cristy3ed852e2009-09-05 21:47:34 +000010579 chunk[4]=4;
10580 chunk[5]=0; /* frame name separator (no name) */
10581 chunk[6]=1; /* flag for changing delay, for next frame only */
10582 chunk[7]=0; /* flag for changing frame timeout */
10583 chunk[8]=1; /* flag for changing frame clipping for next frame */
10584 chunk[9]=0; /* flag for changing frame sync_id */
10585 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
10586 chunk[14]=0; /* clipping boundaries delta type */
10587 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
10588 PNGLong(chunk+19,
glennrp5af765f2010-03-30 11:12:18 +000010589 (png_uint_32) (mng_info->page.x + ping_width));
cristy3ed852e2009-09-05 21:47:34 +000010590 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
10591 PNGLong(chunk+27,
glennrp5af765f2010-03-30 11:12:18 +000010592 (png_uint_32) (mng_info->page.y + ping_height));
cristy3ed852e2009-09-05 21:47:34 +000010593 (void) WriteBlob(image,31,chunk);
10594 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
10595 mng_info->old_framing_mode=4;
10596 mng_info->framing_mode=1;
10597 }
glennrp0fe50b42010-11-16 03:52:51 +000010598
cristy3ed852e2009-09-05 21:47:34 +000010599 else
10600 mng_info->framing_mode=3;
10601 }
10602 if (mng_info->write_mng && !mng_info->need_fram &&
10603 ((int) image->dispose == 3))
glennrpc70af4a2011-03-07 00:08:23 +000010604 (void) ThrowMagickException(&image->exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +000010605 CoderError,"Cannot convert GIF with disposal method 3 to MNG-LC",
glennrpc70af4a2011-03-07 00:08:23 +000010606 "`%s'",image->filename);
glennrp0fe50b42010-11-16 03:52:51 +000010607
cristy3ed852e2009-09-05 21:47:34 +000010608 /*
10609 Free PNG resources.
10610 */
glennrp5af765f2010-03-30 11:12:18 +000010611
cristy3ed852e2009-09-05 21:47:34 +000010612 png_destroy_write_struct(&ping,&ping_info);
10613
glennrpcf002022011-01-30 02:38:15 +000010614 ping_pixels=(unsigned char *) RelinquishMagickMemory(ping_pixels);
cristy3ed852e2009-09-05 21:47:34 +000010615
10616#if defined(PNG_SETJMP_NOT_THREAD_SAFE)
glennrpcf002022011-01-30 02:38:15 +000010617 UnlockSemaphoreInfo(ping_semaphore);
cristy3ed852e2009-09-05 21:47:34 +000010618#endif
10619
glennrpda8f3a72011-02-27 23:54:12 +000010620 if (ping_have_blob != MagickFalse)
glennrpb9cfe272010-12-21 15:08:06 +000010621 (void) CloseBlob(image);
10622
10623 image_info=DestroyImageInfo(image_info);
10624 image=DestroyImage(image);
10625
10626 /* Store bit depth actually written */
10627 s[0]=(char) ping_bit_depth;
10628 s[1]='\0';
10629
10630 (void) SetImageProperty(IMimage,"png:bit-depth-written",s);
10631
cristy3ed852e2009-09-05 21:47:34 +000010632 if (logging != MagickFalse)
10633 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10634 " exit WriteOnePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000010635
cristy3ed852e2009-09-05 21:47:34 +000010636 return(MagickTrue);
10637/* End write one PNG image */
10638}
10639
10640/*
10641%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10642% %
10643% %
10644% %
10645% W r i t e P N G I m a g e %
10646% %
10647% %
10648% %
10649%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10650%
10651% WritePNGImage() writes a Portable Network Graphics (PNG) or
10652% Multiple-image Network Graphics (MNG) image file.
10653%
10654% MNG support written by Glenn Randers-Pehrson, glennrp@image...
10655%
10656% The format of the WritePNGImage method is:
10657%
10658% MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
10659%
10660% A description of each parameter follows:
10661%
10662% o image_info: the image info.
10663%
10664% o image: The image.
10665%
10666% Returns MagickTrue on success, MagickFalse on failure.
10667%
10668% Communicating with the PNG encoder:
10669%
10670% While the datastream written is always in PNG format and normally would
10671% be given the "png" file extension, this method also writes the following
10672% pseudo-formats which are subsets of PNG:
10673%
glennrp5a39f372011-02-25 04:52:16 +000010674% o PNG8: An 8-bit indexed PNG datastream is written. If the image has
10675% a depth greater than 8, the depth is reduced. If transparency
cristy3ed852e2009-09-05 21:47:34 +000010676% is present, the tRNS chunk must only have values 0 and 255
10677% (i.e., transparency is binary: fully opaque or fully
glennrp5a39f372011-02-25 04:52:16 +000010678% transparent). If other values are present they will be
10679% 50%-thresholded to binary transparency. If more than 256
glennrpe9637cb2011-03-24 16:34:37 +000010680% colors are present, they will be quantized to the 4-4-4-1,
10681% 3-3-3-1, or 3-3-2-1 palette.
10682%
10683% If you want better quantization or dithering of the colors
10684% or alpha than that, you need to do it before calling the
glennrp5a39f372011-02-25 04:52:16 +000010685% PNG encoder. The pixels contain 8-bit indices even if
10686% they could be represented with 1, 2, or 4 bits. Grayscale
cristy3ed852e2009-09-05 21:47:34 +000010687% images will be written as indexed PNG files even though the
glennrp5a39f372011-02-25 04:52:16 +000010688% PNG grayscale type might be slightly more efficient. Please
10689% note that writing to the PNG8 format may result in loss
10690% of color and alpha data.
cristy3ed852e2009-09-05 21:47:34 +000010691%
10692% o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
10693% chunk can be present to convey binary transparency by naming
glennrp5a39f372011-02-25 04:52:16 +000010694% one of the colors as transparent. The only loss incurred
10695% is reduction of sample depth to 8. If the image has more
10696% than one transparent color, has semitransparent pixels, or
10697% has an opaque pixel with the same RGB components as the
10698% transparent color, an image is not written.
cristy3ed852e2009-09-05 21:47:34 +000010699%
10700% o PNG32: An 8-bit per sample RGBA PNG is written. Partial
10701% transparency is permitted, i.e., the alpha sample for
10702% each pixel can have any value from 0 to 255. The alpha
glennrp0fe50b42010-11-16 03:52:51 +000010703% channel is present even if the image is fully opaque.
glennrp5a39f372011-02-25 04:52:16 +000010704% The only loss in data is the reduction of the sample depth
10705% to 8.
cristy3ed852e2009-09-05 21:47:34 +000010706%
10707% o -define: For more precise control of the PNG output, you can use the
10708% Image options "png:bit-depth" and "png:color-type". These
10709% can be set from the commandline with "-define" and also
glennrpbb8a7332010-11-13 15:17:35 +000010710% from the application programming interfaces. The options
10711% are case-independent and are converted to lowercase before
10712% being passed to this encoder.
cristy3ed852e2009-09-05 21:47:34 +000010713%
10714% png:color-type can be 0, 2, 3, 4, or 6.
10715%
10716% When png:color-type is 0 (Grayscale), png:bit-depth can
10717% be 1, 2, 4, 8, or 16.
10718%
10719% When png:color-type is 2 (RGB), png:bit-depth can
10720% be 8 or 16.
10721%
10722% When png:color-type is 3 (Indexed), png:bit-depth can
10723% be 1, 2, 4, or 8. This refers to the number of bits
10724% used to store the index. The color samples always have
10725% bit-depth 8 in indexed PNG files.
10726%
10727% When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
10728% png:bit-depth can be 8 or 16.
10729%
glennrp5a39f372011-02-25 04:52:16 +000010730% If the image cannot be written without loss with the requested bit-depth
10731% and color-type, a PNG file will not be written, and the encoder will
10732% return MagickFalse.
10733%
cristy3ed852e2009-09-05 21:47:34 +000010734% Since image encoders should not be responsible for the "heavy lifting",
10735% the user should make sure that ImageMagick has already reduced the
10736% image depth and number of colors and limit transparency to binary
glennrp5a39f372011-02-25 04:52:16 +000010737% transparency prior to attempting to write the image with depth, color,
glennrp54dc0692011-07-01 19:02:13 +000010738% or transparency limitations.
cristy3ed852e2009-09-05 21:47:34 +000010739%
cristy3ed852e2009-09-05 21:47:34 +000010740% Note that another definition, "png:bit-depth-written" exists, but it
10741% is not intended for external use. It is only used internally by the
10742% PNG encoder to inform the JNG encoder of the depth of the alpha channel.
10743%
10744% It is possible to request that the PNG encoder write previously-formatted
10745% ancillary chunks in the output PNG file, using the "-profile" commandline
10746% option as shown below or by setting the profile via a programming
10747% interface:
10748%
10749% -profile PNG-chunk-x:<file>
10750%
10751% where x is a location flag and <file> is a file containing the chunk
10752% name in the first 4 bytes, then a colon (":"), followed by the chunk data.
glennrpbb8a7332010-11-13 15:17:35 +000010753% This encoder will compute the chunk length and CRC, so those must not
10754% be included in the file.
cristy3ed852e2009-09-05 21:47:34 +000010755%
10756% "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
10757% or "e" (end, i.e., after IDAT). If you want to write multiple chunks
10758% of the same type, then add a short unique string after the "x" to prevent
glennrpbb8a7332010-11-13 15:17:35 +000010759% subsequent profiles from overwriting the preceding ones, e.g.,
cristy3ed852e2009-09-05 21:47:34 +000010760%
glennrpbb8a7332010-11-13 15:17:35 +000010761% -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
cristy3ed852e2009-09-05 21:47:34 +000010762%
glennrp3241bd02010-12-12 04:36:28 +000010763% As of version 6.6.6 the following optimizations are always done:
glennrp0fe50b42010-11-16 03:52:51 +000010764%
glennrpd6afd542010-11-19 01:53:05 +000010765% o 32-bit depth is reduced to 16.
10766% o 16-bit depth is reduced to 8 if all pixels contain samples whose
10767% high byte and low byte are identical.
glennrp0fe50b42010-11-16 03:52:51 +000010768% o Palette is sorted to remove unused entries and to put a
glennrpcf002022011-01-30 02:38:15 +000010769% transparent color first, if BUILD_PNG_PALETTE is defined.
glennrp0fe50b42010-11-16 03:52:51 +000010770% o Opaque matte channel is removed when writing an indexed PNG.
glennrpd6afd542010-11-19 01:53:05 +000010771% o Grayscale images are reduced to 1, 2, or 4 bit depth if
10772% this can be done without loss and a larger bit depth N was not
10773% requested via the "-define PNG:bit-depth=N" option.
10774% o If matte channel is present but only one transparent color is
10775% present, RGB+tRNS is written instead of RGBA
10776% o Opaque matte channel is removed (or added, if color-type 4 or 6
10777% was requested when converting an opaque image).
glennrp0fe50b42010-11-16 03:52:51 +000010778%
cristy3ed852e2009-09-05 21:47:34 +000010779%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10780*/
10781static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
10782 Image *image)
10783{
10784 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000010785 excluding,
10786 logging,
10787 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000010788 status;
10789
10790 MngInfo
10791 *mng_info;
10792
10793 const char
10794 *value;
10795
10796 int
glennrp21f0e622011-01-07 16:20:57 +000010797 i,
glennrp5c7cf4e2010-12-24 00:30:00 +000010798 source;
10799
cristy3ed852e2009-09-05 21:47:34 +000010800 /*
10801 Open image file.
10802 */
10803 assert(image_info != (const ImageInfo *) NULL);
10804 assert(image_info->signature == MagickSignature);
10805 assert(image != (Image *) NULL);
10806 assert(image->signature == MagickSignature);
10807 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000010808 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000010809 /*
10810 Allocate a MngInfo structure.
10811 */
10812 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000010813 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
glennrp0fe50b42010-11-16 03:52:51 +000010814
cristy3ed852e2009-09-05 21:47:34 +000010815 if (mng_info == (MngInfo *) NULL)
10816 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000010817
cristy3ed852e2009-09-05 21:47:34 +000010818 /*
10819 Initialize members of the MngInfo structure.
10820 */
10821 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
10822 mng_info->image=image;
glennrpa521b2f2010-10-29 04:11:03 +000010823 mng_info->equal_backgrounds=MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000010824 have_mng_structure=MagickTrue;
10825
10826 /* See if user has requested a specific PNG subformat */
10827
10828 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
10829 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
10830 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
10831
10832 if (mng_info->write_png8)
10833 {
glennrp9c1eb072010-06-06 22:19:15 +000010834 mng_info->write_png_colortype = /* 3 */ 4;
10835 mng_info->write_png_depth = 8;
10836 image->depth = 8;
cristy3ed852e2009-09-05 21:47:34 +000010837 }
10838
10839 if (mng_info->write_png24)
10840 {
glennrp9c1eb072010-06-06 22:19:15 +000010841 mng_info->write_png_colortype = /* 2 */ 3;
10842 mng_info->write_png_depth = 8;
10843 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010844
glennrp9c1eb072010-06-06 22:19:15 +000010845 if (image->matte == MagickTrue)
10846 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010847
glennrp9c1eb072010-06-06 22:19:15 +000010848 else
10849 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010850
glennrp9c1eb072010-06-06 22:19:15 +000010851 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010852 }
10853
10854 if (mng_info->write_png32)
10855 {
glennrp9c1eb072010-06-06 22:19:15 +000010856 mng_info->write_png_colortype = /* 6 */ 7;
10857 mng_info->write_png_depth = 8;
10858 image->depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010859
glennrp9c1eb072010-06-06 22:19:15 +000010860 if (image->matte == MagickTrue)
10861 (void) SetImageType(image,TrueColorMatteType);
glennrp0fe50b42010-11-16 03:52:51 +000010862
glennrp9c1eb072010-06-06 22:19:15 +000010863 else
10864 (void) SetImageType(image,TrueColorType);
glennrp0fe50b42010-11-16 03:52:51 +000010865
glennrp9c1eb072010-06-06 22:19:15 +000010866 (void) SyncImage(image);
cristy3ed852e2009-09-05 21:47:34 +000010867 }
10868
10869 value=GetImageOption(image_info,"png:bit-depth");
glennrp8bb3a022010-12-13 20:40:04 +000010870
cristy3ed852e2009-09-05 21:47:34 +000010871 if (value != (char *) NULL)
10872 {
10873 if (LocaleCompare(value,"1") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010874 mng_info->write_png_depth = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010875
cristy3ed852e2009-09-05 21:47:34 +000010876 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010877 mng_info->write_png_depth = 2;
glennrp0fe50b42010-11-16 03:52:51 +000010878
cristy3ed852e2009-09-05 21:47:34 +000010879 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010880 mng_info->write_png_depth = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010881
cristy3ed852e2009-09-05 21:47:34 +000010882 else if (LocaleCompare(value,"8") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010883 mng_info->write_png_depth = 8;
glennrp0fe50b42010-11-16 03:52:51 +000010884
cristy3ed852e2009-09-05 21:47:34 +000010885 else if (LocaleCompare(value,"16") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010886 mng_info->write_png_depth = 16;
glennrp0fe50b42010-11-16 03:52:51 +000010887
glennrpbb8a7332010-11-13 15:17:35 +000010888 else
10889 (void) ThrowMagickException(&image->exception,
10890 GetMagickModule(),CoderWarning,
10891 "ignoring invalid defined png:bit-depth",
10892 "=%s",value);
10893
cristy3ed852e2009-09-05 21:47:34 +000010894 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpbb8a7332010-11-13 15:17:35 +000010896 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
cristy3ed852e2009-09-05 21:47:34 +000010897 }
glennrp0fe50b42010-11-16 03:52:51 +000010898
cristy3ed852e2009-09-05 21:47:34 +000010899 value=GetImageOption(image_info,"png:color-type");
glennrp0fe50b42010-11-16 03:52:51 +000010900
cristy3ed852e2009-09-05 21:47:34 +000010901 if (value != (char *) NULL)
10902 {
10903 /* We must store colortype+1 because 0 is a valid colortype */
10904 if (LocaleCompare(value,"0") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010905 mng_info->write_png_colortype = 1;
glennrp0fe50b42010-11-16 03:52:51 +000010906
cristy3ed852e2009-09-05 21:47:34 +000010907 else if (LocaleCompare(value,"2") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010908 mng_info->write_png_colortype = 3;
glennrp0fe50b42010-11-16 03:52:51 +000010909
cristy3ed852e2009-09-05 21:47:34 +000010910 else if (LocaleCompare(value,"3") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010911 mng_info->write_png_colortype = 4;
glennrp0fe50b42010-11-16 03:52:51 +000010912
cristy3ed852e2009-09-05 21:47:34 +000010913 else if (LocaleCompare(value,"4") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010914 mng_info->write_png_colortype = 5;
glennrp0fe50b42010-11-16 03:52:51 +000010915
cristy3ed852e2009-09-05 21:47:34 +000010916 else if (LocaleCompare(value,"6") == 0)
glennrp9c1eb072010-06-06 22:19:15 +000010917 mng_info->write_png_colortype = 7;
glennrp0fe50b42010-11-16 03:52:51 +000010918
glennrpbb8a7332010-11-13 15:17:35 +000010919 else
10920 (void) ThrowMagickException(&image->exception,
10921 GetMagickModule(),CoderWarning,
10922 "ignoring invalid defined png:color-type",
10923 "=%s",value);
10924
cristy3ed852e2009-09-05 21:47:34 +000010925 if (logging != MagickFalse)
glennrp9c1eb072010-06-06 22:19:15 +000010926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpd6bf1612010-12-17 17:28:54 +000010927 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
cristy3ed852e2009-09-05 21:47:34 +000010928 }
10929
glennrp0e8ea192010-12-24 18:00:33 +000010930 /* Check for chunks to be excluded:
10931 *
glennrp0dff56c2011-01-29 19:10:02 +000010932 * The default is to not exclude any known chunks except for any
glennrp0e8ea192010-12-24 18:00:33 +000010933 * listed in the "unused_chunks" array, above.
10934 *
10935 * Chunks can be listed for exclusion via a "PNG:exclude-chunk"
10936 * define (in the image properties or in the image artifacts)
10937 * or via a mng_info member. For convenience, in addition
10938 * to or instead of a comma-separated list of chunks, the
10939 * "exclude-chunk" string can be simply "all" or "none".
10940 *
10941 * The exclude-chunk define takes priority over the mng_info.
10942 *
10943 * A "PNG:include-chunk" define takes priority over both the
10944 * mng_info and the "PNG:exclude-chunk" define. Like the
10945 * "exclude-chunk" string, it can define "all" or "none" as
glennrp0dff56c2011-01-29 19:10:02 +000010946 * well as a comma-separated list. Chunks that are unknown to
10947 * ImageMagick are always excluded, regardless of their "copy-safe"
10948 * status according to the PNG specification, and even if they
10949 * appear in the "include-chunk" list.
glennrp0e8ea192010-12-24 18:00:33 +000010950 *
10951 * Finally, all chunks listed in the "unused_chunks" array are
10952 * automatically excluded, regardless of the other instructions
10953 * or lack thereof.
10954 *
10955 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
10956 * will not be written and the gAMA chunk will only be written if it
10957 * is not between .45 and .46, or approximately (1.0/2.2).
10958 *
10959 * If you exclude tRNS and the image has transparency, the colortype
10960 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
10961 *
10962 * The -strip option causes StripImage() to set the png:include-chunk
10963 * artifact to "none,gama".
10964 */
10965
glennrp26f37912010-12-23 16:22:42 +000010966 mng_info->ping_exclude_bKGD=MagickFalse;
10967 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000010968 mng_info->ping_exclude_date=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010969 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
10970 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010971 mng_info->ping_exclude_iCCP=MagickFalse;
10972 /* mng_info->ping_exclude_iTXt=MagickFalse; */
10973 mng_info->ping_exclude_oFFs=MagickFalse;
10974 mng_info->ping_exclude_pHYs=MagickFalse;
10975 mng_info->ping_exclude_sRGB=MagickFalse;
10976 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000010977 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp26f37912010-12-23 16:22:42 +000010978 mng_info->ping_exclude_vpAg=MagickFalse;
10979 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
10980 mng_info->ping_exclude_zTXt=MagickFalse;
10981
glennrp8d3d6e52011-04-19 04:39:51 +000010982 mng_info->ping_preserve_colormap=MagickFalse;
10983
10984 value=GetImageArtifact(image,"png:preserve-colormap");
10985 if (value == NULL)
10986 value=GetImageOption(image_info,"png:preserve-colormap");
10987 if (value != NULL)
10988 mng_info->ping_preserve_colormap=MagickTrue;
10989
glennrp18682582011-06-30 18:11:47 +000010990 /* Thes compression-level, compression-strategy, and compression-filter
10991 * defines take precedence over values from the -quality option.
10992 */
10993 value=GetImageArtifact(image,"png:compression-level");
10994 if (value == NULL)
10995 value=GetImageOption(image_info,"png:compression-level");
10996 if (value != NULL)
10997 {
glennrp18682582011-06-30 18:11:47 +000010998 /* We have to add 1 to everything because 0 is a valid input,
10999 * and we want to use 0 (the default) to mean undefined.
11000 */
11001 if (LocaleCompare(value,"0") == 0)
11002 mng_info->write_png_compression_level = 1;
11003
11004 if (LocaleCompare(value,"1") == 0)
11005 mng_info->write_png_compression_level = 2;
11006
11007 else if (LocaleCompare(value,"2") == 0)
11008 mng_info->write_png_compression_level = 3;
11009
11010 else if (LocaleCompare(value,"3") == 0)
11011 mng_info->write_png_compression_level = 4;
11012
11013 else if (LocaleCompare(value,"4") == 0)
11014 mng_info->write_png_compression_level = 5;
11015
11016 else if (LocaleCompare(value,"5") == 0)
11017 mng_info->write_png_compression_level = 6;
11018
11019 else if (LocaleCompare(value,"6") == 0)
11020 mng_info->write_png_compression_level = 7;
11021
11022 else if (LocaleCompare(value,"7") == 0)
11023 mng_info->write_png_compression_level = 8;
11024
11025 else if (LocaleCompare(value,"8") == 0)
11026 mng_info->write_png_compression_level = 9;
11027
11028 else if (LocaleCompare(value,"9") == 0)
11029 mng_info->write_png_compression_level = 10;
11030
11031 else
11032 (void) ThrowMagickException(&image->exception,
11033 GetMagickModule(),CoderWarning,
11034 "ignoring invalid defined png:compression-level",
11035 "=%s",value);
11036 }
11037
11038 value=GetImageArtifact(image,"png:compression-strategy");
11039 if (value == NULL)
11040 value=GetImageOption(image_info,"png:compression-strategy");
11041 if (value != NULL)
11042 {
11043
11044 if (LocaleCompare(value,"0") == 0)
11045 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11046
11047 else if (LocaleCompare(value,"1") == 0)
11048 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11049
11050 else if (LocaleCompare(value,"2") == 0)
11051 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11052
11053 else if (LocaleCompare(value,"3") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011054#ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
glennrp18682582011-06-30 18:11:47 +000011055 mng_info->write_png_compression_strategy = Z_RLE+1;
glennrp98c07ad2011-07-01 02:52:59 +000011056#else
11057 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11058#endif
glennrp18682582011-06-30 18:11:47 +000011059
11060 else if (LocaleCompare(value,"4") == 0)
glennrp98c07ad2011-07-01 02:52:59 +000011061#ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
glennrp18682582011-06-30 18:11:47 +000011062 mng_info->write_png_compression_strategy = Z_FIXED+1;
glennrp98c07ad2011-07-01 02:52:59 +000011063#else
11064 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11065#endif
glennrp18682582011-06-30 18:11:47 +000011066
11067 else
11068 (void) ThrowMagickException(&image->exception,
11069 GetMagickModule(),CoderWarning,
11070 "ignoring invalid defined png:compression-strategy",
11071 "=%s",value);
11072 }
11073
11074 value=GetImageArtifact(image,"png:compression-filter");
11075 if (value == NULL)
11076 value=GetImageOption(image_info,"png:compression-filter");
11077 if (value != NULL)
11078 {
11079
11080 /* To do: combinations of filters allowed by libpng
11081 * masks 0x08 through 0xf8
11082 *
11083 * Implement this as a comma-separated list of 0,1,2,3,4,5
11084 * where 5 is a special case meaning PNG_ALL_FILTERS.
11085 */
11086
11087 if (LocaleCompare(value,"0") == 0)
11088 mng_info->write_png_compression_filter = 1;
11089
11090 if (LocaleCompare(value,"1") == 0)
11091 mng_info->write_png_compression_filter = 2;
11092
11093 else if (LocaleCompare(value,"2") == 0)
11094 mng_info->write_png_compression_filter = 3;
11095
11096 else if (LocaleCompare(value,"3") == 0)
11097 mng_info->write_png_compression_filter = 4;
11098
11099 else if (LocaleCompare(value,"4") == 0)
11100 mng_info->write_png_compression_filter = 5;
11101
11102 else if (LocaleCompare(value,"5") == 0)
11103 mng_info->write_png_compression_filter = 6;
11104
glennrp54dc0692011-07-01 19:02:13 +000011105 else if (LocaleCompare(value,"6") == 0)
11106 mng_info->write_png_compression_filter = 7;
11107
11108 else if (LocaleCompare(value,"7") == 0)
11109 mng_info->write_png_compression_filter = 8;
11110
11111 else if (LocaleCompare(value,"8") == 0)
11112 mng_info->write_png_compression_filter = 9;
11113
11114 else if (LocaleCompare(value,"9") == 0)
11115 mng_info->write_png_compression_filter = 10;
glennrp18682582011-06-30 18:11:47 +000011116
11117 else
11118 (void) ThrowMagickException(&image->exception,
11119 GetMagickModule(),CoderWarning,
11120 "ignoring invalid defined png:compression-filter",
11121 "=%s",value);
11122 }
11123
glennrp03812ae2010-12-24 01:31:34 +000011124 excluding=MagickFalse;
11125
glennrp5c7cf4e2010-12-24 00:30:00 +000011126 for (source=0; source<1; source++)
11127 {
11128 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011129 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011130 value=GetImageArtifact(image,"png:exclude-chunk");
glennrpacba0042010-12-24 14:27:26 +000011131
11132 if (value == NULL)
11133 value=GetImageArtifact(image,"png:exclude-chunks");
11134 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011135 else
glennrpacba0042010-12-24 14:27:26 +000011136 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011137 value=GetImageOption(image_info,"png:exclude-chunk");
glennrp26f37912010-12-23 16:22:42 +000011138
glennrpacba0042010-12-24 14:27:26 +000011139 if (value == NULL)
11140 value=GetImageOption(image_info,"png:exclude-chunks");
11141 }
11142
glennrp03812ae2010-12-24 01:31:34 +000011143 if (value != NULL)
glennrpce91ed52010-12-23 22:37:49 +000011144 {
glennrp03812ae2010-12-24 01:31:34 +000011145
11146 size_t
11147 last;
11148
11149 excluding=MagickTrue;
11150
11151 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011152 {
11153 if (source == 0)
11154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11155 " png:exclude-chunk=%s found in image artifacts.\n", value);
11156 else
11157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11158 " png:exclude-chunk=%s found in image properties.\n", value);
11159 }
glennrp03812ae2010-12-24 01:31:34 +000011160
11161 last=strlen(value);
11162
11163 for (i=0; i<(int) last; i+=5)
glennrpce91ed52010-12-23 22:37:49 +000011164 {
glennrp03812ae2010-12-24 01:31:34 +000011165
11166 if (LocaleNCompare(value+i,"all",3) == 0)
11167 {
11168 mng_info->ping_exclude_bKGD=MagickTrue;
11169 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011170 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011171 mng_info->ping_exclude_EXIF=MagickTrue;
11172 mng_info->ping_exclude_gAMA=MagickTrue;
11173 mng_info->ping_exclude_iCCP=MagickTrue;
11174 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11175 mng_info->ping_exclude_oFFs=MagickTrue;
11176 mng_info->ping_exclude_pHYs=MagickTrue;
11177 mng_info->ping_exclude_sRGB=MagickTrue;
11178 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011179 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011180 mng_info->ping_exclude_vpAg=MagickTrue;
11181 mng_info->ping_exclude_zCCP=MagickTrue;
11182 mng_info->ping_exclude_zTXt=MagickTrue;
11183 i--;
11184 }
glennrp2cc891a2010-12-24 13:44:32 +000011185
glennrp03812ae2010-12-24 01:31:34 +000011186 if (LocaleNCompare(value+i,"none",4) == 0)
11187 {
11188 mng_info->ping_exclude_bKGD=MagickFalse;
11189 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011190 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011191 mng_info->ping_exclude_EXIF=MagickFalse;
11192 mng_info->ping_exclude_gAMA=MagickFalse;
11193 mng_info->ping_exclude_iCCP=MagickFalse;
11194 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11195 mng_info->ping_exclude_oFFs=MagickFalse;
11196 mng_info->ping_exclude_pHYs=MagickFalse;
11197 mng_info->ping_exclude_sRGB=MagickFalse;
11198 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011199 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011200 mng_info->ping_exclude_vpAg=MagickFalse;
11201 mng_info->ping_exclude_zCCP=MagickFalse;
11202 mng_info->ping_exclude_zTXt=MagickFalse;
11203 }
glennrp2cc891a2010-12-24 13:44:32 +000011204
glennrp03812ae2010-12-24 01:31:34 +000011205 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11206 mng_info->ping_exclude_bKGD=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011207
glennrp03812ae2010-12-24 01:31:34 +000011208 if (LocaleNCompare(value+i,"chrm",4) == 0)
11209 mng_info->ping_exclude_cHRM=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011210
glennrpa0ed0092011-04-18 16:36:29 +000011211 if (LocaleNCompare(value+i,"date",4) == 0)
11212 mng_info->ping_exclude_date=MagickTrue;
11213
glennrp03812ae2010-12-24 01:31:34 +000011214 if (LocaleNCompare(value+i,"exif",4) == 0)
11215 mng_info->ping_exclude_EXIF=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011216
glennrp03812ae2010-12-24 01:31:34 +000011217 if (LocaleNCompare(value+i,"gama",4) == 0)
11218 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011219
glennrp03812ae2010-12-24 01:31:34 +000011220 if (LocaleNCompare(value+i,"iccp",4) == 0)
11221 mng_info->ping_exclude_iCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011222
glennrp03812ae2010-12-24 01:31:34 +000011223 /*
11224 if (LocaleNCompare(value+i,"itxt",4) == 0)
11225 mng_info->ping_exclude_iTXt=MagickTrue;
11226 */
glennrp2cc891a2010-12-24 13:44:32 +000011227
glennrp03812ae2010-12-24 01:31:34 +000011228 if (LocaleNCompare(value+i,"gama",4) == 0)
11229 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011230
glennrp03812ae2010-12-24 01:31:34 +000011231 if (LocaleNCompare(value+i,"offs",4) == 0)
11232 mng_info->ping_exclude_oFFs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011233
glennrp03812ae2010-12-24 01:31:34 +000011234 if (LocaleNCompare(value+i,"phys",4) == 0)
11235 mng_info->ping_exclude_pHYs=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011236
glennrpa1e3b7b2010-12-24 16:37:33 +000011237 if (LocaleNCompare(value+i,"srgb",4) == 0)
11238 mng_info->ping_exclude_sRGB=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011239
glennrp03812ae2010-12-24 01:31:34 +000011240 if (LocaleNCompare(value+i,"text",4) == 0)
11241 mng_info->ping_exclude_tEXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011242
glennrpa1e3b7b2010-12-24 16:37:33 +000011243 if (LocaleNCompare(value+i,"trns",4) == 0)
11244 mng_info->ping_exclude_tRNS=MagickTrue;
11245
glennrp03812ae2010-12-24 01:31:34 +000011246 if (LocaleNCompare(value+i,"vpag",4) == 0)
11247 mng_info->ping_exclude_vpAg=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011248
glennrp03812ae2010-12-24 01:31:34 +000011249 if (LocaleNCompare(value+i,"zccp",4) == 0)
11250 mng_info->ping_exclude_zCCP=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011251
glennrp03812ae2010-12-24 01:31:34 +000011252 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11253 mng_info->ping_exclude_zTXt=MagickTrue;
glennrp2cc891a2010-12-24 13:44:32 +000011254
glennrp03812ae2010-12-24 01:31:34 +000011255 }
glennrpce91ed52010-12-23 22:37:49 +000011256 }
glennrp26f37912010-12-23 16:22:42 +000011257 }
11258
glennrp5c7cf4e2010-12-24 00:30:00 +000011259 for (source=0; source<1; source++)
11260 {
11261 if (source==0)
glennrpacba0042010-12-24 14:27:26 +000011262 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011263 value=GetImageArtifact(image,"png:include-chunk");
glennrpacba0042010-12-24 14:27:26 +000011264
11265 if (value == NULL)
11266 value=GetImageArtifact(image,"png:include-chunks");
11267 }
glennrp5c7cf4e2010-12-24 00:30:00 +000011268 else
glennrpacba0042010-12-24 14:27:26 +000011269 {
glennrp5c7cf4e2010-12-24 00:30:00 +000011270 value=GetImageOption(image_info,"png:include-chunk");
glennrp26f37912010-12-23 16:22:42 +000011271
glennrpacba0042010-12-24 14:27:26 +000011272 if (value == NULL)
11273 value=GetImageOption(image_info,"png:include-chunks");
11274 }
11275
glennrp03812ae2010-12-24 01:31:34 +000011276 if (value != NULL)
11277 {
11278 size_t
11279 last;
glennrp26f37912010-12-23 16:22:42 +000011280
glennrp03812ae2010-12-24 01:31:34 +000011281 excluding=MagickTrue;
glennrp26f37912010-12-23 16:22:42 +000011282
glennrp03812ae2010-12-24 01:31:34 +000011283 if (logging != MagickFalse)
glennrp2cc891a2010-12-24 13:44:32 +000011284 {
11285 if (source == 0)
11286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11287 " png:include-chunk=%s found in image artifacts.\n", value);
11288 else
11289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11290 " png:include-chunk=%s found in image properties.\n", value);
11291 }
glennrp03812ae2010-12-24 01:31:34 +000011292
11293 last=strlen(value);
11294
11295 for (i=0; i<(int) last; i+=5)
11296 {
11297 if (LocaleNCompare(value+i,"all",3) == 0)
11298 {
11299 mng_info->ping_exclude_bKGD=MagickFalse;
11300 mng_info->ping_exclude_cHRM=MagickFalse;
glennrpa0ed0092011-04-18 16:36:29 +000011301 mng_info->ping_exclude_date=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011302 mng_info->ping_exclude_EXIF=MagickFalse;
11303 mng_info->ping_exclude_gAMA=MagickFalse;
11304 mng_info->ping_exclude_iCCP=MagickFalse;
11305 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11306 mng_info->ping_exclude_oFFs=MagickFalse;
11307 mng_info->ping_exclude_pHYs=MagickFalse;
11308 mng_info->ping_exclude_sRGB=MagickFalse;
11309 mng_info->ping_exclude_tEXt=MagickFalse;
glennrpa1e3b7b2010-12-24 16:37:33 +000011310 mng_info->ping_exclude_tRNS=MagickFalse;
glennrp03812ae2010-12-24 01:31:34 +000011311 mng_info->ping_exclude_vpAg=MagickFalse;
11312 mng_info->ping_exclude_zCCP=MagickFalse;
11313 mng_info->ping_exclude_zTXt=MagickFalse;
11314 i--;
11315 }
glennrp2cc891a2010-12-24 13:44:32 +000011316
glennrp03812ae2010-12-24 01:31:34 +000011317 if (LocaleNCompare(value+i,"none",4) == 0)
11318 {
11319 mng_info->ping_exclude_bKGD=MagickTrue;
11320 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000011321 mng_info->ping_exclude_date=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011322 mng_info->ping_exclude_EXIF=MagickTrue;
11323 mng_info->ping_exclude_gAMA=MagickTrue;
11324 mng_info->ping_exclude_iCCP=MagickTrue;
11325 /* mng_info->ping_exclude_iTXt=MagickTrue; */
11326 mng_info->ping_exclude_oFFs=MagickTrue;
11327 mng_info->ping_exclude_pHYs=MagickTrue;
11328 mng_info->ping_exclude_sRGB=MagickTrue;
11329 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000011330 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp03812ae2010-12-24 01:31:34 +000011331 mng_info->ping_exclude_vpAg=MagickTrue;
11332 mng_info->ping_exclude_zCCP=MagickTrue;
11333 mng_info->ping_exclude_zTXt=MagickTrue;
11334 }
glennrp2cc891a2010-12-24 13:44:32 +000011335
glennrp03812ae2010-12-24 01:31:34 +000011336 if (LocaleNCompare(value+i,"bkgd",4) == 0)
11337 mng_info->ping_exclude_bKGD=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011338
glennrp03812ae2010-12-24 01:31:34 +000011339 if (LocaleNCompare(value+i,"chrm",4) == 0)
11340 mng_info->ping_exclude_cHRM=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011341
glennrpa0ed0092011-04-18 16:36:29 +000011342 if (LocaleNCompare(value+i,"date",4) == 0)
11343 mng_info->ping_exclude_date=MagickFalse;
11344
glennrp03812ae2010-12-24 01:31:34 +000011345 if (LocaleNCompare(value+i,"exif",4) == 0)
11346 mng_info->ping_exclude_EXIF=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011347
glennrp03812ae2010-12-24 01:31:34 +000011348 if (LocaleNCompare(value+i,"gama",4) == 0)
11349 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011350
glennrp03812ae2010-12-24 01:31:34 +000011351 if (LocaleNCompare(value+i,"iccp",4) == 0)
11352 mng_info->ping_exclude_iCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011353
glennrp03812ae2010-12-24 01:31:34 +000011354 /*
11355 if (LocaleNCompare(value+i,"itxt",4) == 0)
11356 mng_info->ping_exclude_iTXt=MagickFalse;
11357 */
glennrp2cc891a2010-12-24 13:44:32 +000011358
glennrp03812ae2010-12-24 01:31:34 +000011359 if (LocaleNCompare(value+i,"gama",4) == 0)
11360 mng_info->ping_exclude_gAMA=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011361
glennrp03812ae2010-12-24 01:31:34 +000011362 if (LocaleNCompare(value+i,"offs",4) == 0)
11363 mng_info->ping_exclude_oFFs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011364
glennrp03812ae2010-12-24 01:31:34 +000011365 if (LocaleNCompare(value+i,"phys",4) == 0)
11366 mng_info->ping_exclude_pHYs=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011367
glennrpa1e3b7b2010-12-24 16:37:33 +000011368 if (LocaleNCompare(value+i,"srgb",4) == 0)
11369 mng_info->ping_exclude_sRGB=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011370
glennrp03812ae2010-12-24 01:31:34 +000011371 if (LocaleNCompare(value+i,"text",4) == 0)
11372 mng_info->ping_exclude_tEXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011373
glennrpa1e3b7b2010-12-24 16:37:33 +000011374 if (LocaleNCompare(value+i,"trns",4) == 0)
11375 mng_info->ping_exclude_tRNS=MagickFalse;
11376
glennrp03812ae2010-12-24 01:31:34 +000011377 if (LocaleNCompare(value+i,"vpag",4) == 0)
11378 mng_info->ping_exclude_vpAg=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011379
glennrp03812ae2010-12-24 01:31:34 +000011380 if (LocaleNCompare(value+i,"zccp",4) == 0)
11381 mng_info->ping_exclude_zCCP=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011382
glennrp03812ae2010-12-24 01:31:34 +000011383 if (LocaleNCompare(value+i,"ztxt",4) == 0)
11384 mng_info->ping_exclude_zTXt=MagickFalse;
glennrp2cc891a2010-12-24 13:44:32 +000011385
glennrp03812ae2010-12-24 01:31:34 +000011386 }
glennrpce91ed52010-12-23 22:37:49 +000011387 }
glennrp26f37912010-12-23 16:22:42 +000011388 }
11389
glennrp03812ae2010-12-24 01:31:34 +000011390 if (excluding != MagickFalse && logging != MagickFalse)
glennrp26f37912010-12-23 16:22:42 +000011391 {
11392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11393 " Chunks to be excluded from the output PNG:");
11394 if (mng_info->ping_exclude_bKGD != MagickFalse)
11395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11396 " bKGD");
11397 if (mng_info->ping_exclude_cHRM != MagickFalse)
11398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11399 " cHRM");
glennrpa0ed0092011-04-18 16:36:29 +000011400 if (mng_info->ping_exclude_date != MagickFalse)
11401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11402 " date");
glennrp26f37912010-12-23 16:22:42 +000011403 if (mng_info->ping_exclude_EXIF != MagickFalse)
11404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11405 " EXIF");
11406 if (mng_info->ping_exclude_gAMA != MagickFalse)
11407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11408 " gAMA");
11409 if (mng_info->ping_exclude_iCCP != MagickFalse)
11410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11411 " iCCP");
11412/*
11413 if (mng_info->ping_exclude_iTXt != MagickFalse)
11414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11415 " iTXt");
11416*/
11417 if (mng_info->ping_exclude_oFFs != MagickFalse)
11418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11419 " oFFs");
11420 if (mng_info->ping_exclude_pHYs != MagickFalse)
11421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11422 " pHYs");
11423 if (mng_info->ping_exclude_sRGB != MagickFalse)
11424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11425 " sRGB");
11426 if (mng_info->ping_exclude_tEXt != MagickFalse)
11427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11428 " tEXt");
glennrpa1e3b7b2010-12-24 16:37:33 +000011429 if (mng_info->ping_exclude_tRNS != MagickFalse)
11430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11431 " tRNS");
glennrp26f37912010-12-23 16:22:42 +000011432 if (mng_info->ping_exclude_vpAg != MagickFalse)
11433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11434 " vpAg");
11435 if (mng_info->ping_exclude_zCCP != MagickFalse)
11436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11437 " zCCP");
11438 if (mng_info->ping_exclude_zTXt != MagickFalse)
11439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11440 " zTXt");
11441 }
11442
glennrpb9cfe272010-12-21 15:08:06 +000011443 mng_info->need_blob = MagickTrue;
cristy3ed852e2009-09-05 21:47:34 +000011444
glennrpb9cfe272010-12-21 15:08:06 +000011445 status=WriteOnePNGImage(mng_info,image_info,image);
cristy3ed852e2009-09-05 21:47:34 +000011446
11447 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000011448
cristy3ed852e2009-09-05 21:47:34 +000011449 if (logging != MagickFalse)
11450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011451
cristy3ed852e2009-09-05 21:47:34 +000011452 return(status);
11453}
11454
11455#if defined(JNG_SUPPORTED)
11456
11457/* Write one JNG image */
11458static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
11459 const ImageInfo *image_info,Image *image)
11460{
11461 Image
11462 *jpeg_image;
11463
11464 ImageInfo
11465 *jpeg_image_info;
11466
11467 MagickBooleanType
glennrp03812ae2010-12-24 01:31:34 +000011468 logging,
cristy3ed852e2009-09-05 21:47:34 +000011469 status;
11470
11471 size_t
11472 length;
11473
11474 unsigned char
11475 *blob,
11476 chunk[80],
11477 *p;
11478
11479 unsigned int
11480 jng_alpha_compression_method,
11481 jng_alpha_sample_depth,
11482 jng_color_type,
cristy3ed852e2009-09-05 21:47:34 +000011483 transparent;
11484
cristybb503372010-05-27 20:51:26 +000011485 size_t
cristy3ed852e2009-09-05 21:47:34 +000011486 jng_quality;
11487
11488 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
glennrpfd05d622011-02-25 04:10:33 +000011489 " Enter WriteOneJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000011490
11491 blob=(unsigned char *) NULL;
11492 jpeg_image=(Image *) NULL;
11493 jpeg_image_info=(ImageInfo *) NULL;
11494
11495 status=MagickTrue;
11496 transparent=image_info->type==GrayscaleMatteType ||
11497 image_info->type==TrueColorMatteType;
11498 jng_color_type=10;
11499 jng_alpha_sample_depth=0;
11500 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality;
11501 jng_alpha_compression_method=0;
11502
11503 if (image->matte != MagickFalse)
11504 {
11505 /* if any pixels are transparent */
11506 transparent=MagickTrue;
11507 if (image_info->compression==JPEGCompression)
11508 jng_alpha_compression_method=8;
11509 }
11510
11511 if (transparent)
11512 {
11513 jng_color_type=14;
glennrp0fe50b42010-11-16 03:52:51 +000011514
cristy3ed852e2009-09-05 21:47:34 +000011515 /* Create JPEG blob, image, and image_info */
11516 if (logging != MagickFalse)
11517 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristy4c08aed2011-07-01 19:47:50 +000011518 " Creating jpeg_image_info for alpha.");
glennrp0fe50b42010-11-16 03:52:51 +000011519
cristy3ed852e2009-09-05 21:47:34 +000011520 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
glennrp0fe50b42010-11-16 03:52:51 +000011521
cristy3ed852e2009-09-05 21:47:34 +000011522 if (jpeg_image_info == (ImageInfo *) NULL)
11523 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011524
cristy3ed852e2009-09-05 21:47:34 +000011525 if (logging != MagickFalse)
11526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11527 " Creating jpeg_image.");
glennrp0fe50b42010-11-16 03:52:51 +000011528
cristy3ed852e2009-09-05 21:47:34 +000011529 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011530
cristy3ed852e2009-09-05 21:47:34 +000011531 if (jpeg_image == (Image *) NULL)
11532 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
glennrp0fe50b42010-11-16 03:52:51 +000011533
cristy3ed852e2009-09-05 21:47:34 +000011534 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
cristy4c08aed2011-07-01 19:47:50 +000011535 status=SeparateImageChannel(jpeg_image,AlphaChannel);
cristy3ed852e2009-09-05 21:47:34 +000011536 jpeg_image->matte=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000011537
cristy3ed852e2009-09-05 21:47:34 +000011538 if (jng_quality >= 1000)
11539 jpeg_image_info->quality=jng_quality/1000;
glennrp0fe50b42010-11-16 03:52:51 +000011540
cristy3ed852e2009-09-05 21:47:34 +000011541 else
11542 jpeg_image_info->quality=jng_quality;
glennrp0fe50b42010-11-16 03:52:51 +000011543
cristy3ed852e2009-09-05 21:47:34 +000011544 jpeg_image_info->type=GrayscaleType;
11545 (void) SetImageType(jpeg_image,GrayscaleType);
11546 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011547 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +000011548 "%s",jpeg_image->filename);
11549 }
11550
11551 /* To do: check bit depth of PNG alpha channel */
11552
11553 /* Check if image is grayscale. */
11554 if (image_info->type != TrueColorMatteType && image_info->type !=
11555 TrueColorType && ImageIsGray(image))
11556 jng_color_type-=2;
11557
11558 if (transparent)
11559 {
11560 if (jng_alpha_compression_method==0)
11561 {
11562 const char
11563 *value;
11564
cristy4c08aed2011-07-01 19:47:50 +000011565 /* Encode alpha as a grayscale PNG blob */
cristy3ed852e2009-09-05 21:47:34 +000011566 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11567 &image->exception);
11568 if (logging != MagickFalse)
11569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11570 " Creating PNG blob.");
11571 length=0;
11572
11573 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
11574 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
11575 jpeg_image_info->interlace=NoInterlace;
11576
11577 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
11578 &image->exception);
11579
11580 /* Retrieve sample depth used */
11581 value=GetImageProperty(jpeg_image,"png:bit-depth-written");
11582 if (value != (char *) NULL)
11583 jng_alpha_sample_depth= (unsigned int) value[0];
11584 }
11585 else
11586 {
cristy4c08aed2011-07-01 19:47:50 +000011587 /* Encode alpha as a grayscale JPEG blob */
cristy3ed852e2009-09-05 21:47:34 +000011588
11589 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11590 &image->exception);
11591
11592 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11593 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11594 jpeg_image_info->interlace=NoInterlace;
11595 if (logging != MagickFalse)
11596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11597 " Creating blob.");
11598 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
cristyf2faecf2010-05-28 19:19:36 +000011599 &image->exception);
cristy3ed852e2009-09-05 21:47:34 +000011600 jng_alpha_sample_depth=8;
glennrp0fe50b42010-11-16 03:52:51 +000011601
cristy3ed852e2009-09-05 21:47:34 +000011602 if (logging != MagickFalse)
11603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011604 " Successfully read jpeg_image into a blob, length=%.20g.",
11605 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011606
11607 }
11608 /* Destroy JPEG image and image_info */
11609 jpeg_image=DestroyImage(jpeg_image);
11610 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11611 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11612 }
11613
11614 /* Write JHDR chunk */
11615 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
11616 PNGType(chunk,mng_JHDR);
glennrp03812ae2010-12-24 01:31:34 +000011617 LogPNGChunk(logging,mng_JHDR,16L);
cristy4e5bc842010-06-09 13:56:01 +000011618 PNGLong(chunk+4,(png_uint_32) image->columns);
11619 PNGLong(chunk+8,(png_uint_32) image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011620 chunk[12]=jng_color_type;
11621 chunk[13]=8; /* sample depth */
11622 chunk[14]=8; /*jng_image_compression_method */
11623 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
11624 chunk[16]=jng_alpha_sample_depth;
11625 chunk[17]=jng_alpha_compression_method;
11626 chunk[18]=0; /*jng_alpha_filter_method */
11627 chunk[19]=0; /*jng_alpha_interlace_method */
11628 (void) WriteBlob(image,20,chunk);
11629 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11630 if (logging != MagickFalse)
11631 {
11632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011633 " JNG width:%15lu",(unsigned long) image->columns);
glennrp0fe50b42010-11-16 03:52:51 +000011634
cristy3ed852e2009-09-05 21:47:34 +000011635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristyf2faecf2010-05-28 19:19:36 +000011636 " JNG height:%14lu",(unsigned long) image->rows);
glennrp0fe50b42010-11-16 03:52:51 +000011637
cristy3ed852e2009-09-05 21:47:34 +000011638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11639 " JNG color type:%10d",jng_color_type);
glennrp0fe50b42010-11-16 03:52:51 +000011640
cristy3ed852e2009-09-05 21:47:34 +000011641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11642 " JNG sample depth:%8d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011643
cristy3ed852e2009-09-05 21:47:34 +000011644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11645 " JNG compression:%9d",8);
glennrp0fe50b42010-11-16 03:52:51 +000011646
cristy3ed852e2009-09-05 21:47:34 +000011647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11648 " JNG interlace:%11d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011649
cristy3ed852e2009-09-05 21:47:34 +000011650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11651 " JNG alpha depth:%9d",jng_alpha_sample_depth);
glennrp0fe50b42010-11-16 03:52:51 +000011652
cristy3ed852e2009-09-05 21:47:34 +000011653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11654 " JNG alpha compression:%3d",jng_alpha_compression_method);
glennrp0fe50b42010-11-16 03:52:51 +000011655
cristy3ed852e2009-09-05 21:47:34 +000011656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11657 " JNG alpha filter:%8d",0);
glennrp0fe50b42010-11-16 03:52:51 +000011658
cristy3ed852e2009-09-05 21:47:34 +000011659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11660 " JNG alpha interlace:%5d",0);
11661 }
11662
glennrp0fe50b42010-11-16 03:52:51 +000011663 /* Write any JNG-chunk-b profiles */
glennrpcf002022011-01-30 02:38:15 +000011664 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
cristy3ed852e2009-09-05 21:47:34 +000011665
11666 /*
11667 Write leading ancillary chunks
11668 */
11669
11670 if (transparent)
11671 {
11672 /*
11673 Write JNG bKGD chunk
11674 */
11675
11676 unsigned char
11677 blue,
11678 green,
11679 red;
11680
cristybb503372010-05-27 20:51:26 +000011681 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011682 num_bytes;
11683
11684 if (jng_color_type == 8 || jng_color_type == 12)
11685 num_bytes=6L;
11686 else
11687 num_bytes=10L;
cristybb503372010-05-27 20:51:26 +000011688 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011689 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000011690 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
cristy3ed852e2009-09-05 21:47:34 +000011691 red=ScaleQuantumToChar(image->background_color.red);
11692 green=ScaleQuantumToChar(image->background_color.green);
11693 blue=ScaleQuantumToChar(image->background_color.blue);
11694 *(chunk+4)=0;
11695 *(chunk+5)=red;
11696 *(chunk+6)=0;
11697 *(chunk+7)=green;
11698 *(chunk+8)=0;
11699 *(chunk+9)=blue;
11700 (void) WriteBlob(image,(size_t) num_bytes,chunk);
11701 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
11702 }
11703
11704 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
11705 {
11706 /*
11707 Write JNG sRGB chunk
11708 */
11709 (void) WriteBlobMSBULong(image,1L);
11710 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000011711 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000011712
cristy3ed852e2009-09-05 21:47:34 +000011713 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000011714 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011715 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011716 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000011717
cristy3ed852e2009-09-05 21:47:34 +000011718 else
glennrpe610a072010-08-05 17:08:46 +000011719 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000011720 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000011721 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000011722
cristy3ed852e2009-09-05 21:47:34 +000011723 (void) WriteBlob(image,5,chunk);
11724 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11725 }
11726 else
11727 {
11728 if (image->gamma != 0.0)
11729 {
11730 /*
11731 Write JNG gAMA chunk
11732 */
11733 (void) WriteBlobMSBULong(image,4L);
11734 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000011735 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000011736 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011737 (void) WriteBlob(image,8,chunk);
11738 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
11739 }
glennrp0fe50b42010-11-16 03:52:51 +000011740
cristy3ed852e2009-09-05 21:47:34 +000011741 if ((mng_info->equal_chrms == MagickFalse) &&
11742 (image->chromaticity.red_primary.x != 0.0))
11743 {
11744 PrimaryInfo
11745 primary;
11746
11747 /*
11748 Write JNG cHRM chunk
11749 */
11750 (void) WriteBlobMSBULong(image,32L);
11751 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000011752 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000011753 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000011754 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
11755 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011756 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000011757 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
11758 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011759 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000011760 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
11761 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011762 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000011763 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
11764 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011765 (void) WriteBlob(image,36,chunk);
11766 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
11767 }
11768 }
glennrp0fe50b42010-11-16 03:52:51 +000011769
cristy3ed852e2009-09-05 21:47:34 +000011770 if (image->x_resolution && image->y_resolution && !mng_info->equal_physs)
11771 {
11772 /*
11773 Write JNG pHYs chunk
11774 */
11775 (void) WriteBlobMSBULong(image,9L);
11776 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000011777 LogPNGChunk(logging,mng_pHYs,9L);
cristy3ed852e2009-09-05 21:47:34 +000011778 if (image->units == PixelsPerInchResolution)
11779 {
cristy35ef8242010-06-03 16:24:13 +000011780 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011781 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011782
cristy35ef8242010-06-03 16:24:13 +000011783 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011784 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011785
cristy3ed852e2009-09-05 21:47:34 +000011786 chunk[12]=1;
11787 }
glennrp0fe50b42010-11-16 03:52:51 +000011788
cristy3ed852e2009-09-05 21:47:34 +000011789 else
11790 {
11791 if (image->units == PixelsPerCentimeterResolution)
11792 {
cristy35ef8242010-06-03 16:24:13 +000011793 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011794 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011795
cristy35ef8242010-06-03 16:24:13 +000011796 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000011797 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000011798
cristy3ed852e2009-09-05 21:47:34 +000011799 chunk[12]=1;
11800 }
glennrp0fe50b42010-11-16 03:52:51 +000011801
cristy3ed852e2009-09-05 21:47:34 +000011802 else
11803 {
cristy35ef8242010-06-03 16:24:13 +000011804 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
11805 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000011806 chunk[12]=0;
11807 }
11808 }
11809 (void) WriteBlob(image,13,chunk);
11810 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11811 }
11812
11813 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
11814 {
11815 /*
11816 Write JNG oFFs chunk
11817 */
11818 (void) WriteBlobMSBULong(image,9L);
11819 PNGType(chunk,mng_oFFs);
glennrp03812ae2010-12-24 01:31:34 +000011820 LogPNGChunk(logging,mng_oFFs,9L);
cristybb503372010-05-27 20:51:26 +000011821 PNGsLong(chunk+4,(ssize_t) (image->page.x));
11822 PNGsLong(chunk+8,(ssize_t) (image->page.y));
cristy3ed852e2009-09-05 21:47:34 +000011823 chunk[12]=0;
11824 (void) WriteBlob(image,13,chunk);
11825 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11826 }
11827 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
11828 {
11829 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11830 PNGType(chunk,mng_vpAg);
glennrp03812ae2010-12-24 01:31:34 +000011831 LogPNGChunk(logging,mng_vpAg,9L);
cristy3ed852e2009-09-05 21:47:34 +000011832 PNGLong(chunk+4,(png_uint_32) image->page.width);
11833 PNGLong(chunk+8,(png_uint_32) image->page.height);
11834 chunk[12]=0; /* unit = pixels */
11835 (void) WriteBlob(image,13,chunk);
11836 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11837 }
11838
11839
11840 if (transparent)
11841 {
11842 if (jng_alpha_compression_method==0)
11843 {
cristybb503372010-05-27 20:51:26 +000011844 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011845 i;
11846
cristybb503372010-05-27 20:51:26 +000011847 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000011848 len;
11849
11850 /* Write IDAT chunk header */
11851 if (logging != MagickFalse)
11852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011853 " Write IDAT chunks from blob, length=%.20g.",(double)
cristyf2faecf2010-05-28 19:19:36 +000011854 length);
cristy3ed852e2009-09-05 21:47:34 +000011855
11856 /* Copy IDAT chunks */
11857 len=0;
11858 p=blob+8;
cristybb503372010-05-27 20:51:26 +000011859 for (i=8; i<(ssize_t) length; i+=len+12)
cristy3ed852e2009-09-05 21:47:34 +000011860 {
11861 len=(*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
11862 p+=4;
glennrp0fe50b42010-11-16 03:52:51 +000011863
cristy3ed852e2009-09-05 21:47:34 +000011864 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
11865 {
11866 /* Found an IDAT chunk. */
cristybb503372010-05-27 20:51:26 +000011867 (void) WriteBlobMSBULong(image,(size_t) len);
glennrp03812ae2010-12-24 01:31:34 +000011868 LogPNGChunk(logging,mng_IDAT,(size_t) len);
cristy3ed852e2009-09-05 21:47:34 +000011869 (void) WriteBlob(image,(size_t) len+4,p);
11870 (void) WriteBlobMSBULong(image,
11871 crc32(0,p,(uInt) len+4));
11872 }
glennrp0fe50b42010-11-16 03:52:51 +000011873
cristy3ed852e2009-09-05 21:47:34 +000011874 else
11875 {
11876 if (logging != MagickFalse)
11877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011878 " Skipping %c%c%c%c chunk, length=%.20g.",
11879 *(p),*(p+1),*(p+2),*(p+3),(double) len);
cristy3ed852e2009-09-05 21:47:34 +000011880 }
11881 p+=(8+len);
11882 }
11883 }
11884 else
11885 {
11886 /* Write JDAA chunk header */
11887 if (logging != MagickFalse)
11888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011889 " Write JDAA chunk, length=%.20g.",(double) length);
cristybb503372010-05-27 20:51:26 +000011890 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011891 PNGType(chunk,mng_JDAA);
glennrp03812ae2010-12-24 01:31:34 +000011892 LogPNGChunk(logging,mng_JDAA,length);
cristy3ed852e2009-09-05 21:47:34 +000011893 /* Write JDAT chunk(s) data */
11894 (void) WriteBlob(image,4,chunk);
11895 (void) WriteBlob(image,length,blob);
11896 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
11897 (uInt) length));
11898 }
11899 blob=(unsigned char *) RelinquishMagickMemory(blob);
11900 }
11901
11902 /* Encode image as a JPEG blob */
11903 if (logging != MagickFalse)
11904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11905 " Creating jpeg_image_info.");
11906 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
11907 if (jpeg_image_info == (ImageInfo *) NULL)
11908 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11909
11910 if (logging != MagickFalse)
11911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11912 " Creating jpeg_image.");
11913
11914 jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
11915 if (jpeg_image == (Image *) NULL)
11916 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11917 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
11918
11919 (void) AcquireUniqueFilename(jpeg_image->filename);
cristy3b6fd2e2011-05-20 12:53:50 +000011920 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
cristy3ed852e2009-09-05 21:47:34 +000011921 jpeg_image->filename);
11922
11923 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
11924 &image->exception);
11925
11926 if (logging != MagickFalse)
11927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011928 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
11929 (double) jpeg_image->rows);
cristy3ed852e2009-09-05 21:47:34 +000011930
11931 if (jng_color_type == 8 || jng_color_type == 12)
11932 jpeg_image_info->type=GrayscaleType;
glennrp0fe50b42010-11-16 03:52:51 +000011933
cristy3ed852e2009-09-05 21:47:34 +000011934 jpeg_image_info->quality=jng_quality % 1000;
11935 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
11936 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
glennrp0fe50b42010-11-16 03:52:51 +000011937
cristy3ed852e2009-09-05 21:47:34 +000011938 if (logging != MagickFalse)
11939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11940 " Creating blob.");
glennrp0fe50b42010-11-16 03:52:51 +000011941
cristy3ed852e2009-09-05 21:47:34 +000011942 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,&image->exception);
glennrp0fe50b42010-11-16 03:52:51 +000011943
cristy3ed852e2009-09-05 21:47:34 +000011944 if (logging != MagickFalse)
11945 {
11946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011947 " Successfully read jpeg_image into a blob, length=%.20g.",
11948 (double) length);
cristy3ed852e2009-09-05 21:47:34 +000011949
11950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000011951 " Write JDAT chunk, length=%.20g.",(double) length);
cristy3ed852e2009-09-05 21:47:34 +000011952 }
glennrp0fe50b42010-11-16 03:52:51 +000011953
cristy3ed852e2009-09-05 21:47:34 +000011954 /* Write JDAT chunk(s) */
cristybb503372010-05-27 20:51:26 +000011955 (void) WriteBlobMSBULong(image,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000011956 PNGType(chunk,mng_JDAT);
glennrp03812ae2010-12-24 01:31:34 +000011957 LogPNGChunk(logging,mng_JDAT,length);
cristy3ed852e2009-09-05 21:47:34 +000011958 (void) WriteBlob(image,4,chunk);
11959 (void) WriteBlob(image,length,blob);
11960 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
11961
11962 jpeg_image=DestroyImage(jpeg_image);
11963 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
11964 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
11965 blob=(unsigned char *) RelinquishMagickMemory(blob);
11966
11967 /* Write any JNG-chunk-e profiles */
glennrpcf002022011-01-30 02:38:15 +000011968 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
cristy3ed852e2009-09-05 21:47:34 +000011969
11970 /* Write IEND chunk */
11971 (void) WriteBlobMSBULong(image,0L);
11972 PNGType(chunk,mng_IEND);
glennrp03812ae2010-12-24 01:31:34 +000011973 LogPNGChunk(logging,mng_IEND,0);
cristy3ed852e2009-09-05 21:47:34 +000011974 (void) WriteBlob(image,4,chunk);
11975 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
11976
11977 if (logging != MagickFalse)
11978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11979 " exit WriteOneJNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000011980
cristy3ed852e2009-09-05 21:47:34 +000011981 return(status);
11982}
11983
11984
11985/*
11986%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11987% %
11988% %
11989% %
11990% W r i t e J N G I m a g e %
11991% %
11992% %
11993% %
11994%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11995%
11996% WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
11997%
11998% JNG support written by Glenn Randers-Pehrson, glennrp@image...
11999%
12000% The format of the WriteJNGImage method is:
12001%
12002% MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
12003%
12004% A description of each parameter follows:
12005%
12006% o image_info: the image info.
12007%
12008% o image: The image.
12009%
12010%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12011*/
12012static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image)
12013{
12014 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012015 have_mng_structure,
glennrp03812ae2010-12-24 01:31:34 +000012016 logging,
cristy3ed852e2009-09-05 21:47:34 +000012017 status;
12018
12019 MngInfo
12020 *mng_info;
12021
cristy3ed852e2009-09-05 21:47:34 +000012022 /*
12023 Open image file.
12024 */
12025 assert(image_info != (const ImageInfo *) NULL);
12026 assert(image_info->signature == MagickSignature);
12027 assert(image != (Image *) NULL);
12028 assert(image->signature == MagickSignature);
12029 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012030 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012031 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12032 if (status == MagickFalse)
12033 return(status);
12034
12035 /*
12036 Allocate a MngInfo structure.
12037 */
12038 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012039 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012040 if (mng_info == (MngInfo *) NULL)
12041 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12042 /*
12043 Initialize members of the MngInfo structure.
12044 */
12045 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12046 mng_info->image=image;
12047 have_mng_structure=MagickTrue;
12048
12049 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12050
12051 status=WriteOneJNGImage(mng_info,image_info,image);
12052 (void) CloseBlob(image);
12053
12054 (void) CatchImageException(image);
12055 MngInfoFreeStruct(mng_info,&have_mng_structure);
12056 if (logging != MagickFalse)
12057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12058 return(status);
12059}
12060#endif
12061
12062
12063
12064static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12065{
12066 const char
12067 *option;
12068
12069 Image
12070 *next_image;
12071
12072 MagickBooleanType
glennrp21f0e622011-01-07 16:20:57 +000012073 have_mng_structure,
cristy3ed852e2009-09-05 21:47:34 +000012074 status;
12075
glennrp03812ae2010-12-24 01:31:34 +000012076 volatile MagickBooleanType
12077 logging;
12078
cristy3ed852e2009-09-05 21:47:34 +000012079 MngInfo
12080 *mng_info;
12081
12082 int
cristy3ed852e2009-09-05 21:47:34 +000012083 image_count,
12084 need_iterations,
12085 need_matte;
12086
12087 volatile int
12088#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12089 defined(PNG_MNG_FEATURES_SUPPORTED)
12090 need_local_plte,
12091#endif
12092 all_images_are_gray,
cristy3ed852e2009-09-05 21:47:34 +000012093 need_defi,
cristy3ed852e2009-09-05 21:47:34 +000012094 use_global_plte;
12095
cristybb503372010-05-27 20:51:26 +000012096 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012097 i;
12098
12099 unsigned char
12100 chunk[800];
12101
12102 volatile unsigned int
12103 write_jng,
12104 write_mng;
12105
cristybb503372010-05-27 20:51:26 +000012106 volatile size_t
cristy3ed852e2009-09-05 21:47:34 +000012107 scene;
12108
cristybb503372010-05-27 20:51:26 +000012109 size_t
cristy3ed852e2009-09-05 21:47:34 +000012110 final_delay=0,
12111 initial_delay;
12112
glennrpd5045b42010-03-24 12:40:35 +000012113#if (PNG_LIBPNG_VER < 10200)
cristy3ed852e2009-09-05 21:47:34 +000012114 if (image_info->verbose)
12115 printf("Your PNG library (libpng-%s) is rather old.\n",
12116 PNG_LIBPNG_VER_STRING);
12117#endif
12118
12119 /*
12120 Open image file.
12121 */
12122 assert(image_info != (const ImageInfo *) NULL);
12123 assert(image_info->signature == MagickSignature);
12124 assert(image != (Image *) NULL);
12125 assert(image->signature == MagickSignature);
12126 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
glennrpfd05d622011-02-25 04:10:33 +000012127 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
cristy3ed852e2009-09-05 21:47:34 +000012128 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
12129 if (status == MagickFalse)
12130 return(status);
12131
12132 /*
12133 Allocate a MngInfo structure.
12134 */
12135 have_mng_structure=MagickFalse;
cristy73bd4a52010-10-05 11:24:23 +000012136 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
cristy3ed852e2009-09-05 21:47:34 +000012137 if (mng_info == (MngInfo *) NULL)
12138 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12139 /*
12140 Initialize members of the MngInfo structure.
12141 */
12142 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12143 mng_info->image=image;
12144 have_mng_structure=MagickTrue;
12145 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12146
12147 /*
12148 * See if user has requested a specific PNG subformat to be used
12149 * for all of the PNGs in the MNG being written, e.g.,
12150 *
12151 * convert *.png png8:animation.mng
12152 *
12153 * To do: check -define png:bit_depth and png:color_type as well,
12154 * or perhaps use mng:bit_depth and mng:color_type instead for
12155 * global settings.
12156 */
12157
12158 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12159 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12160 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12161
12162 write_jng=MagickFalse;
12163 if (image_info->compression == JPEGCompression)
12164 write_jng=MagickTrue;
12165
12166 mng_info->adjoin=image_info->adjoin &&
12167 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12168
cristy3ed852e2009-09-05 21:47:34 +000012169 if (logging != MagickFalse)
12170 {
12171 /* Log some info about the input */
12172 Image
12173 *p;
12174
12175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12176 " Checking input image(s)");
glennrp0fe50b42010-11-16 03:52:51 +000012177
cristy3ed852e2009-09-05 21:47:34 +000012178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012179 " Image_info depth: %.20g",(double) image_info->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012180
cristy3ed852e2009-09-05 21:47:34 +000012181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12182 " Type: %d",image_info->type);
12183
12184 scene=0;
12185 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12186 {
12187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012188 " Scene: %.20g",(double) scene++);
glennrp0fe50b42010-11-16 03:52:51 +000012189
cristy3ed852e2009-09-05 21:47:34 +000012190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012191 " Image depth: %.20g",(double) p->depth);
glennrp0fe50b42010-11-16 03:52:51 +000012192
cristy3ed852e2009-09-05 21:47:34 +000012193 if (p->matte)
12194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12195 " Matte: True");
glennrp0fe50b42010-11-16 03:52:51 +000012196
cristy3ed852e2009-09-05 21:47:34 +000012197 else
12198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12199 " Matte: False");
glennrp0fe50b42010-11-16 03:52:51 +000012200
cristy3ed852e2009-09-05 21:47:34 +000012201 if (p->storage_class == PseudoClass)
12202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12203 " Storage class: PseudoClass");
glennrp0fe50b42010-11-16 03:52:51 +000012204
cristy3ed852e2009-09-05 21:47:34 +000012205 else
12206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12207 " Storage class: DirectClass");
glennrp0fe50b42010-11-16 03:52:51 +000012208
cristy3ed852e2009-09-05 21:47:34 +000012209 if (p->colors)
12210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012211 " Number of colors: %.20g",(double) p->colors);
glennrp0fe50b42010-11-16 03:52:51 +000012212
cristy3ed852e2009-09-05 21:47:34 +000012213 else
12214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12215 " Number of colors: unspecified");
glennrp0fe50b42010-11-16 03:52:51 +000012216
cristy3ed852e2009-09-05 21:47:34 +000012217 if (mng_info->adjoin == MagickFalse)
12218 break;
12219 }
12220 }
12221
cristy3ed852e2009-09-05 21:47:34 +000012222 use_global_plte=MagickFalse;
12223 all_images_are_gray=MagickFalse;
12224#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12225 need_local_plte=MagickTrue;
12226#endif
12227 need_defi=MagickFalse;
12228 need_matte=MagickFalse;
12229 mng_info->framing_mode=1;
12230 mng_info->old_framing_mode=1;
12231
12232 if (write_mng)
12233 if (image_info->page != (char *) NULL)
12234 {
12235 /*
12236 Determine image bounding box.
12237 */
12238 SetGeometry(image,&mng_info->page);
12239 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12240 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12241 }
12242 if (write_mng)
12243 {
12244 unsigned int
12245 need_geom;
12246
12247 unsigned short
12248 red,
12249 green,
12250 blue;
12251
12252 mng_info->page=image->page;
12253 need_geom=MagickTrue;
12254 if (mng_info->page.width || mng_info->page.height)
12255 need_geom=MagickFalse;
12256 /*
12257 Check all the scenes.
12258 */
12259 initial_delay=image->delay;
12260 need_iterations=MagickFalse;
12261 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12262 mng_info->equal_physs=MagickTrue,
12263 mng_info->equal_gammas=MagickTrue;
12264 mng_info->equal_srgbs=MagickTrue;
12265 mng_info->equal_backgrounds=MagickTrue;
12266 image_count=0;
12267#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12268 defined(PNG_MNG_FEATURES_SUPPORTED)
12269 all_images_are_gray=MagickTrue;
12270 mng_info->equal_palettes=MagickFalse;
12271 need_local_plte=MagickFalse;
12272#endif
12273 for (next_image=image; next_image != (Image *) NULL; )
12274 {
12275 if (need_geom)
12276 {
12277 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12278 mng_info->page.width=next_image->columns+next_image->page.x;
glennrp0fe50b42010-11-16 03:52:51 +000012279
cristy3ed852e2009-09-05 21:47:34 +000012280 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12281 mng_info->page.height=next_image->rows+next_image->page.y;
12282 }
glennrp0fe50b42010-11-16 03:52:51 +000012283
cristy3ed852e2009-09-05 21:47:34 +000012284 if (next_image->page.x || next_image->page.y)
12285 need_defi=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012286
cristy3ed852e2009-09-05 21:47:34 +000012287 if (next_image->matte)
12288 need_matte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012289
cristy3ed852e2009-09-05 21:47:34 +000012290 if ((int) next_image->dispose >= BackgroundDispose)
12291 if (next_image->matte || next_image->page.x || next_image->page.y ||
12292 ((next_image->columns < mng_info->page.width) &&
12293 (next_image->rows < mng_info->page.height)))
12294 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012295
cristy3ed852e2009-09-05 21:47:34 +000012296 if (next_image->iterations)
12297 need_iterations=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012298
cristy3ed852e2009-09-05 21:47:34 +000012299 final_delay=next_image->delay;
glennrp0fe50b42010-11-16 03:52:51 +000012300
cristy3ed852e2009-09-05 21:47:34 +000012301 if (final_delay != initial_delay || final_delay > 1UL*
12302 next_image->ticks_per_second)
12303 mng_info->need_fram=1;
glennrp0fe50b42010-11-16 03:52:51 +000012304
cristy3ed852e2009-09-05 21:47:34 +000012305#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12306 defined(PNG_MNG_FEATURES_SUPPORTED)
12307 /*
12308 check for global palette possibility.
12309 */
12310 if (image->matte != MagickFalse)
12311 need_local_plte=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012312
cristy3ed852e2009-09-05 21:47:34 +000012313 if (need_local_plte == 0)
12314 {
12315 if (ImageIsGray(image) == MagickFalse)
12316 all_images_are_gray=MagickFalse;
12317 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
12318 if (use_global_plte == 0)
12319 use_global_plte=mng_info->equal_palettes;
12320 need_local_plte=!mng_info->equal_palettes;
12321 }
12322#endif
12323 if (GetNextImageInList(next_image) != (Image *) NULL)
12324 {
12325 if (next_image->background_color.red !=
12326 next_image->next->background_color.red ||
12327 next_image->background_color.green !=
12328 next_image->next->background_color.green ||
12329 next_image->background_color.blue !=
12330 next_image->next->background_color.blue)
12331 mng_info->equal_backgrounds=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012332
cristy3ed852e2009-09-05 21:47:34 +000012333 if (next_image->gamma != next_image->next->gamma)
12334 mng_info->equal_gammas=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012335
cristy3ed852e2009-09-05 21:47:34 +000012336 if (next_image->rendering_intent !=
12337 next_image->next->rendering_intent)
12338 mng_info->equal_srgbs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012339
cristy3ed852e2009-09-05 21:47:34 +000012340 if ((next_image->units != next_image->next->units) ||
12341 (next_image->x_resolution != next_image->next->x_resolution) ||
12342 (next_image->y_resolution != next_image->next->y_resolution))
12343 mng_info->equal_physs=MagickFalse;
glennrp0fe50b42010-11-16 03:52:51 +000012344
cristy3ed852e2009-09-05 21:47:34 +000012345 if (mng_info->equal_chrms)
12346 {
12347 if (next_image->chromaticity.red_primary.x !=
12348 next_image->next->chromaticity.red_primary.x ||
12349 next_image->chromaticity.red_primary.y !=
12350 next_image->next->chromaticity.red_primary.y ||
12351 next_image->chromaticity.green_primary.x !=
12352 next_image->next->chromaticity.green_primary.x ||
12353 next_image->chromaticity.green_primary.y !=
12354 next_image->next->chromaticity.green_primary.y ||
12355 next_image->chromaticity.blue_primary.x !=
12356 next_image->next->chromaticity.blue_primary.x ||
12357 next_image->chromaticity.blue_primary.y !=
12358 next_image->next->chromaticity.blue_primary.y ||
12359 next_image->chromaticity.white_point.x !=
12360 next_image->next->chromaticity.white_point.x ||
12361 next_image->chromaticity.white_point.y !=
12362 next_image->next->chromaticity.white_point.y)
12363 mng_info->equal_chrms=MagickFalse;
12364 }
12365 }
12366 image_count++;
12367 next_image=GetNextImageInList(next_image);
12368 }
12369 if (image_count < 2)
12370 {
12371 mng_info->equal_backgrounds=MagickFalse;
12372 mng_info->equal_chrms=MagickFalse;
12373 mng_info->equal_gammas=MagickFalse;
12374 mng_info->equal_srgbs=MagickFalse;
12375 mng_info->equal_physs=MagickFalse;
12376 use_global_plte=MagickFalse;
12377#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12378 need_local_plte=MagickTrue;
12379#endif
12380 need_iterations=MagickFalse;
12381 }
glennrp0fe50b42010-11-16 03:52:51 +000012382
cristy3ed852e2009-09-05 21:47:34 +000012383 if (mng_info->need_fram == MagickFalse)
12384 {
12385 /*
12386 Only certain framing rates 100/n are exactly representable without
12387 the FRAM chunk but we'll allow some slop in VLC files
12388 */
12389 if (final_delay == 0)
12390 {
12391 if (need_iterations != MagickFalse)
12392 {
12393 /*
12394 It's probably a GIF with loop; don't run it *too* fast.
12395 */
glennrp02617122010-07-28 13:07:35 +000012396 if (mng_info->adjoin)
glennrpd908de42010-07-28 13:28:27 +000012397 {
12398 final_delay=10;
12399 (void) ThrowMagickException(&image->exception,
12400 GetMagickModule(),CoderWarning,
12401 "input has zero delay between all frames; assuming",
12402 " 10 cs `%s'","");
12403 }
cristy3ed852e2009-09-05 21:47:34 +000012404 }
12405 else
12406 mng_info->ticks_per_second=0;
12407 }
12408 if (final_delay != 0)
glennrpcf002022011-01-30 02:38:15 +000012409 mng_info->ticks_per_second=(png_uint_32)
12410 (image->ticks_per_second/final_delay);
cristy3ed852e2009-09-05 21:47:34 +000012411 if (final_delay > 50)
12412 mng_info->ticks_per_second=2;
glennrp0fe50b42010-11-16 03:52:51 +000012413
cristy3ed852e2009-09-05 21:47:34 +000012414 if (final_delay > 75)
12415 mng_info->ticks_per_second=1;
glennrp0fe50b42010-11-16 03:52:51 +000012416
cristy3ed852e2009-09-05 21:47:34 +000012417 if (final_delay > 125)
12418 mng_info->need_fram=MagickTrue;
glennrp0fe50b42010-11-16 03:52:51 +000012419
cristy3ed852e2009-09-05 21:47:34 +000012420 if (need_defi && final_delay > 2 && (final_delay != 4) &&
12421 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
12422 (final_delay != 25) && (final_delay != 50) && (final_delay !=
12423 1UL*image->ticks_per_second))
12424 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
12425 }
glennrp0fe50b42010-11-16 03:52:51 +000012426
cristy3ed852e2009-09-05 21:47:34 +000012427 if (mng_info->need_fram != MagickFalse)
12428 mng_info->ticks_per_second=1UL*image->ticks_per_second;
12429 /*
12430 If pseudocolor, we should also check to see if all the
12431 palettes are identical and write a global PLTE if they are.
12432 ../glennrp Feb 99.
12433 */
12434 /*
12435 Write the MNG version 1.0 signature and MHDR chunk.
12436 */
12437 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
12438 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
12439 PNGType(chunk,mng_MHDR);
glennrp03812ae2010-12-24 01:31:34 +000012440 LogPNGChunk(logging,mng_MHDR,28L);
cristy4e5bc842010-06-09 13:56:01 +000012441 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
12442 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
cristy3ed852e2009-09-05 21:47:34 +000012443 PNGLong(chunk+12,mng_info->ticks_per_second);
12444 PNGLong(chunk+16,0L); /* layer count=unknown */
12445 PNGLong(chunk+20,0L); /* frame count=unknown */
12446 PNGLong(chunk+24,0L); /* play time=unknown */
12447 if (write_jng)
12448 {
12449 if (need_matte)
12450 {
12451 if (need_defi || mng_info->need_fram || use_global_plte)
12452 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
glennrp0fe50b42010-11-16 03:52:51 +000012453
cristy3ed852e2009-09-05 21:47:34 +000012454 else
12455 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
12456 }
glennrp0fe50b42010-11-16 03:52:51 +000012457
cristy3ed852e2009-09-05 21:47:34 +000012458 else
12459 {
12460 if (need_defi || mng_info->need_fram || use_global_plte)
12461 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012462
cristy3ed852e2009-09-05 21:47:34 +000012463 else
12464 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
12465 }
12466 }
glennrp0fe50b42010-11-16 03:52:51 +000012467
cristy3ed852e2009-09-05 21:47:34 +000012468 else
12469 {
12470 if (need_matte)
12471 {
12472 if (need_defi || mng_info->need_fram || use_global_plte)
12473 PNGLong(chunk+28,11L); /* simplicity=LC */
glennrp0fe50b42010-11-16 03:52:51 +000012474
cristy3ed852e2009-09-05 21:47:34 +000012475 else
12476 PNGLong(chunk+28,9L); /* simplicity=VLC */
12477 }
glennrp0fe50b42010-11-16 03:52:51 +000012478
cristy3ed852e2009-09-05 21:47:34 +000012479 else
12480 {
12481 if (need_defi || mng_info->need_fram || use_global_plte)
12482 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
glennrp0fe50b42010-11-16 03:52:51 +000012483
cristy3ed852e2009-09-05 21:47:34 +000012484 else
12485 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
12486 }
12487 }
12488 (void) WriteBlob(image,32,chunk);
12489 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
12490 option=GetImageOption(image_info,"mng:need-cacheoff");
12491 if (option != (const char *) NULL)
12492 {
12493 size_t
12494 length;
12495
12496 /*
12497 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
12498 */
12499 PNGType(chunk,mng_nEED);
12500 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
cristybb503372010-05-27 20:51:26 +000012501 (void) WriteBlobMSBULong(image,(size_t) length);
glennrp03812ae2010-12-24 01:31:34 +000012502 LogPNGChunk(logging,mng_nEED,(size_t) length);
cristy3ed852e2009-09-05 21:47:34 +000012503 length+=4;
12504 (void) WriteBlob(image,length,chunk);
12505 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
12506 }
12507 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
12508 (GetNextImageInList(image) != (Image *) NULL) &&
12509 (image->iterations != 1))
12510 {
12511 /*
12512 Write MNG TERM chunk
12513 */
12514 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12515 PNGType(chunk,mng_TERM);
glennrp03812ae2010-12-24 01:31:34 +000012516 LogPNGChunk(logging,mng_TERM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012517 chunk[4]=3; /* repeat animation */
12518 chunk[5]=0; /* show last frame when done */
12519 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
12520 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012521
cristy3ed852e2009-09-05 21:47:34 +000012522 if (image->iterations == 0)
12523 PNGLong(chunk+10,PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012524
cristy3ed852e2009-09-05 21:47:34 +000012525 else
12526 PNGLong(chunk+10,(png_uint_32) image->iterations);
glennrp0fe50b42010-11-16 03:52:51 +000012527
cristy3ed852e2009-09-05 21:47:34 +000012528 if (logging != MagickFalse)
12529 {
12530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012531 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
12532 final_delay/MagickMax(image->ticks_per_second,1)));
glennrp0fe50b42010-11-16 03:52:51 +000012533
cristy3ed852e2009-09-05 21:47:34 +000012534 if (image->iterations == 0)
12535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012536 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
glennrp0fe50b42010-11-16 03:52:51 +000012537
cristy3ed852e2009-09-05 21:47:34 +000012538 else
12539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +000012540 " Image iterations: %.20g",(double) image->iterations);
cristy3ed852e2009-09-05 21:47:34 +000012541 }
12542 (void) WriteBlob(image,14,chunk);
12543 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
12544 }
12545 /*
12546 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
12547 */
12548 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
12549 mng_info->equal_srgbs)
12550 {
12551 /*
12552 Write MNG sRGB chunk
12553 */
12554 (void) WriteBlobMSBULong(image,1L);
12555 PNGType(chunk,mng_sRGB);
glennrp03812ae2010-12-24 01:31:34 +000012556 LogPNGChunk(logging,mng_sRGB,1L);
glennrp0fe50b42010-11-16 03:52:51 +000012557
cristy3ed852e2009-09-05 21:47:34 +000012558 if (image->rendering_intent != UndefinedIntent)
glennrpe610a072010-08-05 17:08:46 +000012559 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012560 Magick_RenderingIntent_to_PNG_RenderingIntent(
glennrpe610a072010-08-05 17:08:46 +000012561 (image->rendering_intent));
glennrp0fe50b42010-11-16 03:52:51 +000012562
cristy3ed852e2009-09-05 21:47:34 +000012563 else
glennrpe610a072010-08-05 17:08:46 +000012564 chunk[4]=(unsigned char)
glennrpcf002022011-01-30 02:38:15 +000012565 Magick_RenderingIntent_to_PNG_RenderingIntent(
12566 (PerceptualIntent));
glennrp0fe50b42010-11-16 03:52:51 +000012567
cristy3ed852e2009-09-05 21:47:34 +000012568 (void) WriteBlob(image,5,chunk);
12569 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12570 mng_info->have_write_global_srgb=MagickTrue;
12571 }
glennrp0fe50b42010-11-16 03:52:51 +000012572
cristy3ed852e2009-09-05 21:47:34 +000012573 else
12574 {
12575 if (image->gamma && mng_info->equal_gammas)
12576 {
12577 /*
12578 Write MNG gAMA chunk
12579 */
12580 (void) WriteBlobMSBULong(image,4L);
12581 PNGType(chunk,mng_gAMA);
glennrp03812ae2010-12-24 01:31:34 +000012582 LogPNGChunk(logging,mng_gAMA,4L);
cristy35ef8242010-06-03 16:24:13 +000012583 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012584 (void) WriteBlob(image,8,chunk);
12585 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12586 mng_info->have_write_global_gama=MagickTrue;
12587 }
12588 if (mng_info->equal_chrms)
12589 {
12590 PrimaryInfo
12591 primary;
12592
12593 /*
12594 Write MNG cHRM chunk
12595 */
12596 (void) WriteBlobMSBULong(image,32L);
12597 PNGType(chunk,mng_cHRM);
glennrp03812ae2010-12-24 01:31:34 +000012598 LogPNGChunk(logging,mng_cHRM,32L);
cristy3ed852e2009-09-05 21:47:34 +000012599 primary=image->chromaticity.white_point;
cristy35ef8242010-06-03 16:24:13 +000012600 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12601 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012602 primary=image->chromaticity.red_primary;
cristy35ef8242010-06-03 16:24:13 +000012603 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12604 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012605 primary=image->chromaticity.green_primary;
cristy35ef8242010-06-03 16:24:13 +000012606 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12607 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012608 primary=image->chromaticity.blue_primary;
cristy35ef8242010-06-03 16:24:13 +000012609 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12610 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012611 (void) WriteBlob(image,36,chunk);
12612 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12613 mng_info->have_write_global_chrm=MagickTrue;
12614 }
12615 }
12616 if (image->x_resolution && image->y_resolution && mng_info->equal_physs)
12617 {
12618 /*
12619 Write MNG pHYs chunk
12620 */
12621 (void) WriteBlobMSBULong(image,9L);
12622 PNGType(chunk,mng_pHYs);
glennrp03812ae2010-12-24 01:31:34 +000012623 LogPNGChunk(logging,mng_pHYs,9L);
glennrp0fe50b42010-11-16 03:52:51 +000012624
cristy3ed852e2009-09-05 21:47:34 +000012625 if (image->units == PixelsPerInchResolution)
12626 {
cristy35ef8242010-06-03 16:24:13 +000012627 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012628 (image->x_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012629
cristy35ef8242010-06-03 16:24:13 +000012630 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012631 (image->y_resolution*100.0/2.54+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012632
cristy3ed852e2009-09-05 21:47:34 +000012633 chunk[12]=1;
12634 }
glennrp0fe50b42010-11-16 03:52:51 +000012635
cristy3ed852e2009-09-05 21:47:34 +000012636 else
12637 {
12638 if (image->units == PixelsPerCentimeterResolution)
12639 {
cristy35ef8242010-06-03 16:24:13 +000012640 PNGLong(chunk+4,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012641 (image->x_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012642
cristy35ef8242010-06-03 16:24:13 +000012643 PNGLong(chunk+8,(png_uint_32)
cristy3ed852e2009-09-05 21:47:34 +000012644 (image->y_resolution*100.0+0.5));
glennrp0fe50b42010-11-16 03:52:51 +000012645
cristy3ed852e2009-09-05 21:47:34 +000012646 chunk[12]=1;
12647 }
glennrp0fe50b42010-11-16 03:52:51 +000012648
cristy3ed852e2009-09-05 21:47:34 +000012649 else
12650 {
cristy35ef8242010-06-03 16:24:13 +000012651 PNGLong(chunk+4,(png_uint_32) (image->x_resolution+0.5));
12652 PNGLong(chunk+8,(png_uint_32) (image->y_resolution+0.5));
cristy3ed852e2009-09-05 21:47:34 +000012653 chunk[12]=0;
12654 }
12655 }
12656 (void) WriteBlob(image,13,chunk);
12657 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12658 }
12659 /*
12660 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
12661 or does not cover the entire frame.
12662 */
12663 if (write_mng && (image->matte || image->page.x > 0 ||
12664 image->page.y > 0 || (image->page.width &&
12665 (image->page.width+image->page.x < mng_info->page.width))
12666 || (image->page.height && (image->page.height+image->page.y
12667 < mng_info->page.height))))
12668 {
12669 (void) WriteBlobMSBULong(image,6L);
12670 PNGType(chunk,mng_BACK);
glennrp03812ae2010-12-24 01:31:34 +000012671 LogPNGChunk(logging,mng_BACK,6L);
cristy3ed852e2009-09-05 21:47:34 +000012672 red=ScaleQuantumToShort(image->background_color.red);
12673 green=ScaleQuantumToShort(image->background_color.green);
12674 blue=ScaleQuantumToShort(image->background_color.blue);
12675 PNGShort(chunk+4,red);
12676 PNGShort(chunk+6,green);
12677 PNGShort(chunk+8,blue);
12678 (void) WriteBlob(image,10,chunk);
12679 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12680 if (mng_info->equal_backgrounds)
12681 {
12682 (void) WriteBlobMSBULong(image,6L);
12683 PNGType(chunk,mng_bKGD);
glennrp03812ae2010-12-24 01:31:34 +000012684 LogPNGChunk(logging,mng_bKGD,6L);
cristy3ed852e2009-09-05 21:47:34 +000012685 (void) WriteBlob(image,10,chunk);
12686 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
12687 }
12688 }
12689
12690#ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12691 if ((need_local_plte == MagickFalse) &&
12692 (image->storage_class == PseudoClass) &&
12693 (all_images_are_gray == MagickFalse))
12694 {
cristybb503372010-05-27 20:51:26 +000012695 size_t
cristy3ed852e2009-09-05 21:47:34 +000012696 data_length;
12697
12698 /*
12699 Write MNG PLTE chunk
12700 */
12701 data_length=3*image->colors;
12702 (void) WriteBlobMSBULong(image,data_length);
12703 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012704 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012705
cristybb503372010-05-27 20:51:26 +000012706 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012707 {
12708 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red) & 0xff;
12709 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green) & 0xff;
12710 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue) & 0xff;
12711 }
glennrp0fe50b42010-11-16 03:52:51 +000012712
cristy3ed852e2009-09-05 21:47:34 +000012713 (void) WriteBlob(image,data_length+4,chunk);
12714 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
12715 mng_info->have_write_global_plte=MagickTrue;
12716 }
12717#endif
12718 }
12719 scene=0;
12720 mng_info->delay=0;
12721#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12722 defined(PNG_MNG_FEATURES_SUPPORTED)
12723 mng_info->equal_palettes=MagickFalse;
12724#endif
12725 do
12726 {
12727 if (mng_info->adjoin)
12728 {
12729#if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12730 defined(PNG_MNG_FEATURES_SUPPORTED)
12731 /*
12732 If we aren't using a global palette for the entire MNG, check to
12733 see if we can use one for two or more consecutive images.
12734 */
12735 if (need_local_plte && use_global_plte && !all_images_are_gray)
12736 {
12737 if (mng_info->IsPalette)
12738 {
12739 /*
12740 When equal_palettes is true, this image has the same palette
12741 as the previous PseudoClass image
12742 */
12743 mng_info->have_write_global_plte=mng_info->equal_palettes;
12744 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
12745 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
12746 {
12747 /*
12748 Write MNG PLTE chunk
12749 */
cristybb503372010-05-27 20:51:26 +000012750 size_t
cristy3ed852e2009-09-05 21:47:34 +000012751 data_length;
12752
12753 data_length=3*image->colors;
12754 (void) WriteBlobMSBULong(image,data_length);
12755 PNGType(chunk,mng_PLTE);
glennrp03812ae2010-12-24 01:31:34 +000012756 LogPNGChunk(logging,mng_PLTE,data_length);
glennrp0fe50b42010-11-16 03:52:51 +000012757
cristybb503372010-05-27 20:51:26 +000012758 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +000012759 {
12760 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
12761 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
12762 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
12763 }
glennrp0fe50b42010-11-16 03:52:51 +000012764
cristy3ed852e2009-09-05 21:47:34 +000012765 (void) WriteBlob(image,data_length+4,chunk);
12766 (void) WriteBlobMSBULong(image,crc32(0,chunk,
12767 (uInt) (data_length+4)));
12768 mng_info->have_write_global_plte=MagickTrue;
12769 }
12770 }
12771 else
12772 mng_info->have_write_global_plte=MagickFalse;
12773 }
12774#endif
12775 if (need_defi)
12776 {
cristybb503372010-05-27 20:51:26 +000012777 ssize_t
cristy3ed852e2009-09-05 21:47:34 +000012778 previous_x,
12779 previous_y;
12780
12781 if (scene)
12782 {
12783 previous_x=mng_info->page.x;
12784 previous_y=mng_info->page.y;
12785 }
12786 else
12787 {
12788 previous_x=0;
12789 previous_y=0;
12790 }
12791 mng_info->page=image->page;
12792 if ((mng_info->page.x != previous_x) ||
12793 (mng_info->page.y != previous_y))
12794 {
12795 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
12796 PNGType(chunk,mng_DEFI);
glennrp03812ae2010-12-24 01:31:34 +000012797 LogPNGChunk(logging,mng_DEFI,12L);
cristy3ed852e2009-09-05 21:47:34 +000012798 chunk[4]=0; /* object 0 MSB */
12799 chunk[5]=0; /* object 0 LSB */
12800 chunk[6]=0; /* visible */
12801 chunk[7]=0; /* abstract */
12802 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
12803 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
12804 (void) WriteBlob(image,16,chunk);
12805 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
12806 }
12807 }
12808 }
12809
12810 mng_info->write_mng=write_mng;
12811
12812 if ((int) image->dispose >= 3)
12813 mng_info->framing_mode=3;
12814
12815 if (mng_info->need_fram && mng_info->adjoin &&
12816 ((image->delay != mng_info->delay) ||
12817 (mng_info->framing_mode != mng_info->old_framing_mode)))
12818 {
12819 if (image->delay == mng_info->delay)
12820 {
12821 /*
12822 Write a MNG FRAM chunk with the new framing mode.
12823 */
12824 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
12825 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012826 LogPNGChunk(logging,mng_FRAM,1L);
cristy3ed852e2009-09-05 21:47:34 +000012827 chunk[4]=(unsigned char) mng_info->framing_mode;
12828 (void) WriteBlob(image,5,chunk);
12829 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12830 }
12831 else
12832 {
12833 /*
12834 Write a MNG FRAM chunk with the delay.
12835 */
12836 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
12837 PNGType(chunk,mng_FRAM);
glennrp03812ae2010-12-24 01:31:34 +000012838 LogPNGChunk(logging,mng_FRAM,10L);
cristy3ed852e2009-09-05 21:47:34 +000012839 chunk[4]=(unsigned char) mng_info->framing_mode;
12840 chunk[5]=0; /* frame name separator (no name) */
12841 chunk[6]=2; /* flag for changing default delay */
12842 chunk[7]=0; /* flag for changing frame timeout */
12843 chunk[8]=0; /* flag for changing frame clipping */
12844 chunk[9]=0; /* flag for changing frame sync_id */
12845 PNGLong(chunk+10,(png_uint_32)
12846 ((mng_info->ticks_per_second*
12847 image->delay)/MagickMax(image->ticks_per_second,1)));
12848 (void) WriteBlob(image,14,chunk);
12849 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
cristy4e5bc842010-06-09 13:56:01 +000012850 mng_info->delay=(png_uint_32) image->delay;
cristy3ed852e2009-09-05 21:47:34 +000012851 }
12852 mng_info->old_framing_mode=mng_info->framing_mode;
12853 }
12854
12855#if defined(JNG_SUPPORTED)
12856 if (image_info->compression == JPEGCompression)
12857 {
12858 ImageInfo
12859 *write_info;
12860
12861 if (logging != MagickFalse)
12862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12863 " Writing JNG object.");
12864 /* To do: specify the desired alpha compression method. */
12865 write_info=CloneImageInfo(image_info);
12866 write_info->compression=UndefinedCompression;
12867 status=WriteOneJNGImage(mng_info,write_info,image);
12868 write_info=DestroyImageInfo(write_info);
12869 }
12870 else
12871#endif
12872 {
12873 if (logging != MagickFalse)
12874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12875 " Writing PNG object.");
glennrp2f2e5142010-12-23 19:13:35 +000012876
glennrpb9cfe272010-12-21 15:08:06 +000012877 mng_info->need_blob = MagickFalse;
glennrp8d3d6e52011-04-19 04:39:51 +000012878 mng_info->ping_preserve_colormap = MagickFalse;
glennrp2f2e5142010-12-23 19:13:35 +000012879
12880 /* We don't want any ancillary chunks written */
12881 mng_info->ping_exclude_bKGD=MagickTrue;
12882 mng_info->ping_exclude_cHRM=MagickTrue;
glennrpa0ed0092011-04-18 16:36:29 +000012883 mng_info->ping_exclude_date=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012884 mng_info->ping_exclude_EXIF=MagickTrue;
12885 mng_info->ping_exclude_gAMA=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012886 mng_info->ping_exclude_iCCP=MagickTrue;
12887 /* mng_info->ping_exclude_iTXt=MagickTrue; */
12888 mng_info->ping_exclude_oFFs=MagickTrue;
12889 mng_info->ping_exclude_pHYs=MagickTrue;
12890 mng_info->ping_exclude_sRGB=MagickTrue;
12891 mng_info->ping_exclude_tEXt=MagickTrue;
glennrpa1e3b7b2010-12-24 16:37:33 +000012892 mng_info->ping_exclude_tRNS=MagickTrue;
glennrp2f2e5142010-12-23 19:13:35 +000012893 mng_info->ping_exclude_vpAg=MagickTrue;
12894 mng_info->ping_exclude_zCCP=MagickTrue;
12895 mng_info->ping_exclude_zTXt=MagickTrue;
12896
cristy3ed852e2009-09-05 21:47:34 +000012897 status=WriteOnePNGImage(mng_info,image_info,image);
12898 }
12899
12900 if (status == MagickFalse)
12901 {
12902 MngInfoFreeStruct(mng_info,&have_mng_structure);
12903 (void) CloseBlob(image);
12904 return(MagickFalse);
12905 }
12906 (void) CatchImageException(image);
12907 if (GetNextImageInList(image) == (Image *) NULL)
12908 break;
12909 image=SyncNextImageInList(image);
12910 status=SetImageProgress(image,SaveImagesTag,scene++,
12911 GetImageListLength(image));
glennrp0fe50b42010-11-16 03:52:51 +000012912
cristy3ed852e2009-09-05 21:47:34 +000012913 if (status == MagickFalse)
12914 break;
glennrp0fe50b42010-11-16 03:52:51 +000012915
cristy3ed852e2009-09-05 21:47:34 +000012916 } while (mng_info->adjoin);
glennrp0fe50b42010-11-16 03:52:51 +000012917
cristy3ed852e2009-09-05 21:47:34 +000012918 if (write_mng)
12919 {
12920 while (GetPreviousImageInList(image) != (Image *) NULL)
12921 image=GetPreviousImageInList(image);
12922 /*
12923 Write the MEND chunk.
12924 */
12925 (void) WriteBlobMSBULong(image,0x00000000L);
12926 PNGType(chunk,mng_MEND);
glennrp03812ae2010-12-24 01:31:34 +000012927 LogPNGChunk(logging,mng_MEND,0L);
cristy3ed852e2009-09-05 21:47:34 +000012928 (void) WriteBlob(image,4,chunk);
12929 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12930 }
12931 /*
12932 Relinquish resources.
12933 */
12934 (void) CloseBlob(image);
12935 MngInfoFreeStruct(mng_info,&have_mng_structure);
glennrp0fe50b42010-11-16 03:52:51 +000012936
cristy3ed852e2009-09-05 21:47:34 +000012937 if (logging != MagickFalse)
12938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
glennrp0fe50b42010-11-16 03:52:51 +000012939
cristy3ed852e2009-09-05 21:47:34 +000012940 return(MagickTrue);
12941}
glennrpd5045b42010-03-24 12:40:35 +000012942#else /* PNG_LIBPNG_VER > 10011 */
glennrp39992b42010-11-14 00:03:43 +000012943
cristy3ed852e2009-09-05 21:47:34 +000012944static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
12945{
12946 image=image;
12947 printf("Your PNG library is too old: You have libpng-%s\n",
12948 PNG_LIBPNG_VER_STRING);
glennrp0fe50b42010-11-16 03:52:51 +000012949
cristy3ed852e2009-09-05 21:47:34 +000012950 ThrowBinaryException(CoderError,"PNG library is too old",
12951 image_info->filename);
12952}
glennrp39992b42010-11-14 00:03:43 +000012953
cristy3ed852e2009-09-05 21:47:34 +000012954static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
12955{
12956 return(WritePNGImage(image_info,image));
12957}
glennrpd5045b42010-03-24 12:40:35 +000012958#endif /* PNG_LIBPNG_VER > 10011 */
cristy3ed852e2009-09-05 21:47:34 +000012959#endif